Page 1 of 3

Custom StringGadget with Image button [CrossPlatform]

Posted: Sat Oct 22, 2016 4:36 pm
by Peyman
Another gadget with canvas, now string gadget.
I really love to have a string gadget with some image button in it like stringgadget in windows 10 and web browsers. so i try and create it with canvas gadget.

I must say my thx to STARGÅTE for http://www.purebasic.fr/german/viewtopi ... 85#p297285 that helps me so much.

please test it and report any bug or any future u want to be added.

Image

see example :
http://purebasic.fr/english/viewtopic.p ... 80#p496080

Enjoy,
Peyman.

Code: Select all

; ====================================
; Name:             StringGadgetEx
; Version:          1.35
; Author:           Peyman
; Created:          22th Oct 2016
; Update:           04th Nov 2016
; OS:               All
; Compiler Option : ThreadSafe
; PB ver.:          5.20+
; License:          Free
;
; Thanks :          
;                   STARGÅTE - For a nice code on creating a StringGadget With CanvasGadget
;                   kenmo - for show DrawText_ api have better result in windows
;                   And all of you that use it and send good comments
;
; ====================================
;{ Release info...
; 1.00 First release version
;
; 1.10 Changed #StringGadgetEx_FontID to #StringGadgetEx_Font
;      Fixed a problem in event system
;      Fixed some problem in drawing Text with GapY param
;      Fixed problem with hilighiting text when gadget dont have focus
;      Fixed a bug in FreeStringGadgetEx that cause crash
;      Added Font in example
;      Added double left click for selecting texts
;
; 1.20 Added CTRL + X, CTRL + C, CTRL + V for cut, copy and paste texts
;      Added Triple Left Click for selecting all text of StringGadgetEx
;
; 1.21 Fixed problem in CTRL + V for numeric, lowercase and uppercase gadgets
;
; 1.22 Changed CTRL + * to Command + * in mac os
;
; 1.24 Changed cursor position after right and left key pressed when text selected
;      Fixed a bug in mac for command + *
;      Added ResizeStringGadgetEx
;
; 1.30 Changed all StringGadgetEx_ procedures to SGEx_*
;      Changed all StringGadgetEx_ constants to SGEx_*
;      Changed GetText parameters, now it can get part of text
;      Changed Password character in unicode mode to Black Circle instead of asterisk
;      Changed Callback system now each gadget can have many callbacks
;      Removed SGEx_SetAttribute (all attributes now is in command)
;      Removed SGEx_SetData and SGEx_GetData (integrated in command)
;      Added Command procedure
;      Added UnbindEvent procedure
;      Added so many commands, see in command enumeration
;      Added #SGEx_CursorColor and #SGEx_DefaultTextColor attribute
;      Added InsertText procedure
;      Added focus, lostfocus and right click event
;      Fixed a bug in button click event
;      Fixed a bug in GetMousePosition for password gadgets
;
; 1.31 Used DrawText_ api instead of PB DrawText in windows (thx kenmo for writing code)
;
; 1.32 Fixed Tab and Shift + Tab in linux and mac with AGF module
;
; 1.35 Added AutoComplete future
;      Added #SGEx_Command_AutoCompleteColor in commands
;
;}


;- Read About AGF module
;  this gadget have problem in linux and mac with TAB and SHIFT + TAB key for jump from current gadget
;  to another gadget so mk-soft find a way to handle this, maybe its not perfect and unofficial but for
;  now its better than nothing. if you include AGF module here, StringGadgetEx module automatically use
;  it on mac and linux os and if yo dont like it just ignore it, so for those want to include it you just
;  must Import it here As XIncludeFile Or copy paste code:
; XIncludeFile "AGF.pbi"


;- START OF MODULE
DeclareModule StringGadgetEx
  ; Flags
CompilerIf #PB_Compiler_Version >= 540
  EnumerationBinary
    #SGEx_BorderLess
    #SGEx_ColoredBorder
    #SGEx_Numeric
    #SGEx_LowerCase
    #SGEx_UpperCase
    #SGEx_Password
  EndEnumeration
CompilerElse
  Enumeration
    #SGEx_BorderLess    = 1
    #SGEx_ColoredBorder = 2
    #SGEx_Numeric       = 4
    #SGEx_LowerCase     = 8
    #SGEx_UpperCase     = 16
    #SGEx_Password      = 32
  EndEnumeration
CompilerEndIf

  ; Buttons
  Enumeration 
    #SGEx_Left_Button
    #SGEx_Right_Button
  EndEnumeration
  
  ; Events
CompilerIf #PB_Compiler_Version >= 540
  EnumerationBinary
    #SGEx_Event_ButtonClick
    #SGEx_Event_RightClick
    #SGEx_Event_Input
    #SGEx_Event_Key
    #SGEx_Event_Focus
    #SGEx_Event_LostFocus
  EndEnumeration
CompilerElse
  Enumeration
    #SGEx_Event_ButtonClick = 1
    #SGEx_Event_RightClick  = 2
    #SGEx_Event_Input       = 4
    #SGEx_Event_Key         = 8
    #SGEx_Event_Focus       = 16
    #SGEx_Event_LostFocus   = 32
  EndEnumeration
CompilerEndIf

  ; Commands
  Enumeration 1
    #SGEx_Command_ClearSelection
    #SGEx_Command_SetSelection
    #SGEx_Command_DeleteSelection
    #SGEx_Command_GetSelectionStart
    #SGEx_Command_GetSelectionEnd
    #SGEx_Command_SelectionLength
    
    #SGEx_Command_Length
    
    #SGEx_Command_Copy
    #SGEx_Command_Cut
    #SGEx_Command_Paste
    
    #SGEx_Command_ActiveColor
    #SGEx_Command_InActiveColor
    #SGEx_Command_BorderSize
    #SGEx_Command_BackColor
    #SGEx_Command_FrontColor
    #SGEx_Command_DefaultTextColor
    #SGEx_Command_HilightColor
    #SGEx_Command_AutoCompleteColor
    
    #SGEx_Command_CursorColor
    #SGEx_Command_CursorBlinkingTime
    
    #SGEx_Command_Font
    
    #SGEx_Command_GapX
    #SGEx_Command_GapY
    
    #SGEx_Command_UserData
  EndEnumeration
  
  
  Structure SGEx_EventHelper
    Event.b
    Gadget.i
    Button.i
    Input.s
    key.i
  EndStructure
  
  ;- Public
  Declare SGEx_Create(Gadget, X, Y, Width, Height, Content$, DefaultString$ = #Null$, Flags.w = #Null)
  Declare SGEx_Free(Gadget)
  Declare SGEx_AddButton(Gadget, ButtonID, Image, Flag.b)
  Declare SGEx_RemoveButton(Gadget, ButtonID)
  Declare SGEx_SetText(Gadget, Text$)
  Declare.s SGEx_GetText(Gadget, StartPos = #PB_Ignore, EndPos = #PB_Ignore)
  Declare SGEx_InsertText(Gadget, Text$)
  Declare SGEx_BindEvent(Gadget, *Callback, EventType = #PB_All)
  Declare SGEx_UnbindEvent(Gadget, *Callback, EventType = #PB_All)
  Declare SGEx_Resize(Gadget, X = #PB_Ignore, Y = #PB_Ignore, Width = #PB_Ignore, Height = #PB_Ignore)
  Declare SGEx_Command(Gadget, Command, Param = #PB_Ignore, lParam = #PB_Ignore)
  Declare SGEx_AddACText(Gadget, Text$, NoCase.b = #False, Seprator$ = " ")
  Declare SGEx_RemoveACText(Gadget, Text$, Seprator$ = " ")
EndDeclareModule


Module StringGadgetEx
  EnableExplicit
  
  CompilerIf #PB_Compiler_Thread = #False
    CompilerError "ThreadSafe option must be enabled in Compiler Option"
  CompilerEndIf
  
  CompilerIf Not Defined(White, #PB_Constant)
    #White = $FFFFFF
  CompilerEndIf
  
  CompilerIf Not Defined(Black, #PB_Constant)
    #Black = 0
  CompilerEndIf
  
  CompilerIf Not Defined(Blue, #PB_Constant)
    #Blue = $FF0000
  CompilerEndIf
  
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    #SGEx_Control_Key = #PB_Canvas_Command
  CompilerElse
    #SGEx_Control_Key = #PB_Canvas_Control
  CompilerEndIf
  
  CompilerIf #PB_Compiler_Unicode
    #Password_Char$ = Chr($25CF)
  CompilerElse
    #Password_Char$ = "*"
  CompilerEndIf
  
  Enumeration #PB_EventType_FirstCustomValue
    #SGEx_EventType_Blink
  EndEnumeration
  
  CompilerIf #PB_Compiler_OS <> #PB_OS_Windows And Defined(AGF, #PB_Module)
    #Use_Internal_Tab = #True
  CompilerElse
    #Use_Internal_Tab = #False
  CompilerEndIf
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Linux
    #TabKey_While_Shift_Pressed = 65056
  CompilerElseIf #PB_Compiler_OS = #PB_OS_MacOS
    #TabKey_While_Shift_Pressed = 25
  CompilerEndIf
  
  Prototype Callback(*Event)
  
  Structure Buttons_desc
    Image.l
    Button.l
    Flag.b
  EndStructure
  
  Structure Callback_desc
    *Callback
    CEvents.b
  EndStructure
  
  Structure ACTexts_desc
    Text.s
    NoCase.b
  EndStructure
  
  Structure StringGadgetEx_desc
    Gadget.l
    *_Data
    
    List Buttons.Buttons_desc()
    LButtonsWidth.l
    RButtonsWidth.l
    
    List Callbacks.Callback_desc()
    Event.SGEx_EventHelper
    
    List ACTexts.ACTexts_desc()
    ACColor.l
    
    Flags.w
    
    hasFocus.b
    String.s
    DefaultString.s
    
    TextPos.i
    
    hTread.i
    BlinkingTime.w
    Blink.b
    
    GapX.b
    GapY.b
    
    CursorPosition.i
    FixCursorPosition.i
    CursorLength.i
    
    Selected.b
    
    TripleTime.i
    
    Fontid.i
    Border_Size.b
    Back_Color.l
    Front_Color.l
    DefaultTextColor.l
    HilightColor.l
    CursorColor.l
    Active_Color.l
    InActive_Color.l
  EndStructure
  
  ;- Internal Procedures
  Procedure ContrastColor(iColor)
    Protected luma.d
    ;  Counting the perceptive luminance (aka luma) - human eye favors green color... 
    luma  = (0.299 * Red(iColor) + 0.587 * Green(iColor) + 0.114 * Blue(iColor)) / 255
  
      ; Return black For bright colors, white For dark colors
    If luma > 0.5
      ProcedureReturn #Black
    Else
      ProcedureReturn #White
    EndIf
  EndProcedure
  
  
  Procedure IsInRegion(x, y, width, height, xcur, ycur)
    If xcur >= x And xcur <= x + width
      If ycur >= y And ycur <= y + height
        ProcedureReturn #True
      EndIf
    EndIf
  EndProcedure
  
  
  Procedure DispatchEvents(*buff.StringGadgetEx_desc, Event)
    Protected Callback.Callback
    
    With *buff
      ForEach \Callbacks()
        If \Callbacks()\CEvents & Event
          Callback = \Callbacks()\Callback
          \Event\Event = Event
          Callback(\Event)
        EndIf
      Next
    EndWith
  EndProcedure
  
  
  Procedure CalcButonsWidth(*buff.StringGadgetEx_desc, Left = #True)
    Protected width
    
    With *buff
      ForEach \Buttons()
        If Left
          If \Buttons()\Flag = #SGEx_Left_Button
            width + ImageWidth(\Buttons()\Image)
          EndIf
        Else
          If \Buttons()\Flag = #SGEx_Right_Button
            width + ImageWidth(\Buttons()\Image)
          EndIf
        EndIf
      Next
    EndWith
    
    ProcedureReturn width
  EndProcedure
  
  
  Procedure CheckButtonClick(*buff.StringGadgetEx_desc)
    Protected Button = -1, X, Y, LX, RX, TmpX
    
    With *buff
      X = GetGadgetAttribute(\Gadget, #PB_Canvas_MouseX)
      Y = GetGadgetAttribute(\Gadget, #PB_Canvas_MouseY)
      RX = GadgetWidth(\Gadget)
      
      ForEach \Buttons()
        If \Buttons()\Flag = #SGEx_Right_Button
          RX - ImageWidth(\Buttons()\Image)
          If IsInRegion(RX, 0, ImageWidth(\Buttons()\Image), ImageHeight(\Buttons()\Image), X, Y)
            Button = \Buttons()\Button
            Break
          EndIf
        Else
          If IsInRegion(LX, 0, ImageWidth(\Buttons()\Image), ImageHeight(\Buttons()\Image), X, Y)
            Button = \Buttons()\Button
            Break
          EndIf
          LX + ImageWidth(\Buttons()\Image)
        EndIf
      Next
    EndWith
    
    ProcedureReturn Button
  EndProcedure
  
  
  Procedure ChangeMouseCursor(*buff.StringGadgetEx_desc)
    Protected GapX, GapY, X, Y
    
    With *buff
      GapX = \GapX + \Border_Size + \LButtonsWidth
      GapY = \Border_Size
      If \GapY > -1
        GapY + \GapY
      EndIf
      
      X = GetGadgetAttribute(\Gadget, #PB_Canvas_MouseX)
      Y = GetGadgetAttribute(\Gadget, #PB_Canvas_MouseY)
      
      If IsInRegion(GapX, GapY, GadgetWidth(\Gadget) - GapX - \RButtonsWidth - \Border_Size, GadgetHeight(\Gadget) - \Border_Size * 2, X, Y)
        SetGadgetAttribute(\Gadget, #PB_Canvas_Cursor, #PB_Cursor_IBeam)
      Else
        SetGadgetAttribute(\Gadget, #PB_Canvas_Cursor, #PB_Cursor_Default)
      EndIf
      
    EndWith
  EndProcedure
  
  
  
  
  Procedure DrawingText(X, Y, Text$, FrontColor, BackColor, hDC, FontID)    
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Protected WinRect.RECT
      
      If FontID
        SelectObject_(hDC, FontID)
      Else
        SelectObject_(hDC, GetGadgetFont(#PB_Default))
      EndIf
        
      With WinRect
        \left = X
        \top = Y
        \right = OutputWidth()
        \bottom = OutputHeight()
      EndWith
        
      SetTextColor_(hDC, FrontColor)
      SetBkColor_(hDC, BackColor)
      DrawText_(hDC, Text$, -1, WinRect, #DT_SINGLELINE | #DT_NOPREFIX)
    CompilerElse
      DrawText(X, Y, Text$, FrontColor, BackColor)
    CompilerEndIf
  EndProcedure
  
  
  Procedure UpdateStringGadgetEx(*buff.StringGadgetEx_desc, On = #PB_Ignore)
    Protected index, color, String$, HilightString$, HilightPosition
    Protected GapX, GapY, Area, LeftImgwidth, RightImgwidth, hDC
    Protected NewList Buttons.Buttons_desc()
    
    With *buff
      If On = #True
        \Blink = #True
      ElseIf On = #False
        \Blink = #False
      Else
        If \hasFocus
          \Blink = #True - \Blink
        EndIf
      EndIf
      
      hDC = StartDrawing(CanvasOutput(\Gadget))
        Box(0, 0, OutputWidth(), OutputHeight(), \Back_Color)
        
        If \Fontid
          DrawingFont(\Fontid)
        Else
          DrawingFont(#PB_Default)
        EndIf
        
        GapX = \Border_Size + \GapX + \LButtonsWidth
        If \GapY > -1
          GapY = \GapY + \Border_Size
        Else
          GapY = Round((OutputHeight() - \Border_Size * 2 - TextHeight("A")) / 2, #PB_Round_Nearest)
        EndIf
        
        If \String
          If \Flags & #SGEx_Password
            For index = 1 To Len(\String)
              String$ + #Password_Char$
            Next
          Else
            String$ = \String
          EndIf
        Else
          DrawingText(GapX, GapY, \DefaultString, \DefaultTextColor, \Back_Color, hDC, \Fontid)
        EndIf
        
        If \CursorPosition = 0
          \TextPos = 0
        Else
          Area = OutputWidth() - GapX - \RButtonsWidth - \Border_Size
          
          If TextWidth(Left(String$, \CursorPosition)) >= Area
            If TextWidth(Left(String$, \CursorPosition)) - Area > \TextPos
              \TextPos = TextWidth(Left(String$, \CursorPosition)) - Area
            EndIf
          EndIf
          
          If GapX - \TextPos + TextWidth(Left(String$, \CursorPosition)) < GapX
            \TextPos - TextWidth(Mid(String$, \CursorPosition - 10, 10))
            If \TextPos < 0
              \TextPos = 0
            EndIf
          EndIf
        EndIf
        
        If ListIndex(\ACTexts()) = -1 Or \Flags & #SGEx_Password
          If \CursorLength <> 0 And \hasFocus
            If \CursorLength < 0
              HilightString$ = Mid(String$, \FixCursorPosition + 1, Abs(\CursorLength))
              HilightPosition = \FixCursorPosition + 1
            Else
              HilightString$ = Mid(String$, \CursorPosition + 1, \CursorLength)
              HilightPosition = \CursorPosition + 1
            EndIf
            
            DrawingText(GapX - \TextPos, GapY, Left(String$, HilightPosition - 1), \Front_Color, \Back_Color, hDC, \Fontid)
            DrawingText(GapX - \TextPos + TextWidth(Left(String$, HilightPosition - 1)), GapY, HilightString$, ContrastColor(\HilightColor), \HilightColor, hDC, \Fontid)
            DrawingText(GapX - \TextPos + TextWidth(Left(String$, HilightPosition - 1)) + TextWidth(HilightString$), GapY, Mid(String$, HilightPosition + Len(HilightString$)), \Front_Color, \Back_Color, hDC, \Fontid)
          Else
            DrawingText(GapX - \TextPos, GapY, String$, \Front_Color, \Back_Color, hDC, \Fontid)
          EndIf
        Else
          HilightString$ = Mid(\ACTexts()\Text, Len(String$) + 1)
          DrawingText(GapX - \TextPos, GapY, String$, \Front_Color, \Back_Color, hDC, \Fontid)
          DrawingText(GapX - \TextPos + TextWidth(String$), GapY, HilightString$, ContrastColor(\ACColor), \ACColor, hDC, \Fontid)
        EndIf
        
        If \Blink
          Protected BlinkPos.i = GapX - \TextPos + TextWidth(Left(String$, \CursorPosition))
          If \CursorPosition = 0
            BlinkPos + 1
          ElseIf \CursorPosition = Len(\String)
            BlinkPos - 1
          EndIf
          Line(BlinkPos, GapY, 1, TextHeight("A"), \CursorColor)
        EndIf
        
        DrawingMode(#PB_2DDrawing_Default)
        
        Box(\LButtonsWidth + \Border_Size, \Border_Size, \GapX, OutputHeight() - \Border_Size * 2, \Back_Color)
        
        CopyList(\Buttons(), Buttons())
        
        ForEach Buttons()
          If Buttons()\Flag = #SGEx_Left_Button
            DrawImage(ImageID(Buttons()\Image), LeftImgwidth + \Border_Size , \Border_Size)
            LeftImgwidth + ImageWidth(Buttons()\Image)
          Else
            RightImgwidth + ImageWidth(Buttons()\Image)
            DrawImage(ImageID(Buttons()\Image), OutputWidth() - \Border_Size - RightImgwidth, \Border_Size)
          EndIf
        Next
        
        FreeList(Buttons())
        
        If \Flags & #SGEx_ColoredBorder
          If \hasFocus
            color = \Active_Color
          Else
            color = \InActive_Color
          EndIf
          
          DrawingMode(#PB_2DDrawing_Outlined)
          For index = 0 To \Border_Size - 1
            Box(index, index, OutputWidth() - index * 2, OutputHeight() - index * 2, color)
          Next
        EndIf
        
      StopDrawing()
    EndWith
  EndProcedure
  
  
  Procedure CursorThread(*buff.StringGadgetEx_desc)
    With *buff
      Repeat
        If \hasFocus
          PostEvent(#PB_Event_Gadget, GetActiveWindow(), \Gadget, #SGEx_EventType_Blink)
        EndIf
        Delay(\BlinkingTime)
      ForEver
    EndWith
  EndProcedure
  
  
  Procedure GetMousePosition(gadget)
    Protected CurentPosition, X, Y
    Protected *buff.StringGadgetEx_desc
    Protected Length, index, GapX, IsIn.b
    Protected MinDistance.f = Infinity(), CursorPosition.i, Distance.f, CursorX.i
    Protected String$
    
    *buff = GetGadgetData(gadget)
    
    With *buff
      GapX = \Border_Size + \GapX + \LButtonsWidth
      X = GetGadgetAttribute(Gadget, #PB_Canvas_MouseX)
      IsIn = IsInRegion(GapX, 0, GadgetWidth(\Gadget) - \RButtonsWidth - \Border_Size - GapX, GadgetHeight(\Gadget), X, 0)
      If (Not IsIn And \Selected) Or (IsIn And Not \Selected) Or (IsIn And \Selected)
        
        If \Flags & #SGEx_Password
          For index = 1 To Len(\String)
            String$ + #Password_Char$
          Next
        Else
          String$ = \String
        EndIf
        
        Length = Len(String$)
      
        StartDrawing(CanvasOutput(Gadget))
          If \Fontid
            DrawingFont(\Fontid)
          Else
            DrawingFont(#PB_Default)
          EndIf
          For Index = 0 To Length
            CursorX = GapX + TextWidth(Left(String$, Index)) - \TextPos
            Distance = (X-CursorX)*(X-CursorX)
            If Distance < MinDistance
              MinDistance = Distance
              CursorPosition = Index
            EndIf
          Next
        StopDrawing()
        If Not IsInfinity(MinDistance)
          ProcedureReturn CursorPosition
        Else
          ProcedureReturn -1
        EndIf
      Else
       ProcedureReturn -1
      EndIf
    EndWith
  EndProcedure
  
  
  Procedure DeleteSelection(*buff.StringGadgetEx_desc)
     With *buff
        If \CursorLength < 0
           \String = Left(\String, \FixCursorPosition) + Mid(\String, \CursorPosition + 1)
           \CursorPosition = \FixCursorPosition
           \CursorLength = 0
           ProcedureReturn #True
        ElseIf \CursorLength > 0
           \String = Left(\String, \CursorPosition) + Mid(\String, \FixCursorPosition + 1)
           \FixCursorPosition = \CursorPosition
           \CursorLength = 0
           ProcedureReturn #True
        EndIf
     EndWith
  EndProcedure
  
  
  Procedure SelectWord(*buff.StringGadgetEx_desc)
    Protected GapX, GapY, X, Y, index, char.c
    
    With *buff
      GapX = \LButtonsWidth + \Border_Size + \GapX
      GapY = \Border_Size
      If \GapY > -1
        GapY + \GapY
      EndIf
      
      X = GetGadgetAttribute(\Gadget, #PB_Canvas_MouseX)
      Y = GetGadgetAttribute(\Gadget, #PB_Canvas_MouseY)
      If IsInRegion(GapX, GapY, GadgetWidth(\Gadget) - \RButtonsWidth - \Border_Size - GapX, GadgetHeight(\Gadget) - GapY - \Border_Size, X, Y)
        
        If \Flags & #SGEx_Password
          \FixCursorPosition = 0
          \CursorPosition = Len(\String)
          \CursorLength = \FixCursorPosition - \CursorPosition
        Else
          char = Asc(Mid(\String, \CursorPosition + 1, 1))
          If (char >= ' ' And char <= '/') Or (char >= ':' And char <= '@') Or (char >= '[' And char <= 96) Or (char >= '{' And char <= '~')
            \FixCursorPosition = \CursorPosition
            \CursorPosition + 1
            \CursorLength = \FixCursorPosition - \CursorPosition
          Else
            For index = \CursorPosition To 0 Step -1
              char = Asc(Mid(\String, index, 1))
              If (char >= ' ' And char <= '/') Or (char >= ':' And char <= '@') Or (char >= '[' And char <= 96) Or (char >= '{' And char <= '~')
                Break
              EndIf
            Next
            
            If index = -1
              \FixCursorPosition = 0
            Else
              \FixCursorPosition = index
            EndIf
            
            For index = \CursorPosition + 1 To Len(\String)
              char = Asc(Mid(\String, index, 1))
              If (char >= ' ' And char <= '/') Or (char >= ':' And char <= '@') Or (char >= '[' And char <= 96) Or (char >= '{' And char <= '~')
                Break
              EndIf
            Next
            
            \CursorPosition = index - 1
            
            \CursorLength = \FixCursorPosition - \CursorPosition
            If \CursorLength = 0
              \CursorLength = -1
            EndIf
          EndIf
        EndIf
        
        ProcedureReturn #True
      EndIf
    EndWith
  EndProcedure
  
  
  Procedure CutClipboard(*buff.StringGadgetEx_desc)
    Protected ClipboardText.s
    
    With *buff
      If \CursorLength <> 0
        If \CursorLength < 0
          ClipboardText = Mid(\String, \FixCursorPosition + 1, Abs(\CursorLength))
        Else
          ClipboardText = Mid(\String, \CursorPosition + 1, \CursorLength)
        EndIf
        SetClipboardText(ClipboardText)
        DeleteSelection(*buff)
        UpdateStringGadgetEx(*buff)
      EndIf
    EndWith
  EndProcedure
  
  
  Procedure CopyClipboard(*buff.StringGadgetEx_desc)
    Protected ClipboardText.s
    
    With *buff
      If \CursorLength <> 0
        If \CursorLength < 0
          ClipboardText = Mid(\String, \FixCursorPosition + 1, Abs(\CursorLength))
        Else
          ClipboardText = Mid(\String, \CursorPosition + 1, \CursorLength)
        EndIf
        SetClipboardText(ClipboardText)
      EndIf
    EndWith
  EndProcedure
  
  
  Procedure PasteClipboard(*buff.StringGadgetEx_desc)
    Protected ClipboardText.s, index, char.c
    
    With *buff
      ClipboardText = GetClipboardText()
                
      If \Flags & #SGEx_LowerCase
        ClipboardText = LCase(ClipboardText)
      ElseIf \Flags & #SGEx_UpperCase
        ClipboardText = UCase(ClipboardText)
      ElseIf \Flags & #SGEx_Numeric
        For index = 1 To Len(ClipboardText)
          char = Asc(Mid(ClipboardText, index, 1))
          If char <= '0' Or char >= '9'
            ClipboardText = #Null$
            Break
          EndIf
        Next
      EndIf
                
      If ClipboardText
        DeleteSelection(*buff)
        \String = Left(\String, \CursorPosition) + ClipboardText + Mid(\String, \CursorPosition + 1)
        \CursorPosition + Len(ClipboardText)
        UpdateStringGadgetEx(*buff)
      EndIf
    EndWith
  EndProcedure
  
  
  Procedure SelectACText(*buff.StringGadgetEx_desc, Text$)
    Protected finded.b, ACText$, tmpText$
    
    With *buff
      ForEach \ACTexts()
        ACText$ = Left(\ACTexts()\Text, Len(Text$))
;         tmpText$ = Text$
;         If \ACTexts()\NoCase
;           ACText$ = LCase(ACText$)
;           tmpText$ = LCase(tmpText$)
;         EndIf
;         If ACText$ = tmpText$
;           finded = #True
;           Break
;         EndIf
        
        If \ACTexts()\NoCase
          If LCase(ACText$) = LCase(Text$)
            finded = #True
            Break
          EndIf
        Else
          If ACText$ = Text$
            finded = #True
            Break
          EndIf
        EndIf
      Next
      
      If Not finded
        ResetList(\ACTexts())
      EndIf
    EndWith
  EndProcedure
  
  Procedure FixACText(*buff.StringGadgetEx_desc)
    With *buff
      If ListIndex(\ACTexts()) <> -1
        \String = \ACTexts()\Text
        ResetList(\ACTexts())
      EndIf
    EndWith
  EndProcedure
 
  Procedure StringGadgetExCallback()
    Protected *buff.StringGadgetEx_desc
    Protected CursorPosition
    Protected Update.b
    
    *buff = GetGadgetData(EventGadget())
    
    With *buff
    
      Select EventType()
        Case #SGEx_EventType_Blink
          UpdateStringGadgetEx(*buff)
          
          
        Case #PB_EventType_Focus
          DispatchEvents(*buff, #SGEx_Event_Focus)
          \hasFocus = #True
          Update = #True
          
          
        Case #PB_EventType_LostFocus
          ResetList(\ACTexts())
          \hasFocus = #False
          DispatchEvents(*buff, #SGEx_Event_LostFocus)
          UpdateStringGadgetEx(*buff, #False)
          
          
        Case #PB_EventType_MouseEnter
          ChangeMouseCursor(*buff)
          
          
        Case #PB_EventType_LeftDoubleClick
          If SelectWord(*buff)
            Update = #True
          EndIf
          \TripleTime = ElapsedMilliseconds()
          
          
        Case #PB_EventType_RightClick
          If IsInRegion(\GapX + \Border_Size + \LButtonsWidth, \Border_Size, GadgetWidth(\Gadget) - \GapX - \Border_Size * 2 - \LButtonsWidth - \RButtonsWidth, GadgetHeight(\Gadget) - \Border_Size * 2, GetGadgetAttribute(\Gadget, #PB_Canvas_MouseX), GetGadgetAttribute(\Gadget, #PB_Canvas_MouseY))
            DispatchEvents(*buff, #SGEx_Event_RightClick)
          EndIf
          
          
        Case #PB_EventType_LeftButtonDown
          FixACText(*buff)
          If \TripleTime
            \TripleTime = ElapsedMilliseconds() - \TripleTime
            If \TripleTime < 200
              \FixCursorPosition = 0
              \CursorPosition = Len(\String)
              \CursorLength = -\CursorPosition
              Update = #True
            EndIf
            \TripleTime = #Null
          EndIf
          
          CursorPosition = GetMousePosition(\Gadget)
          If CursorPosition <> -1 And Not Update
            \CursorPosition = CursorPosition
            \FixCursorPosition = CursorPosition
            \CursorLength = 0
            \Selected = #True
            Update = #True
          EndIf
          
          
        Case #PB_EventType_LeftButtonUp
          If Not \Selected
            \Event\Button = CheckButtonClick(*buff)
            If \Event\Button <> -1
              DispatchEvents(*buff, #SGEx_Event_ButtonClick)
            EndIf
          Else
            \Selected = #False
          EndIf
          
          
        Case #PB_EventType_MouseMove
          If \Selected
            CursorPosition = GetMousePosition(\Gadget)
            If CursorPosition <> -1 And \CursorPosition <> CursorPosition
              \CursorPosition = CursorPosition
              \CursorLength = \FixCursorPosition - CursorPosition
              Update = #True
            EndIf
          EndIf
          ChangeMouseCursor(*buff)
          
          
        Case #PB_EventType_Input
          If Not GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #SGEx_Control_Key
            \Event\Input = Chr(GetGadgetAttribute(\Gadget, #PB_Canvas_Input))
            If \Flags & #SGEx_Numeric
              If Asc(\Event\Input) < '0' Or Asc(\Event\Input) > '9'
                \Event\Input = #Null$
              EndIf
            Else
              If \Flags & #SGEx_LowerCase
                \Event\Input = LCase(\Event\Input)
              ElseIf \Flags & #SGEx_UpperCase
                \Event\Input = UCase(\Event\Input)
              EndIf
            EndIf
            
            If \Event\Input
              DeleteSelection(*buff)
              \String = Left(\String, \CursorPosition) + \Event\Input + Mid(\String, \CursorPosition + 1)
              \CursorPosition + 1
              SelectACText(*buff, \String)
              DispatchEvents(*buff, #SGEx_Event_Input)
              Update = #True
            EndIf
          EndIf
            
          
        Case #PB_EventType_KeyDown
          \Event\key = GetGadgetAttribute(\Gadget, #PB_Canvas_Key)
          Select \Event\key
            Case #PB_Shortcut_A
              If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #SGEx_Control_Key
                FixACText(*buff)
                \FixCursorPosition = 0
                \CursorPosition = Len(\String)
                \CursorLength = -\CursorPosition
                Update = #True
              EndIf
              
              
            Case #PB_Shortcut_X
              If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #SGEx_Control_Key And Not (\Flags & #SGEx_Password)
                CutClipboard(*buff)
              EndIf
              
              
            Case #PB_Shortcut_C
              If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #SGEx_Control_Key And Not (\Flags & #SGEx_Password)
                CopyClipboard(*buff)
              EndIf
              
              
            Case #PB_Shortcut_V
              If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #SGEx_Control_Key
                PasteClipboard(*buff.StringGadgetEx_desc)
                SelectACText(*buff, \String)
              EndIf
              
              
            Case #PB_Shortcut_Back
              If ListIndex(\ACTexts()) = -1
                Update = DeleteSelection(*buff)
                If Not Update
                  If \CursorPosition > 0
                    \String = Left(\String, \CursorPosition - 1) + Mid(\String, \CursorPosition + 1)
                    \CursorPosition - 1
                    Update = #True
                  EndIf
                EndIf
              Else
                ResetList(\ACTexts())
                Update = #True
              EndIf
              
              
            Case #PB_Shortcut_Delete
              If ListIndex(\ACTexts()) = -1
                Update = DeleteSelection(*buff)
                If Not Update
                  If \CursorPosition < Len(\String)
                    \String = Left(\String, \CursorPosition) + Mid(\String, \CursorPosition + 2)
                    Update = #True
                  EndIf
                EndIf
              Else
                ResetList(\ACTexts())
                Update = #True
              EndIf
              
              
            Case #PB_Shortcut_Left
              FixACText(*buff)
              If \CursorPosition > 0
                If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #PB_Canvas_Shift
                  If \CursorLength = 0
                    \FixCursorPosition = \CursorPosition
                  EndIf
                  \CursorLength + 1
                  \CursorPosition - 1
                Else
                  If \CursorLength < 0
                    \CursorPosition = \FixCursorPosition
                  ElseIf \CursorLength = 0
                    \CursorPosition - 1
                  EndIf
                  \CursorLength = 0
                EndIf
                
                Update = #True
              ElseIf \CursorLength And Not (GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #PB_Canvas_Shift)
                \CursorLength = 0
                Update = #True
              EndIf
              
              
            Case #PB_Shortcut_Right
              FixACText(*buff)
              If \CursorPosition < Len(\String)
                If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #PB_Canvas_Shift
                  If \CursorLength = 0
                    \FixCursorPosition = \CursorPosition
                  EndIf
                  \CursorLength - 1
                  \CursorPosition + 1
                Else
                  If \CursorLength > 0
                    \CursorPosition = \FixCursorPosition
                  ElseIf \CursorLength = 0
                    \CursorPosition + 1
                  EndIf
                  \CursorLength = 0
                EndIf
                
                Update = #True
              ElseIf \CursorLength And Not (GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #PB_Canvas_Shift)
                \CursorLength = 0
                Update = #True
              EndIf
              
              
            Case #PB_Shortcut_Home
              FixACText(*buff)
              \CursorLength = 0
              If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #PB_Canvas_Shift
                \FixCursorPosition = \CursorPosition
                \CursorLength = \FixCursorPosition
              EndIf
              \CursorPosition = 0
              Update = #True
              
              
            Case #PB_Shortcut_End
              FixACText(*buff)
              \CursorLength = 0
              If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #PB_Canvas_Shift
                \FixCursorPosition = \CursorPosition
                \CursorLength = \FixCursorPosition - Len(\String)
              EndIf
              \CursorPosition = Len(\String)
              Update = #True
              
              
  CompilerIf #Use_Internal_Tab
            Case #PB_Shortcut_Tab
              SetActiveGadget(AGF::GetNextGadget(\Gadget, WindowID(EventWindow())))
              
              
            Case #TabKey_While_Shift_Pressed
              If GetGadgetAttribute(\Gadget, #PB_Canvas_Modifiers) & #PB_Canvas_Shift
                SetActiveGadget(AGF::GetPreviousGadget(\Gadget, WindowID(EventWindow())))
              EndIf
  CompilerEndIf
            
          EndSelect
          
          DispatchEvents(*buff, #SGEx_Event_Key)
          
      EndSelect
      
      If Update
        UpdateStringGadgetEx(*buff, #True)
      EndIf
      
    EndWith
    
  EndProcedure
  
  ;- External Procedures
  Procedure SGEx_Create(Gadget, X, Y, Width, Height, Content$, DefaultString$ = #Null$, Flags.w = #Null)
    Protected Result
    Protected *buff.StringGadgetEx_desc
    
    If Not (Flags & #SGEx_BorderLess) And Not (Flags & #SGEx_ColoredBorder)
      Result = CanvasGadget(Gadget, X, Y, Width, Height, #PB_Canvas_Border | #PB_Canvas_Keyboard)
    Else
      Result = CanvasGadget(Gadget, X, Y, Width, Height, #PB_Canvas_Keyboard)
    EndIf
    
    If Result
      If Gadget = #PB_Any
        Gadget = Result
      EndIf
      
      *buff = AllocateMemory(SizeOf(StringGadgetEx_desc))
      InitializeStructure(*buff, StringGadgetEx_desc)
      SetGadgetData(Gadget, *buff)
      
      With *buff
        \Event\Gadget = Gadget
        \Flags = Flags
        \DefaultString = DefaultString$
        \String = Content$
        
        \Back_Color   = #White
        \Front_Color  = #Black
        CompilerIf #PB_Compiler_OS = #PB_OS_Windows
          \HilightColor = GetSysColor_(#COLOR_HIGHLIGHT)
        CompilerElse
          \HilightColor = $D77800
        CompilerEndIf
        \ACColor = \HilightColor
        \CursorColor  = #Black
        \DefaultTextColor = $999999
        
        \Gadget = Gadget
        \BlinkingTime = 500 ; MiliSeconds
        \GapX = 8
        \GapY = -1
      
        If Flags & #SGEx_ColoredBorder
          \Border_Size = 1
          \Active_Color = #Blue
          \InActive_Color = #Black
        EndIf
        
        BindGadgetEvent(Gadget, @StringGadgetExCallback())
        \hTread = CreateThread(@CursorThread(), *buff)
      EndWith
      
      UpdateStringGadgetEx(*buff)
      
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  
  Procedure SGEx_Free(Gadget)
    Protected *buff.StringGadgetEx_desc
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
      *buff = GetGadgetData(Gadget)
      With *buff
        \hasFocus = #False
        KillThread(\hTread)
      EndWith
      FreeGadget(Gadget)
      ClearStructure(*buff, StringGadgetEx_desc)
      ProcedureReturn #True
    EndIf
  EndProcedure
  
  
  Procedure SGEx_Command(Gadget, Command, Param = #PB_Ignore, lParam = #PB_Ignore)
    Protected *buff.StringGadgetEx_desc
    Protected Update.b
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
      *buff = GetGadgetData(Gadget)
      With *buff
        Select Command
          Case #SGEx_Command_ClearSelection
            \CursorLength = #Null
            Update = #True
            
          Case #SGEx_Command_SetSelection
            If lParam < 0
              lParam = Len(\String)
            EndIf
            
            If Param < 0
              Param = 0
            EndIf
            
            If Param <= Len(\String) And lParam <= Len(\String) And Param <> lParam
              \FixCursorPosition = Param
              \CursorPosition = lParam
              \CursorLength = \FixCursorPosition - \CursorPosition
              If \hasFocus
                Update = #True
              EndIf
            EndIf
            
          Case #SGEx_Command_DeleteSelection
            DeleteSelection(*buff)
            Update = #True
            
          Case #SGEx_Command_GetSelectionStart
            If \CursorLength < 0
              ProcedureReturn \FixCursorPosition
            ElseIf \CursorLength > 0
              ProcedureReturn \CursorPosition
            EndIf
            ProcedureReturn -1
            
          Case #SGEx_Command_GetSelectionEnd
            If \CursorLength < 0
              ProcedureReturn \CursorPosition
            ElseIf \CursorLength > 0
              ProcedureReturn \FixCursorPosition
            EndIf
            ProcedureReturn -1
            
          Case #SGEx_Command_SelectionLength
            ProcedureReturn Abs(\CursorLength)
            
          Case #SGEx_Command_Length
            ProcedureReturn Len(\String)
            
          Case #SGEx_Command_Copy
            CopyClipboard(*buff)
            
          Case #SGEx_Command_Cut
            CutClipboard(*buff)
            
          Case #SGEx_Command_Paste
            PasteClipboard(*buff)
            
          Case #SGEx_Command_ActiveColor
            If Param >= 0
              \Active_Color = Param
              Update = #True
            Else
              ProcedureReturn \Active_Color
            EndIf
            
          Case #SGEx_Command_AutoCompleteColor
            If Param >= 0
              \ACColor = Param
              Update = #True
            Else
              ProcedureReturn \ACColor
            EndIf
            
          Case #SGEx_Command_InActiveColor
            If Param >= 0
              \InActive_Color = Param
              Update = #True
            Else
              ProcedureReturn \InActive_Color
            EndIf
            
          Case #SGEx_Command_BackColor
            If Param >= 0
              \Back_Color = Param
              Update = #True
            Else
              ProcedureReturn \Back_Color
            EndIf
            
          Case #SGEx_Command_FrontColor
            If Param >= 0
              \Front_Color = Param
              Update = #True
            Else
              ProcedureReturn \Front_Color
            EndIf
            
          Case #SGEx_Command_DefaultTextColor
            If Param >= 0
              \DefaultTextColor = Param
              Update = #True
            Else
              ProcedureReturn \DefaultTextColor
            EndIf
            
          Case #SGEx_Command_CursorColor
            If Param >= 0
              \CursorColor = Param
              Update = #True
            Else
              ProcedureReturn \CursorColor
            EndIf
            
          Case #SGEx_Command_BorderSize
            If Param >= 0
              If \Flags & #SGEx_ColoredBorder
                \Border_Size = Param
              EndIf
              Update = #True
            Else
              ProcedureReturn \Border_Size
            EndIf
            
          Case #SGEx_Command_Font
            If IsFont(Param)
              \Fontid = FontID(Param)
              Update = #True
            EndIf
            
          Case #SGEx_Command_HilightColor
            If Param >= 0
              \HilightColor = Param
              Update = #True
            Else
              ProcedureReturn \HilightColor
            EndIf
            
          Case #SGEx_Command_GapX
            If Param = #PB_Ignore
              ProcedureReturn \GapX
            Else
              \GapX = Param
              Update = #True
            EndIf
  
          Case #SGEx_Command_GapY
            If Param = #PB_Ignore
              ProcedureReturn \GapY
            Else
              \GapY = Param
              Update = #True
            EndIf
            
          Case #SGEx_Command_UserData
            If Param = #PB_Ignore
              ProcedureReturn \_Data
            Else
              \_Data = Param
            EndIf
            
          Case #SGEx_Command_CursorBlinkingTime
            If Param > 0
              \BlinkingTime = Param
            EndIf
            
        EndSelect
        
        If Update
          UpdateStringGadgetEx(*buff)
        EndIf
      EndWith
    EndIf
    
  EndProcedure

  
  Procedure SGEx_SetText(Gadget, Text$)
    Protected *buff.StringGadgetEx_desc
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
      *buff = GetGadgetData(Gadget)
      *buff\String = Text$
      *buff\CursorPosition = 0
      *buff\CursorLength = 0
      UpdateStringGadgetEx(*buff)
    EndIf
  EndProcedure
  
  
  Procedure.s SGEx_GetText(Gadget, StartPos = #PB_Ignore, EndPos = #PB_Ignore)
    Protected *buff.StringGadgetEx_desc
    Protected String$
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
      *buff = GetGadgetData(Gadget)
      With *buff
        If StartPos >= 0 And StartPos < EndPos
          If EndPos < 0
            EndPos = Len(\String)
          EndIf
          String$ = Mid(\String, StartPos + 1, EndPos - StartPos)
        Else
          String$ = \String
        EndIf
      EndWith
      
      ProcedureReturn String$
    EndIf
  EndProcedure
  
  
  Procedure SGEx_InsertText(Gadget, Text$)
    Protected *buff.StringGadgetEx_desc
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas And Text$
      *buff = GetGadgetData(Gadget)
      DeleteSelection(*buff)
      With *buff
        \String = Left(\String, \CursorPosition) + Text$ + Mid(\String, \CursorPosition + 1)
        \CursorPosition + Len(Text$)
      EndWith
      UpdateStringGadgetEx(*buff)
    EndIf
  EndProcedure
  
  
  Procedure SGEx_AddButton(Gadget, ButtonID, Image, Flag.b)
    Protected *buff.StringGadgetEx_desc
    
    If IsImage(Image) And IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas And ButtonID >= 0
      *buff = GetGadgetData(Gadget)
      With *buff
        AddElement(\Buttons())
        \Buttons()\Button = ButtonID
        \Buttons()\Flag = Flag
        \Buttons()\Image = Image
        If Flag = #SGEx_Left_Button
          \LButtonsWidth = CalcButonsWidth(*buff)
        Else
          \RButtonsWidth = CalcButonsWidth(*buff, #False)
        EndIf
        UpdateStringGadgetEx(*buff)
      EndWith
      ProcedureReturn #True
    EndIf
    
  EndProcedure
  
  
  Procedure SGEx_RemoveButton(Gadget, ButtonID)
    Protected *buff.StringGadgetEx_desc
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
      *buff = GetGadgetData(Gadget)
      With *buff
        ForEach \Buttons()
          If \Buttons()\Button = ButtonID
            If \Buttons()\Flag = #SGEx_Left_Button
              \LButtonsWidth - ImageWidth(\Buttons()\Image)
            Else
              \RButtonsWidth - ImageWidth(\Buttons()\Image)
            EndIf
            DeleteElement(\Buttons())
            Break
          EndIf
        Next
        UpdateStringGadgetEx(*buff)
      EndWith
    EndIf
    
  EndProcedure
  
  
  Procedure SGEx_BindEvent(Gadget, *Callback, EventType = #PB_All)
    Protected *buff.StringGadgetEx_desc
    Protected finded.b
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas And *Callback
      *buff = GetGadgetData(Gadget)
      With *buff
        If EventType = #PB_All
          EventType = #SGEx_Event_ButtonClick | #SGEx_Event_Input | #SGEx_Event_Key
          EventType | #SGEx_Event_Focus | #SGEx_Event_LostFocus | #SGEx_Event_RightClick
        EndIf
        
        ForEach \Callbacks()
          If \Callbacks()\Callback = *Callback
            \Callbacks()\CEvents | EventType
            finded = #True
            Break
          EndIf
        Next
        
        If Not finded
          AddElement(\Callbacks())
          \Callbacks()\Callback = *Callback
          \Callbacks()\CEvents = EventType
        EndIf
        
      EndWith
    EndIf
  EndProcedure
  
  
  Procedure SGEx_UnbindEvent(Gadget, *Callback, EventType = #PB_All)
    Protected *buff.StringGadgetEx_desc, finded.b
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas And *Callback
      *buff = GetGadgetData(Gadget)
      With *buff
        If EventType = #PB_All
          EventType = #SGEx_Event_ButtonClick | #SGEx_Event_Input | #SGEx_Event_Key
          EventType | #SGEx_Event_Focus | #SGEx_Event_LostFocus | #SGEx_Event_RightClick
        EndIf
        
        ForEach \Callbacks()
          If \Callbacks()\Callback = *Callback
            \Callbacks()\CEvents ! EventType
            finded = #True
            Break
          EndIf
        Next
        
        If finded And \Callbacks()\CEvents = #Null
          DeleteElement(\Callbacks())
        EndIf
      EndWith
    EndIf
  EndProcedure
  
  
  Procedure SGEx_Resize(Gadget, X = #PB_Ignore, Y = #PB_Ignore, Width = #PB_Ignore, Height = #PB_Ignore)
    Protected *buff.StringGadgetEx_desc
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
      *buff = GetGadgetData(Gadget)
      ResizeGadget(Gadget, X, Y, Width, Height)
      UpdateStringGadgetEx(*buff)
    EndIf
  EndProcedure
  
  
  Procedure SGEx_AddACText(Gadget, Text$, NoCase.b = #False, Seprator$ = " ")
    Protected *buff.StringGadgetEx_desc
    Protected index, index2, field$
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
      *buff = GetGadgetData(Gadget)
      With *buff
        If Not (\Flags & #SGEx_Password)
          For index = 1 To CountString(Text$, Seprator$) + 1
            field$ = StringField(Text$, index, Seprator$)
            If \Flags & #SGEx_LowerCase
              field$ = LCase(field$)
            ElseIf \Flags & #SGEx_UpperCase
              field$ = UCase(field$)
            ElseIf \Flags & #SGEx_Numeric
              For index2 = 1 To Len(field$)
                If Asc(Mid(field$, index2, 1)) < '0' Or Asc(Mid(field$, index2, 1)) > '9'
                  field$ = #Null$
                  Break
                EndIf
              Next
            EndIf
            
            If field$
              AddElement(\ACTexts())
              \ACTexts()\Text = field$
              \ACTexts()\NoCase = NoCase
            EndIf
          Next
          SortStructuredList(\ACTexts(), #PB_Sort_Ascending, OffsetOf(ACTexts_desc\Text), TypeOf(ACTexts_desc\Text))
          ResetList(\ACTexts())
        EndIf
      EndWith
    EndIf
    
  EndProcedure
  
  
  Procedure SGEx_RemoveACText(Gadget, Text$, Seprator$ = " ")
    Protected *buff.StringGadgetEx_desc
    Protected index, reset.b
    
    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
      *buff = GetGadgetData(Gadget)
      With *buff
        If Text$ = #Null$
          ClearList(\ACTexts())
          reset = #True
        Else
          ForEach \ACTexts()
            For index = 1 To CountString(Text$, Seprator$) + 1
              If \ACTexts()\Text = StringField(Text$, index, Seprator$)
                DeleteElement(\ACTexts())
                reset = #True
                Break
              EndIf
            Next
          Next
        EndIf
        
        If reset
          ResetList(\ACTexts())
        EndIf
      EndWith
    EndIf
    
  EndProcedure
  
  
EndModule
;- END OF MODULE

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sat Oct 22, 2016 5:31 pm
by IdeasVacuum
Huge work, thanks for sharing 8)

#StringGadgetEx_FontID seems to be ignored? Perhaps you could add font selections to the example?

Code: Select all

#Font10R = 20
LoadFont(#Font10R,"Arial",10, #PB_Font_HighQuality)
SetStringGadgetExAttribute(7, #StringGadgetEx_FontID, #Font10R)

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sat Oct 22, 2016 6:07 pm
by Peyman
Example :

Code: Select all

;- START OF EXAMPLE
XIncludeFile "StringGadgetEx.pbi"

UsePNGImageDecoder()

CompilerIf Not Defined(White, #PB_Constant)
  #White = $FFFFFF
CompilerEndIf

Enumeration
  #Search_Image
  #SSL_Image
  #Voice_Search_Image
  #Search_BTN
  #SSL_BTN
  #Voice_Search_BTN
  #Font_10
  
  #Menu
  #Menu_Cut
  #Menu_Copy
  #Menu_Paste
  #Menu_Del
EndEnumeration

CatchImage(#Search_Image, ?Search_Btn)
CatchImage(#SSL_Image, ?SSL_Btn)
CatchImage(#Voice_Search_Image, ?Voice_Search_Btn)

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  LoadFont(#Font_10, "Arial", 10, #PB_Font_HighQuality)
CompilerElseIf #PB_Compiler_OS = #PB_OS_MacOS
  LoadFont(#Font_10, "Helvetica", 12)
CompilerElse
  LoadFont(#Font_10, "Arial", 10)
CompilerEndIf

UseModule StringGadgetEx

Global Menu_Gadget


Procedure StringGadgetEx_Callback(*Events.SGEx_EventHelper)
  Protected StartPos
  
  With *Events
    Select \Event
      Case #SGEx_Event_ButtonClick
        Debug "Gadget " + \Gadget + " Clicked on Button " + \Button
        If \Button = #Search_BTN And SGEx_GetText(\Gadget)
          SGEx_RemoveACText(0, "ali abood arezoo")
          MessageRequester("Search", SGEx_GetText(\Gadget))
        EndIf
        
        
      Case #SGEx_Event_Input
        ;Debug *Events\Input
        
        
      Case #SGEx_Event_RightClick
        If IsMenu(#Menu)
          If SGEx_Command(\Gadget, #SGEx_Command_SelectionLength)
            DisableMenuItem(#Menu, #Menu_Cut, #False)
            DisableMenuItem(#Menu, #Menu_Copy, #False)
            DisableMenuItem(#Menu, #Menu_Del, #False)
          Else
            DisableMenuItem(#Menu, #Menu_Cut, #True)
            DisableMenuItem(#Menu, #Menu_Copy, #True)
            DisableMenuItem(#Menu, #Menu_Del, #True)
          EndIf
          
          If GetClipboardText()
            DisableMenuItem(#Menu, #Menu_Paste, #False)
          Else
            DisableMenuItem(#Menu, #Menu_Paste, #True)
          EndIf
          Menu_Gadget = \Gadget
          DisplayPopupMenu(#Menu, WindowID(0))
        EndIf
        
        
      Case #SGEx_Event_Focus
        SGEx_Command(\Gadget, #SGEx_Command_SetSelection, 0, -1)
        Debug "Focus on " + Str(\Gadget)
        
        
      Case #SGEx_Event_Key
        If \key = #PB_Shortcut_Return And \Gadget = 7
          If SGEx_GetText(\Gadget)
            MessageRequester("Search", SGEx_GetText(\Gadget))
          EndIf
        EndIf
    EndSelect
  EndWith
EndProcedure

  
If OpenWindow(0, 0, 0, 644, 300, "StringGadgetEx", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  SGEx_Create(0, 8,  10, 306, 30, "", "Normal StringGadget...", #SGEx_ColoredBorder)
  SGEx_Create(1, 8,  45, 306, 30, "", "Numeric StringGadget", #SGEx_Numeric | #SGEx_ColoredBorder)
  SGEx_Create(2, 8,  80, 306, 30, "", "lowercase...", #SGEx_LowerCase | #SGEx_ColoredBorder)
  SGEx_Create(3, 8, 115, 306, 30, "", "uppercase...", #SGEx_UpperCase | #SGEx_ColoredBorder)
  SGEx_Create(4, 8, 150, 306, 30, "", "Borderless", #SGEx_BorderLess)
  SGEx_Create(5, 8, 185, 306, 30, "", "Bordered")
  SGEx_Create(6, 8, 220, 306, 30, "", "Password", #SGEx_Password | #SGEx_ColoredBorder)
  
  
  SGEx_Create(7, 330, 10, 306, 32, "", "Find a setting", #SGEx_ColoredBorder)
  SGEx_Command(7, #SGEx_Command_BorderSize, 2)
  SGEx_Command(7, #SGEx_Command_ActiveColor, RGB(0, 120, 215))
  SGEx_Command(7, #SGEx_Command_InActiveColor, RGB(23, 23, 23))
  SGEx_AddButton(7, #Search_BTN, #Search_Image, #SGEx_Right_Button)    
  SGEx_BindEvent(7, @StringGadgetEx_Callback())
  
  
  SGEx_Create(8, 330, 47, 306, 32, "https://www.google.com", "Some Site", #SGEx_ColoredBorder)
  SGEx_Command(8, #SGEx_Command_BorderSize, 2)
  SGEx_Command(8, #SGEx_Command_ActiveColor, RGB(0, 120, 215))
  SGEx_Command(8, #SGEx_Command_InActiveColor, RGB(23, 23, 23))
  SGEx_AddButton(8, #SSL_BTN, #SSL_Image, #SGEx_Left_Button)
  
  
  SGEx_Create(9, 330, 84, 306, 32, "", "Search Something", #SGEx_ColoredBorder)
  SGEx_Command(9, #SGEx_Command_BorderSize, 2)
  SGEx_Command(9, #SGEx_Command_ActiveColor, RGB(0, 120, 215))
  SGEx_Command(9, #SGEx_Command_InActiveColor, RGB(23, 23, 23))
  SGEx_AddButton(9, #Search_BTN, #Search_Image, #SGEx_Right_Button)
  SGEx_AddButton(9, #Voice_Search_BTN, #Voice_Search_Image, #SGEx_Right_Button)
  
  
  SGEx_Create(10, 330, 121, 306, 32, "", "Colored and GapX", #SGEx_ColoredBorder)
  SGEx_Command(10, #SGEx_Command_BorderSize, 2)
  SGEx_Command(10, #SGEx_Command_ActiveColor, RGB(0, 120, 215))
  SGEx_Command(10, #SGEx_Command_InActiveColor, RGB(23, 23, 23))
  SGEx_Command(10, #SGEx_Command_GapX, 40)
  SGEx_Command(10, #SGEx_Command_GapY, 5)
  SGEx_Command(10, #SGEx_Command_FrontColor, $FF)
  SGEx_Command(10, #SGEx_Command_HilightColor, $FF00)
  SGEx_Command(10, #SGEx_Command_CursorColor, $FF0000)
  SGEx_Command(10, #SGEx_Command_DefaultTextColor, $8CA926)
  
  SGEx_Create(11, 330, 158, 306, 32, "", "AutoComplete With Case", #SGEx_ColoredBorder)
  SGEx_Command(11, #SGEx_Command_BorderSize, 2)
  SGEx_Command(11, #SGEx_Command_ActiveColor, RGB(0, 120, 215))
  SGEx_Command(11, #SGEx_Command_InActiveColor, RGB(23, 23, 23))
  
  SGEx_Create(12, 330, 195, 306, 32, "", "AutoComplete With NoCase", #SGEx_ColoredBorder)
  SGEx_Command(12, #SGEx_Command_BorderSize, 2)
  SGEx_Command(12, #SGEx_Command_ActiveColor, RGB(0, 120, 215))
  SGEx_Command(12, #SGEx_Command_InActiveColor, RGB(23, 23, 23))
  
  ACText.s = "Else ElseIf EnableDebugger EnableExplicit End EndDataSection " +
             "EndEnumeration EndIf EndImport EndInterface EndMacro EndProcedure " +
             "EndSelect EndStructure EndStructureUnion EndWith Enumeration"
  
  SGEx_AddACText(11, ACText)
  SGEx_Command(11, #SGEx_Command_AutoCompleteColor, $FF00)
  
  SGEx_AddACText(12, ACText, #True)
  
  If CreatePopupMenu(#Menu)      ; creation of the pop-up menu begins...
    MenuItem(#Menu_Cut, "Cut")
    MenuItem(#Menu_Copy, "Copy")
    MenuItem(#Menu_Paste, "Paste")
    MenuItem(#Menu_Del, "Delete")
  EndIf
  
  For index = 0 To 12
    SGEx_Command(index, #SGEx_Command_Font, #Font_10)
    SGEx_BindEvent(index, @StringGadgetEx_Callback())
  Next
  
  ; Remove Right Click event from password gadget
  SGEx_UnBindEvent(6, @StringGadgetEx_Callback(), #SGEx_Event_RightClick)
  
  SetActiveGadget(0)
  SGEx_Command(8, #SGEx_Command_SetSelection, 6, 15)
  
  SetWindowColor(0, #White)
  
  Repeat
    event = WaitWindowEvent()
    
    Select event
      Case #PB_Event_Menu
        Select EventMenu()
          Case #Menu_Cut
            SGEx_Command(Menu_Gadget, #SGEx_Command_Cut)
            
          Case #Menu_Copy
            SGEx_Command(Menu_Gadget, #SGEx_Command_Copy)
            
          Case #Menu_Paste
            SGEx_Command(Menu_Gadget, #SGEx_Command_Paste)
            
          Case #Menu_Del
            SGEx_Command(Menu_Gadget, #SGEx_Command_DeleteSelection)
            
            
        EndSelect
        
    EndSelect
    
    
  Until event = #PB_Event_CloseWindow
  
  For index = 0 To 10
    SGEx_Free(index)
  Next
EndIf

DataSection
  Search_Btn:
    Data.q $0A1A0A0D474E5089,$524448490D000000,$1C0000001C000000,$DF0D720000000608,$5948700900000094,$0E0000C40E000073,$00001B0E2B9501C4
    Data.q $E007454D49740700,$ED2A230B2806090A,$5845740700000099,$00726F6874754174,$0C00000048CCAEA9,$6373654474584574,$006E6F6974706972
    Data.q $0A00000023210913,$79706F4374584574,$0FAC007468676972,$45740E0000003ACC,$6974616572437458,$00656D6974206E6F,$09000000090FF735
    Data.q $74666F5374584574,$FF705D0065726177,$5845740B0000003A,$69616C6373694474,$8FB4C0B70072656D,$7458457408000000,$00676E696E726157
    Data.q $0700000087E61BC0,$72756F5374584574,$00EB83FFF5006563,$4374584574080000,$F600746E656D6D6F,$7406000000BF96CC,$656C746954745845
    Data.q $01000027D2EEA800,$ED89485441444906,$D0051430848AB1D5,$C12A482256A2CB3B,$5482FDA7F0B076C6,$02485827E0AD89DA,$ECB74DE6D4558589
    Data.q $78BB032331966CEE,$217783C8A72E3CCB,$04EC4AF6F30BC222,$A594CFEF806FF04F,$2BAEB0000EBAE894,$DA18611CF3C86318,$6BAEA8186198E44B
    Data.q $7389AD69BC7CC632,$E720CE7B5B3CF34E,$DAC69A6D19F60BFC,$086318252943FA73,$529431C7166EE082,$9D679E78D2F3AED6,$C61145182C7FBEF9
    Data.q $5DAFCB2CB671FC71,$F7D0C63002083027,$B0A28A2DB6D61DFD,$4D34D3CFFB442F76,$85294196590AAAA8,$B852941B6DB04924,$BBDE5ED4D348AF5E
    Data.q $D6B492CB2A6B5A9D,$090421218C644444,$2E63BF02EFEA1E21,$811E05DB023C1879,$FFF933CD2D391111,$7F6E77704F04E9E7,$00A12D8D1576CBEC
    Data.q $AE444E4549000000
    Data.b $42,$60,$82
    
  SSL_Btn:
    Data.q $0A1A0A0D474E5089,$524448490D000000,$1C0000001C000000,$DF0D720000000608,$5948700900000094,$0E0000C40E000073,$00001B0E2B9501C4
    Data.q $E007454D49740700,$277E55283711090A,$5845740700000080,$00726F6874754174,$0C00000048CCAEA9,$6373654474584574,$006E6F6974706972
    Data.q $0A00000023210913,$79706F4374584574,$0FAC007468676972,$45740E0000003ACC,$6974616572437458,$00656D6974206E6F,$09000000090FF735
    Data.q $74666F5374584574,$FF705D0065726177,$5845740B0000003A,$69616C6373694474,$8FB4C0B70072656D,$7458457408000000,$00676E696E726157
    Data.q $0700000087E61BC0,$72756F5374584574,$00EB83FFF5006563,$4374584574080000,$F600746E656D6D6F,$7406000000BF96CC,$656C746954745845
    Data.q $00000027D2EEA800,$63894854414449C9,$023A067FFFFFFFFC,$85A8E16A365A7A26,$F1F87F1452160164,$37CD8622E0FDA185,$6AF830303030328E
    Data.q $21649167BA743358,$E2E190B03B6C3E49,$B6197BE3DB0C3BF3,$A1877E7C5C32F7C7,$4FF8642C9254C76C,$FDE0FFFBF76C9002,$35FFE7FBC1FF3873
    Data.q $3E493FE2314923FA,$0480BC832BF3F4FC,$E3F8F0C1202F2F9C,$66D16C48C83C922B,$587BCBCAE18D2331,$CFA78E19942574E5,$32CB887DA242CA24
    Data.q $12EE2FCB86060606,$C216A387C808CC6B,$80E8725912B88E81,$748378302D416CE8,$19A42D470B51C2D4,$98B2719E01910000,$45490000000019E5
    Data.b $4E,$44,$AE,$42,$60,$82
    
    
  Voice_Search_Btn:
    Data.q $0A1A0A0D474E5089,$524448490D000000,$1C0000001B000000,$C4D1900000000608,$59487009000000ED,$0E0000C40E000073,$00001B0E2B9501C4
    Data.q $E007454D49740700,$234E80352C0E160A,$5845740700000047,$00726F6874754174,$0C00000048CCAEA9,$6373654474584574,$006E6F6974706972
    Data.q $0A00000023210913,$79706F4374584574,$0FAC007468676972,$45740E0000003ACC,$6974616572437458,$00656D6974206E6F,$09000000090FF735
    Data.q $74666F5374584574,$FF705D0065726177,$5845740B0000003A,$69616C6373694474,$8FB4C0B70072656D,$7458457408000000,$00676E696E726157
    Data.q $0700000087E61BC0,$72756F5374584574,$00EB83FFF5006563,$4374584574080000,$F600746E656D6D6F,$7406000000BF96CC,$656C746954745845
    Data.q $01000027D2EEA800,$DD894854414449A8,$7FC7185144284B96,$86CA0B0C33B99987,$A63C89A32B0A5158,$8585B59488A15DD1,$0B5B5B2565B29D95
    Data.q $945142C34850AF29,$ABCA21642CA282C7,$453163998EE61DC4,$CBFE3268F32677BA,$79D7CE77EFBEF9FF,$174ACA4D24A52909,$430724C92CC37FE8
    Data.q $4211D53BE3A5B930,$46B0DAF0195956A0,$E5674B726C149AA9,$08828F9600080828,$36A93E34CDAA8301,$52F46277A3754EFA,$789BF3C185D37B02
    Data.q $66D8FD1249998129,$C9874CC4DE8FD6F6,$0775D7B2BBBFB418,$2639003BD0809ADA,$C69A94948F69D767,$C1894D6D0F1D6B7D,$010F9F8400E59DB2
    Data.q $F11D5A90178C6B50,$0965253922CE2E3E,$00272FD001DA8AC2,$2A715AD6742AF018,$C7BF42CB9740AB90,$BB9B0006717F800A,$5BF7E1438C80BBE1
    Data.q $E06455D7CBE46DA9,$EF2533369C6A3A78,$A7A191E1DF792EBC,$6E8752FBC45F2F87,$5280AC09C716D0E6,$25454B0FA7A38CDF,$8D3F3FC738B8F848
    Data.q $8A3237904203B7BE,$40186E372BF5B9BD,$5FA4318997937B60,$868738B939171D33,$20FF44FD3FA8B7B1,$F7D16696DE47A7E1,$8A5810815F5E0977
    Data.q $C97B3A388DDC514B,$1E1F15224E34282A,$310BBD0700376CD7,$B494A58E22FB32E3,$EC2B4C6DBFFAB7F1,$C328AC75DC36A613,$4E454900000000AB
    Data.b $44,$AE,$42,$60,$82
    
EndDataSection
;- END OF EXAMPLE

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sat Oct 22, 2016 7:08 pm
by davido
@Peyman,

Another very nice custom gadget.
Thank you for sharing.

I like the elegant way you have made the module cross-platform. :D

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sat Oct 22, 2016 8:00 pm
by Wolfram
Nice, but on OSX 10.7 it crashes if I close the window.

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sat Oct 22, 2016 10:19 pm
by Peyman
@davido,

your welcome, thx for your comment, its really fun to code for all platform i like it :D , this is my second gadget for all platform.

@Wolfram,

i think before the program want to exit all thread of gadgets must be killed so FreeStringGadgetEx added to example please test it again.

as i see on net default font for programs in mac is 'Helvetica' and Arial is exist on ubuntu 16.04 so these font added in example.

new version release :

Code: Select all

;{ Release info...
; 1.00 First release version
;
; 1.10 Changed #StringGadgetEx_FontID to #StringGadgetEx_Font
;      Fixed a problem in event system
;      Fixed some problem in drawing Text with GapY param
;      Fixed problem with hilighing text when gadget dont have focus
;      Fixed a bug in FreeStringGadgetEx that cause crash
;      Added Font in example
;      Added double left click for selecting texts
;}

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sat Oct 22, 2016 11:53 pm
by falsam
I appreciate this module. Thank you for sharing.

If you could add copy / paste, it would be absolutely awesome. ^^

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sun Oct 23, 2016 11:20 am
by Peyman
@falsam,

yea its a very good idea so i add Cut (CTRL + X), Copy (CTRL + C) and Paste (CTRL + V), and a triple left click for selecting all texts in StringGadgetEx.

new release:

Code: Select all

;{ Release info...
; 1.00 First release version
;
; 1.10 Changed #StringGadgetEx_FontID to #StringGadgetEx_Font
;      Fixed a problem in event system
;      Fixed some problem in drawing Text with GapY param
;      Fixed problem with hilighiting text when gadget dont have focus
;      Fixed a bug in FreeStringGadgetEx that cause crash
;      Added Font in example
;      Added double left click for selecting texts
;
; 1.20 Added CTRL + X, CTRL + C, CTRL + V for cut, copy and paste texts
;      Added Triple Left Click for selecting all text of StringGadgetEx
;}

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sun Oct 23, 2016 11:29 am
by falsam
Very good ...... but ^^

Stringgadget numeric, lowercase and uppercase no longer works with the copy / paste.

Thank for this new release.

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sun Oct 23, 2016 11:45 am
by Peyman
your are right i didn't think about them :D, now code fixed for numeric, lowercase and uppercase

Code: Select all

;{ Release info...
; 1.00 First release version
;
; 1.10 Changed #StringGadgetEx_FontID to #StringGadgetEx_Font
;      Fixed a problem in event system
;      Fixed some problem in drawing Text with GapY param
;      Fixed problem with hilighiting text when gadget dont have focus
;      Fixed a bug in FreeStringGadgetEx that cause crash
;      Added Font in example
;      Added double left click for selecting texts
;
; 1.20 Added CTRL + X, CTRL + C, CTRL + V for cut, copy and paste texts
;      Added Triple Left Click for selecting all text of StringGadgetEx
;
; 1.21 Fixed problem in CTRL + V for numeric, lowercase and uppercase gadgets
;
;}

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sun Oct 23, 2016 11:55 am
by davido
@Peyman,

I noticed that the numeric gadget only allows entry of positive integers.
Is this intentional?

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sun Oct 23, 2016 12:10 pm
by Peyman
@davido,

yes its intentional, in windows and ubuntu as i test, PB StringGadget don't let you to enter minus character i don't have mac to test it.
In windows you can paste a negative number (a number started with a minus char) but you can't type it, i didn't add this paste future because its not logical.
In ubuntu all characters truncated except numbers no matter you type it or paste it.

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sun Oct 23, 2016 12:21 pm
by Wolfram
Hi,

the crash at close window is fixed. Thanks

But copy past does not work. If I change #PB_Canvas_Control to #PB_Canvas_Command past works.
On Command + X and Command + C I write only the letter into the gadget.
With #PB_Canvas_Control nothing is happened, not letter and no copy paste.

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sun Oct 23, 2016 6:55 pm
by davido
@Peyman,
Thank you for the explanation. :D

Re: Custom StringGadget with Image button [CrossPlatform]

Posted: Sun Oct 23, 2016 7:54 pm
by Peyman
@Wolfram,

yes your are right as i see in https://support.apple.com/en-us/HT201236 in mac copy, cut and paste works with Command key instead of control so its added in the code as a compiler directive.
Please test the new code in first post.

@davido

np, thx for your comments.