Page 1 of 1

Coloring Framework

Posted: Tue Dec 13, 2005 2:51 am
by Xombie
Code updated For 5.20+

I needed some coloring for my main project in the listicons and I couldn't use gonzal's stuff (don't ask) and figured it can't be hard to do the simple kind of coloring I need without needing a high powered library like purecolor.

So... here you are. I smashed this together today with some code I stole from srod and Sparkie and some of my own stuff. It's in it's infancy but I'm posting it to see if there's interest. I'll probably be adding some other features into it and could possibly add things in that other people want but it's currently just another side project for me until I get my main project done. So work will be a little slow on it. Right now only string, text and combobox gadgets are in. Oh, also listicons. Not many functions to them and not much error checking currently. Sorry :)

Code: Select all

; By Xombie - 12/12/2005
;
Enumeration ; Window List
  #WindowMain
EndEnumeration
Enumeration ; Menu List
  #MenuMain
EndEnumeration
Enumeration ; Control List
  #ButtonTest
  #StringTest
  #TextTest
  #ComboTest
  #ListTest
EndEnumeration
;- Includes
XIncludeFile "xColor.pb"
;
;- Callback
Procedure.l WindowCallback(HandleWindow, Message, wParam, lParam)
  ; Main form callback procedure.
  lResult.l = #PB_ProcessPureBasicEvents
  ;
  lResult = xc_HandleEvents(HandleWindow, Message, wParam, lParam, lResult)
  ;
  ProcedureReturn lResult
  ;
EndProcedure
;-
;
DoQuit.b
;
If OpenWindow(#WindowMain, 100, 300, 300, 200, "Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
  ;
  
  ;           AdvancedGadgetEvents(#True)
  StringGadget(#StringTest, 0, 0, 100, 20, "")
  TextGadget(#TextTest, GadgetX(#StringTest) + GadgetWidth(#StringTest) + 1, GadgetY(#StringTest), 100, 20, "Test Text")
  ComboBoxGadget(#ComboTest, GadgetX(#StringTest), GadgetY(#StringTest) + GadgetHeight(#StringTest) + 1, 100, 200)
  ButtonGadget(#ButtonTest, GadgetX(#ComboTest), GadgetY(#ComboTest) + GadgetHeight(#ComboTest) + 1, 100, 20, "Test")
  ListIconGadget(#ListTest, GadgetX(#TextTest), GadgetY(#TextTest) + GadgetHeight(#TextTest) + 1, 150, 100, "One", 50)
  ;        EndIf
  ;
  AddGadgetItem(#ComboTest, -1, "Test01")
  AddGadgetItem(#ComboTest, -1, "Test02")
  AddGadgetItem(#ComboTest, -1, "Test03")
  AddGadgetItem(#ComboTest, -1, "Test04")
  ;
  AddGadgetColumn(#ListTest, 1, "Two", 50)
  AddGadgetItem(#ListTest, -1, "Blah" + Chr(10) + "Blah2")
  AddGadgetItem(#ListTest, -1, "Test" + Chr(10) + "Test2")
  AddGadgetItem(#ListTest, -1, "Test3" + Chr(10) + "Test4")
  AddGadgetItem(#ListTest, -1, "Test5" + Chr(10) + "Test6")
  AddGadgetItem(#ListTest, -1, "Test7" + Chr(10) + "Test8")
  AddGadgetItem(#ListTest, -1, "Test9" + Chr(10) + "Test10")
  ;
  SetWindowCallback(@WindowCallback())
  ;
  xcSetGadgetColor(#StringTest, RGB(0, 0, 0), RGB(255, 0, 0))
  xcSetGadgetColor(#TextTest, RGB(0, 64, 128), RGB(128, 64, 0))
  xcSetGadgetItemColor(#ListTest, 1, RGB(0, 0, 255), RGB(128, 128, 0))
  xcSetGadgetItemColor(#ListTest, 4, -1, RGB(128, 128, 0))
  ;
  Repeat
    ;
    EventID.l = WaitWindowEvent()
    ;
    If EventID = #PB_Event_CloseWindow  ; If the user has pressed on the close button
      ;
      DoQuit = #True
      ;
    ElseIf EventID = #PB_Event_Gadget
      ;
      If EventGadget() = #ButtonTest
        ;
        If EventType() = #PB_EventType_LeftClick
          xcRemoveColor(#StringTest)
          xcSetGadgetColor(#TextTest, RGB(0, 0, 0), RGB(64, 64, 0))
          xcSetGadgetColor(#ComboTest, RGB(0, 64, 0), 0)
          xcRemoveItemColor(#ListTest, 1)
          xcSetGadgetItemColor(#ListTest, 2, 0, RGB(255, 255, 255))
        EndIf
        ;
      EndIf
      ;
    EndIf
    ;
  Until DoQuit = #True
  
EndIf

xcDestroy()
; Free the memory used by all of the controls.
End
That's the main form I used for testing so save it as whatever you want. The next bit of code is the actual coloring stuff. I have it set as xColor.pb in the main form so you'll have to rename it there if you save it differently.

Code: Select all

; By Xombie - 12/12/2005
;- Constants
#CDDS_ITEM = $10000
#CDDS_SUBITEM = $20000
#CDDS_PREPAINT = $1
#CDDS_ITEMPREPAINT = #CDDS_ITEM | #CDDS_PREPAINT
#CDDS_SUBITEMPREPAINT = #CDDS_SUBITEM | #CDDS_ITEMPREPAINT
#CDRF_DODEFAULT = $0
#CDRF_NOTIFYITEMDRAW = $20
#CDRF_NOTIFYSUBITEMDRAW = $20
#CDRF_NEWFONT = $2
;- Enumeration
Enumeration ; Control Type Enumeration
  #xColor_Text
  #xColor_String
  #xColor_Combo
  #xColor_ListIcon
EndEnumeration
;- Structures
Structure s_xColor_Colors
  ;
  Index.l
  ; The zero index of the item to color.
  ColorBack.l
  ; The background color for the item.
  ColorFore.l
  ; The foreground color (text) of the item.
  HandleBrush.l
  ; Handle of brush used to paint the background of a control.
EndStructure
Structure s_xColor_Controls
  ;
  Identifier.l
  ; The PB identifier for the control.
  Handle.l
  ; The handle for the control.
  CallBack.l
  ; The old callback for the control.
  Type.b
  ; The type of control.
  Colors.l
  ; Pointer to an array of colors.  For controls like buttons, there will only ever be one item in the array.  Only items
  ; like listicons will have more than one item to allow for colors per item.
  Size.l
  ; The size of the color array.
  Count.l
  ; The count of items in the color array.
EndStructure
Structure s_xColor_Main
  ;
  Controls.l
  ; Pointer to an array of controls.
  Size.l
  ; Size of the control array.
  Count.l
  ; Count of controls in the array.
EndStructure
;- Global Variables
Global _xColor.s_xColor_Main
; Pointer to an array of s_xColor structures.
;- Private Procedures
Procedure.b xc_GetGadgetType(Handle.l)
  ; Return the gadget type based on the classname used in CreateWindowExW_()
  HoldString.s
  ; This will store the length of the wide character string, in characters.
  lCount.l
  ; Used to store the number of character copied.
  HoldString = Space(255)
  ; Allocate size for our string.
  GetClassName_(Handle, @HoldString, 255)
  ; Call our function to retrieve the classname.
  If HoldString = "Edit"
    ProcedureReturn #xColor_String
  ElseIf HoldString = "Static"
    ProcedureReturn #xColor_Text
  ElseIf HoldString = "ComboBox"
    ProcedureReturn #xColor_Combo
  ElseIf HoldString = "SysListView32"
    ProcedureReturn #xColor_ListIcon
  EndIf
  ;
EndProcedure
;- Private Procedures
Procedure.l xc_ColorFromHandle(Handle.l, Index.l)
  ; This will return the back color brush handle for a control and, alternately, for an item in the control.
  
  ;
  *HoldColor.s_xColor_Colors
  ;
  *HoldControl.s_xColor_Controls
  ;
  If _xColor\Controls
    ; Ensure some controls exist within the array.
    *Position = _xColor\Controls
    While *Position - _xColor\Controls < _xColor\Size
      ; Loop through the controls.
      *HoldControl = PeekL(*Position)
      ; Fill the control structure with the current control.
      If Handle = *HoldControl\Handle
        ; lParam should be the handle to the control receiving the event.  Check against the current control in the array.
        ProcedureReturn PeekL(*HoldControl\Colors)
        ; Return the pointer to the color structure.
      EndIf
      ;
      *Position + 4
      ;
    Wend
    ;
  EndIf
  ;
EndProcedure
Procedure.l xc_ControlFromHandle(Handle.l)
  ; This will return the back color brush handle for a control and, alternately, for an item in the control.
  
  ;
  *HoldControl.s_xColor_Controls
  ;
  If _xColor\Controls
    ; Ensure some controls exist within the array.
    *Position = _xColor\Controls
    While *Position - _xColor\Controls < _xColor\Size
      ; Loop through the controls.
      *HoldControl = PeekL(*Position)
      ; Fill the control structure with the current control.
      If Handle = *HoldControl\Handle : ProcedureReturn *HoldControl : EndIf
      ;
      *Position + 4
      ;
    Wend
    ;
  EndIf
  ;
EndProcedure
Procedure xc_DeleteColor(IndexControl.l, IndexItem.l)
  ; Used to remove a color from a specific line in a multi-line control.
  
  ;
EndProcedure
Procedure.b xc_GetRowColors(*HoldControl.s_xColor_Controls, Index.l, *ColorBack, *ColorFore)
  ;
  
  ;
  *HoldColor.s_xColor_Colors
  ;
  *Position = *HoldControl\Colors
  While *Position - *HoldControl\Colors < *HoldControl\Size
    ;
    *HoldColor = PeekL(*Position)
    ;
    If *HoldColor\Index = Index
      ;
      PokeL(*ColorBack, *HoldColor\ColorBack)
      PokeL(*ColorFore, *HoldColor\ColorFore)
      ;
      ProcedureReturn #True
      ;
    EndIf
    ;
    *Position + 4
    ;
  Wend
  ;
  ProcedureReturn #False
  ;
EndProcedure
;- Callbacks
Procedure.l xc_HandleComboEvents(HandleWindow.l, Message.l, wParam.l, lParam.l)
  ; Custom callback for combobox controls.
  lResult.l
  ;
  *HoldColor.s_xColor_Colors
  ;
  *HoldControl.s_xColor_Controls
  ;
  *HoldControl = xc_ControlFromHandle(HandleWindow)
  ;
  If Message = #WM_CTLCOLORLISTBOX
    ;
    *HoldColor = xc_ColorFromHandle(HandleWindow, -1)
    ; Return the pointer to the color array for the control.
    If *HoldColor
      ; Ensure the control exists in the color array.
      SetBkMode_(wParam, #TRANSPARENT)
      ;
      SetTextColor_(wParam, *HoldColor\ColorFore)
      ; Set the foreground color for the button.
      SetBkColor_(wParam, *HoldColor\ColorBack)
      ; The above line is borrowed a little from Sparkie.  ( http://forums.purebasic.com/english/viewtopic.php?t=17426 )
      lResult = *HoldColor\HandleBrush
      ; Return the handle to the color brush to show that the message was processed.
    EndIf
    ;
  Else
    ;
    lResult = CallWindowProc_(*HoldControl\CallBack, HandleWindow, Message, wParam, lParam)
    ;
  EndIf
  ;
  ProcedureReturn lResult
  ;
EndProcedure
Procedure.l xc_HandleEvents(HandleWindow.l, Message.l, wParam.l, lParam.l, lResult.l)
  ;
  HoldHandle.l
  ;
  HoldFore.l : HoldBack.l
  ;
  *HoldColor.s_xColor_Colors
  ;
  *HoldControl.s_xColor_Controls
  ;
  Protected Column, Row
  ;
  Protected *pnmh.NMHDR, *LVCDHeader.NMLVCUSTOMDRAW
  ;
  If Message = #WM_NOTIFY
    ;
    *pnmh = lParam
    ;
    If *pnmh\code = #NM_CUSTOMDRAW
      ; ListIcon custom draw code borrowed and modified from srod's code ( http://forums.purebasic.com/english/viewtopic.php?t=17662 )
      *HoldControl = xc_ControlFromHandle(*pnmh\hwndFrom)
      ;
      If *HoldControl
        ;
        *LVCDHeader = lParam
        ;
        If *LVCDHeader\nmcd\dwDrawStage = #CDDS_PREPAINT
          ;
          lResult = #CDRF_NOTIFYITEMDRAW
          ;
        ElseIf *LVCDHeader\nmcd\dwDrawStage = #CDDS_ITEMPREPAINT
          ;
          lResult = #CDRF_NOTIFYSUBITEMDRAW
          ;
        ElseIf *LVCDHeader\nmcd\dwDrawStage = #CDDS_SUBITEMPREPAINT
          ;
          Row = *LVCDHeader\nmcd\dwItemSpec
          ;
          Column = *LVCDHeader\iSubItem
          ;/
          If xc_GetRowColors(*HoldControl, Row, @HoldBack, @HoldFore)
            ;
            *LVCDHeader\clrTextBk = HoldBack
            *LVCDHeader\clrText = HoldFore
            ;
            lResult = #CDRF_NEWFONT
            ;
          EndIf
          ;/
          ; If Column = 0
          ; *LVCDHeader\clrTextBk = #green
          ; *LVCDHeader\clrText = #blue ;Text colour
          ; Else
          ; *LVCDHeader\clrTextBk = #yellow
          ; *LVCDHeader\clrText = #red
          ; EndIf
          ;/
          ;
        EndIf
        ;
      EndIf
      ;
    EndIf
    ;
  ElseIf Message = #WM_CTLCOLOREDIT Or Message = #WM_CTLCOLORSTATIC
    ; Test for a string or text color event.
    HoldHandle = lParam
    ;
    *HoldControl = xc_ControlFromHandle(HoldHandle)
    ;
    If *HoldControl
      ;
      *HoldColor = xc_ColorFromHandle(HoldHandle, -1)
      ; Return the pointer to the color array for the control.
      If *HoldColor
        ; Ensure the control exists in the color array.
        SetBkMode_(wParam, #TRANSPARENT)
        ;
        SetTextColor_(wParam, *HoldColor\ColorFore)
        ; Set the foreground color for the button.
        If *HoldControl\Type = #xColor_Combo : SetBkColor_(wParam, *HoldColor\ColorBack) : EndIf
        ; The above line is borrowed a little from Sparkie.  ( http://forums.purebasic.com/english/viewtopic.php?t=17426 ).
        ; Modified to work with these custom comboboxes.  This will force a display of the color even if the combobox is
        ; not currently editable. 
        ProcedureReturn *HoldColor\HandleBrush
        ; Return the handle to the color brush to show that the message was processed.
      EndIf
      ;
    EndIf
    ;
  EndIf
  ;
  ProcedureReturn lResult
  ;
EndProcedure
;- Public Procedures
Procedure xcSetGadgetColor(Gadget.l, ColorBack.l, ColorFore.l)
  ;
  
  ; Position pointer within the array.
  NewArray.l
  ; Array used to store new information.
  
  ; Pointer to the new array.
  ExistsControl.b
  ; True if the control already exists in the array.
  Handle.l
  ; The handle to the control.
  Type.b
  ; Temporarily store the type of the control.
  *HoldControl.s_xColor_Controls
  ; Pointer to new control structure.
  *HoldColor.s_xColor_Colors
  ; Pointer to new color structure.
  HoldStyle.l
  ; Used to store the previous style for the control.
  Handle = GadgetID(Gadget)
  ; Store the handle to the control.
  Type = xc_GetGadgetType(Handle)
  ; Retrieve the type of the control.
  If Type <> #xColor_Combo And Type <> #xColor_String And Type <> #xColor_Text : MessageRequester("Error!", "Please use xcSetGadgetItemText() to set the color for a listicon.", #PB_MessageRequester_Ok) : ProcedureReturn : EndIf
  ;
  If _xColor\Controls
    ; Controls exist within the array.
    *Position = _xColor\Controls
    While *Position - _xColor\Controls < _xColor\Size
      ; Begin looping through the existing controls.
      *HoldControl = PeekL(*Position)
      ; Fill the control structure with the current control.
      If *HoldControl\Identifier = Gadget : ExistsControl = #True : Break : EndIf
      ; Check if the control already exists in the array.
      *Position + 4
      ;
    Wend
    ;
    If ExistsControl
      ; The control already exists in the array.  Update with new color information.
      *HoldColor = PeekL(*HoldControl\Colors)
      ; For non-item controls, there is only 1 color in the array.  The first element.  Fill the color structure with this information.
      If ColorBack <> *HoldColor\ColorBack
        ; New back color.  Update.
        *HoldColor\ColorBack = ColorBack
        ; Store the new background color.
        If *HoldColor\HandleBrush : DeleteObject_(*HoldColor\HandleBrush) : EndIf
        ; Delete the previous brush if it exists.
        *HoldColor\HandleBrush = CreateSolidBrush_(ColorBack)
        ; Create the brush used to color the background.
      EndIf
      ;
      If ColorFore <> -1 : *HoldColor\ColorFore = ColorFore : EndIf
      ; Update the foreground (text) color information.
    Else
      ; The control does not exist in the array.
      NewArray = AllocateMemory(_xColor\Size + 4)
      ; Allocate room for the new control.
      CopyMemory(_xColor\Controls, NewArray, _xColor\Size)
      ; Copy the existing control information into the new array.
      *HoldControl = AllocateMemory(SizeOf(s_xColor_Controls))
      ; Allocate memory for the new control structure.
      *HoldControl\Identifier = Gadget
      *HoldControl\Handle = Handle
      *HoldControl\Type = Type
      ;
      If *HoldControl\Type = #xColor_Combo : *HoldControl\CallBack = SetWindowLong_(Handle, #GWL_WNDPROC, @xc_HandleComboEvents())  : EndIf
      ; Combobox requires a custom callback handler.
      *HoldColor = AllocateMemory(SizeOf(s_xColor_Colors))
      ;
      *HoldColor\Index = -1
      ; Non-item controls store no index.
      *HoldColor\ColorBack = ColorBack
      ; Store the back color for the control.
      *HoldColor\ColorFore = ColorFore
      ; Store the foreground (text) color for the control.
      *HoldColor\HandleBrush = CreateSolidBrush_(ColorBack)
      ; Create the brush used to color the background.
      *HoldControl\Colors = AllocateMemory(4)
      ; Allocate 4 bytes to store the color information for the control.
      PokeL(*HoldControl\Colors, *HoldColor)
      ; Insert the color structure pointer in the control structure.
      *HoldControl\Size = 4 : *HoldControl\Count = 1
      ; There is now 1 color for the control and it consumes 4 bytes of memory.
      PokeL(NewArray + _xColor\Size, *HoldControl)
      ; Insert the control information into the new array.
      FreeMemory(_xColor\Controls) : _xColor\Controls = NewArray
      ; Free the previous control array and set to the new array.
      _xColor\Size + 4 : _xColor\Count + 1
      ; Increment the control size and count values for the new control.
    EndIf
    ;
  Else
    ; No controls exist in the array.
    _xColor\Controls = AllocateMemory(4)
    ;
    *HoldControl = AllocateMemory(SizeOf(s_xColor_Controls))
    ;
    *HoldControl\Identifier = Gadget
    *HoldControl\Handle = Handle
    *HoldControl\Type = Type
    ;
    If *HoldControl\Type = #xColor_Combo : *HoldControl\CallBack = SetWindowLong_(Handle, #GWL_WNDPROC, @xc_HandleComboEvents())  : EndIf
    ; Combobox requires a custom callback handler.
    *HoldColor = AllocateMemory(SizeOf(s_xColor_Colors))
    ;
    *HoldColor\Index = -1
    ; Non-item controls store no index.
    *HoldColor\ColorBack = ColorBack
    ; Store the back color for the control.
    *HoldColor\ColorFore = ColorFore
    ; Store the foreground (text) color for the control.
    *HoldColor\HandleBrush = CreateSolidBrush_(ColorBack)
    ; Create the brush used to color the background.
    *HoldControl\Colors = AllocateMemory(4)
    ; Allocate 4 bytes to store the color information for the control.
    PokeL(*HoldControl\Colors, *HoldColor)
    ; Insert the color structure pointer in the control structure.
    *HoldControl\Size = 4 : *HoldControl\Count = 1
    ; There is now 1 color for the control and it consumes 4 bytes of memory.
    PokeL(_xColor\Controls, *HoldControl)
    ; Insert the pointer to the control into the array.
    _xColor\Size = 4 : _xColor\Count = 1
    ; The initial control is added.  One item now exists and it takes up 4 bytes of memory.
  EndIf
  ;
  InvalidateRect_(Handle, 0, #True)
  ; Invalidate the control.  This will force a redraw of the control.
EndProcedure
Procedure xcSetGadgetItemColor(Gadget.l, Index.l, ColorBack.l, ColorFore.l)
  ; Set the color for a specific row in control.  Currently only for listicons.
  
  ; Position pointer within the array.
  NewArray.l
  ; Array used to store new information.
  
  ; Pointer to the new array.
  ExistsControl.b
  ; True if the control already exists in the array.
  Handle.l
  ; The handle to the control.
  Type.b
  ; Temporarily store the type of the control.
  *HoldControl.s_xColor_Controls
  ; Pointer to new control structure.
  *HoldColor.s_xColor_Colors
  ; Pointer to new color structure.
  HoldStyle.l
  ; Used to store the previous style for the control.
  Handle = GadgetID(Gadget)
  ; Store the handle to the control.
  Type = xc_GetGadgetType(Handle)
  ; Retrieve the type of the control.
  If Type <> #xColor_ListIcon : MessageRequester("Error!", "Please use xcSetGadgetColor() for gadgets other than ListIcon()") : EndIf
  ;
  If _xColor\Controls
    ; Controls exist within the array.
    *Position = _xColor\Controls
    While *Position - _xColor\Controls < _xColor\Size
      ; Begin looping through the existing controls.
      *HoldControl = PeekL(*Position)
      ; Fill the control structure with the current control.
      If *HoldControl\Identifier = Gadget : ExistsControl = #True : Break : EndIf
      ; Check if the control already exists in the array.
      *Position + 4
      ;
    Wend
    ;
    If ExistsControl
      ; The control already exists in the array.  Update with new color information.
      *Position = *HoldControl\Colors
      While *Position - *HoldControl\Colors < *HoldControl\Size
        ;
        *HoldColor = PeekL(*Position)
        ;
        If *HoldColor\Index = Index
          ; Located the passed index within the control.
          *HoldColor\ColorBack = ColorBack
          ; A -1 means the back color should not be changed.
          *HoldColor\ColorFore = ColorFore
          ; Update the foreground (text) color information.
          ProcedureReturn
          ; Exit the procedure.
        EndIf
        ;
        *Position + 4
        ;
      Wend
      ; After this point, the index did not exist in the control.  Add the new index and it's color.
      NewArray = AllocateMemory(*HoldControl\Size + 4)
      ;
      CopyMemory(*HoldControl\Colors, NewArray, *HoldControl\Size)
      ;
      *HoldColor = AllocateMemory(SizeOf(s_xColor_Colors))
      ;
      *HoldColor\Index = Index
      ;
      *HoldColor\ColorBack = ColorBack
      ; Store the back color for the control.
      *HoldColor\ColorFore = ColorFore
      ; Store the foreground (text) color for the control.
      *HoldColor\HandleBrush = 0
      ;
      PokeL(NewArray + *HoldControl\Size, *HoldColor)
      ; Insert the color structure pointer in the control structure.
      FreeMemory(*HoldControl\Colors) : *HoldControl\Colors = NewArray
      ;
      *HoldControl\Size + 4 : *HoldControl\Count + 1
      ; There is now 1 color for the control and it consumes 4 bytes of memory.
    Else
      ; The control does not exist in the array.
      NewArray = AllocateMemory(_xColor\Size + 4)
      ; Allocate room for the new control.
      CopyMemory(_xColor\Controls, NewArray, _xColor\Size)
      ; Copy the existing control information into the new array.
      *HoldControl = AllocateMemory(SizeOf(s_xColor_Controls))
      ; Allocate memory for the new control structure.
      *HoldControl\Identifier = Gadget
      *HoldControl\Handle = Handle
      *HoldControl\Type = Type
      ;
      If *HoldControl\Type = #xColor_Combo : *HoldControl\CallBack = SetWindowLong_(Handle, #GWL_WNDPROC, @xc_HandleComboEvents())  : EndIf
      ; Combobox requires a custom callback handler.
      *HoldColor = AllocateMemory(SizeOf(s_xColor_Colors))
      ;
      *HoldColor\Index = Index
      ;
      *HoldColor\ColorBack = ColorBack
      ; Store the back color for the control.
      *HoldColor\ColorFore = ColorFore
      ; Store the foreground (text) color for the control.
      *HoldColor\HandleBrush = 0
      ; Create the brush used to color the background.
      *HoldControl\Colors = AllocateMemory(4)
      ; Allocate 4 bytes to store the color information for the control.
      PokeL(*HoldControl\Colors, *HoldColor)
      ; Insert the color structure pointer in the control structure.
      *HoldControl\Size = 4 : *HoldControl\Count = 1
      ; There is now 1 color for the control and it consumes 4 bytes of memory.
      PokeL(NewArray + _xColor\Size, *HoldControl)
      ; Insert the control information into the new array.
      FreeMemory(_xColor\Controls) : _xColor\Controls = NewArray
      ; Free the previous control array and set to the new array.
      _xColor\Size + 4 : _xColor\Count + 1
      ; Increment the control size and count values for the new control.
    EndIf
    ;
  Else
    ; No controls exist in the array.
    _xColor\Controls = AllocateMemory(4)
    ;
    *HoldControl = AllocateMemory(SizeOf(s_xColor_Controls))
    ;
    *HoldControl\Identifier = Gadget
    *HoldControl\Handle = Handle
    *HoldControl\Type = Type
    ;
    If *HoldControl\Type = #xColor_Combo : *HoldControl\CallBack = SetWindowLong_(Handle, #GWL_WNDPROC, @xc_HandleComboEvents())  : EndIf
    ; Combobox requires a custom callback handler.
    *HoldColor = AllocateMemory(SizeOf(s_xColor_Colors))
    ;
    *HoldColor\Index = Index
    ;
    *HoldColor\ColorBack = ColorBack
    ; Store the back color for the control.
    *HoldColor\ColorFore = ColorFore
    ; Store the foreground (text) color for the control.
    *HoldColor\HandleBrush = 0
    ; Create the brush used to color the background.
    *HoldControl\Colors = AllocateMemory(4)
    ; Allocate 4 bytes to store the color information for the control.
    PokeL(*HoldControl\Colors, *HoldColor)
    ; Insert the color structure pointer in the control structure.
    *HoldControl\Size = 4 : *HoldControl\Count = 1
    ; There is now 1 color for the control and it consumes 4 bytes of memory.
    PokeL(_xColor\Controls, *HoldControl)
    ; Insert the pointer to the control into the array.
    _xColor\Size = 4 : _xColor\Count = 1
    ; The initial control is added.  One item now exists and it takes up 4 bytes of memory.
  EndIf
  ;
  InvalidateRect_(Handle, 0, #True)
  ; Invalidate the control.  This will force a redraw of the control.
EndProcedure
Procedure xcRemoveItemColor(Gadget.l, Index.l)
  ;
  
  ;
  Handle.l
  ;
  ExistsIndex.b
  ;
  
  ;
  *HoldColor.s_xColor_Colors
  ;
  *HoldControl.s_xColor_Controls
  ;
  *HoldControl = xc_ControlFromHandle(GadgetID(Gadget))
  ; Fill the control structure with information about the control.
  If *HoldControl = 0 : ProcedureReturn : EndIf
  ; Ensure the control exists in the array.
  If *HoldControl\Count = 0 : ProcedureReturn : EndIf
  ; There must be colored items to remove.
  If *HoldControl\Type <> #xColor_ListIcon : ProcedureReturn : EndIf
  ; Can only currently remove color from items in a listicon.
  *Position = *HoldControl\Colors
  While *Position - *HoldControl\Colors < *HoldControl\Size
    ;
    *HoldColor = PeekL(*Position)
    ;
    If *HoldColor\Index = Index : ExistsIndex = #True : Break : EndIf
    ;
    *Position + 4
    ;
  Wend
  ;
  If ExistsIndex
    ;
    If *HoldControl\Count = 1
      ; Only one item in the control.
      FreeMemory(*HoldColor)
      ;
      FreeMemory(*HoldControl\Colors) : *HoldControl\Colors = 0
      ;
      *HoldControl\Size = 0 : *HoldControl\Count = 0
      ;
    Else
      ;
      NewArray = AllocateMemory(*HoldControl\Size - 4)
      ;
      *NewPosition = NewArray
      ;
      *Position = *HoldControl\Colors
      While *Position - *HoldControl\Colors < *HoldControl\Size
        ;
        *HoldColor = PeekL(*Position)
        ;
        If *HoldColor\Index <> Index
          ;
          PokeL(*NewPosition, *HoldColor)
          ;
          *NewPosition + 4
          ;
        Else
          ;
          FreeMemory(*HoldColor)
          ;
        EndIf
        ;
        *Position + 4
        ;
      Wend
      ;
      FreeMemory(*HoldControl\Colors) : *HoldControl\Colors = NewArray
      ;
      *HoldControl\Size - 4 : *HoldControl\Count - 1
      ;
    EndIf
    ;
  EndIf
  ;
  InvalidateRect_(*HoldControl\Handle, 0, #True)
  ; Invalidate the control.  This will force a redraw of the control.
EndProcedure
Procedure.l xcGetGadgetColor(Gadget.l, RetrieveBackColor.b)
  ; This will return the back color (if RetrieveBackColor is True) or the fore color (if RetrieveBackColor is False) for a control.
  *HoldColor.s_xColor_Colors
  ;
  *HoldControl.s_xColor_Controls
  ;
  *HoldControl = xc_ControlFromHandle(GadgetID(Gadget))
  ;
  If *HoldControl
    ;
    If *HoldControl\Type = #xColor_Text Or *HoldControl\Type = #xColor_String Or *HoldControl\Type = #xColor_Combo
      ; Single Item Control.
      *HoldColor = PeekL(*HoldControl\Colors)
      ;
      If RetrieveBackColor : ProcedureReturn *HoldColor\ColorBack : Else : ProcedureReturn *HoldColor\ColorFore : EndIf
      ;
    Else
      ; Multiple Item Control.
      ;
    EndIf
    ;
  EndIf
  ;
EndProcedure
Procedure xcRemoveColor(Gadget.l)
  ; This will remove any user defined color from a control.
  
  ;
  Handle.l
  ;
  
  ;
  *HoldColor.s_xColor_Colors
  ;
  *HoldControl.s_xColor_Controls
  ;
  If _xColor\Controls = 0 : ProcedureReturn : EndIf
  ; Ensure custom colors exist.
  *HoldControl = xc_ControlFromHandle(GadgetID(Gadget))
  ; Fill the control structure with information about the control.
  If *HoldControl = 0 : ProcedureReturn : EndIf
  ; Ensure the control exists in the array.
  Handle = *HoldControl\Handle
  ; Store the handle to the control so it can be redrawn later.
  If *HoldControl\Type = #xColor_Text Or *HoldControl\Type = #xColor_String Or *HoldControl\Type = #xColor_Combo
    ; Single Item Control.
    *HoldColor = PeekL(*HoldControl\Colors)
    ;
    If *HoldColor\HandleBrush : DeleteObject_(*HoldColor\HandleBrush) : EndIf
    ;
    FreeMemory(*HoldColor)
    ;
    FreeMemory(*HoldControl\Colors)
    ;
    If *HoldControl\CallBack : SetWindowLong_(*HoldControl\Handle, #GWL_WNDPROC, *HoldControl\CallBack) : EndIf
    ;
    FreeMemory(*HoldControl)
    ;
    If _xColor\Count = 1
      ; This was the only control in the array.
      FreeMemory(_xColor\Controls) : _xColor\Controls = 0
      ; Free the memory used to store the control array.
      _xColor\Count = 0 : _xColor\Size = 0
      ; Reset the count and size to zero.
    Else
      ;
      NewArray = AllocateMemory(_xColor\Size - 4)
      ;
      *NewPosition = NewArray
      ;
      *Position = _xColor\Controls
      ;
      While *Position - _xColor\Controls < _xColor\Size
        ;
        If PeekL(*Position) <> *HoldControl
          ;
          PokeL(*NewPosition, PeekL(*Position))
          ;
          *NewPosition + 4
          ;
        EndIf
        ;
        *Position + 4
        ;
      Wend
      ;
      FreeMemory(_xColor\Controls) : _xColor\Controls = NewArray
      ;
    EndIf
    ;
  Else
    ; Multiple Item Control.
    ;
  EndIf
  ;
  InvalidateRect_(Handle, 0, #True)
  ; Invalidate the control.  This will force a redraw of the control.
EndProcedure
Procedure xcDestroy()
  ; Used to free the memory used by all the colored controls.
  
  ;
  *HoldColor.s_xColor_Colors
  ;
  *HoldControl.s_xColor_Controls
  ;
  If _xColor\Controls
    ;
    *Position = _xColor\Controls
    While *Position - _xColor\Controls < _xColor\Size
      ;
      *HoldControl = PeekL(*Position)
      ;
      If *HoldControl\Colors
        ;
        *PositionSub = *HoldControl\Colors
        While *PositionSub - *HoldControl\Colors < *HoldControl\Size
          ;
          *HoldColor = PeekL(*PositionSub)
          ;
          If *HoldControl\CallBack : SetWindowLong_(*HoldControl\Handle, #GWL_WNDPROC, *HoldControl\CallBack) : EndIf
          ; Remove any custom callback procedures from the control.
          If *HoldColor\HandleBrush : DeleteObject_(*HoldColor\HandleBrush) : EndIf
          ;
          FreeMemory(*HoldColor)
          ;
          *PositionSub + 4
          ;
        Wend
        ;
      EndIf
      ;
      FreeMemory(*HoldControl\Colors)
      ;
      FreeMemory(*HoldControl)
      ;
      *Position + 4
      ;
    Wend
    ;
  EndIf
  ;
  FreeMemory(_xColor\Controls)
  ;
EndProcedure
And there you have it. Let me know if you spot errors. This is my first stab at doing control coloring. Use it or not :) And have fun :D

Oh, for controls with only one possible color (string, text, etc...) use xcSetGadgetColor(). For the listicon, use xcSetGadgetItemColor(). Likewise, there's two different removing procedures: xcRemoveColor() and xcRemoveItemColor(). There's also a simple xcGetGadgetColor() that I may add an index version for the item controls.

Posted: Tue Dec 13, 2005 4:04 am
by rsts
Very nice.

Looking forward to the development of this one.

Many thanks Xombie

Posted: Tue Dec 13, 2005 8:58 am
by gnozal
Sorry : Invalid memory access at line 798 in xColor.pb.

How to reproduce the error : click on 'Test' button, click on exit button [x].

PB3.94 / WinNT4

Posted: Tue Dec 13, 2005 9:03 am
by Xombie
I will take a look at it tomorrow! Thanks ^_^ There's hardly any error checks yet so I'm not surprised at all.

Posted: Tue Dec 13, 2005 12:00 pm
by Fangbeast
This is going to come in so useful for distributing nicely coloured forms free of possible compiler changes affecting libs. Of course, I am going to go mad colouring things MWUAHAHAHAHAH.

For the Americans, Colour (proper spelling) = Color (american spelling)

Just in case they had trouble reading my sentence.



(Now watch me get killed) ( NOOOO< put down that axe!!!!!!!!!)

Posted: Tue Dec 13, 2005 1:05 pm
by srod
Nice work Xombie.

Posted: Wed Dec 14, 2005 7:09 am
by Xombie
Thanks, fellas. srod - the listicon coloring is thanks to you so thank *you* :)

An updated release to fix a bug adding color to listicon rows is now available.

http://www.seijin.net/Storage/xColor.pb

@gnozal - can you try that? I swear, I don't know why my code doesn't like you :D

Posted: Wed Dec 14, 2005 8:47 am
by gnozal
Xombie wrote:@gnozal - can you try that? I swear, I don't know why my code doesn't like you :D
Some code don't work for me, probably because I don't have XP.

I have tested the new release, and again : 'invalid memory access at line 882' (still WinNT4 / PB3.94). Same procedure to get the crash.

It's like my problems with xGrid and eGrid I tested on Win98SE. The only all OS working grid for me is CSGrid.

MSDN API compatibility list

Posted: Wed Dec 14, 2005 11:46 am
by Fangbeast
There was a list on MSDN at some stage that detailed which API's were compatible with which O/S version and if you find it, it might help figure out the crashes. Same with the constants. I believe a lot of the incompatibilities were with NT because it wasn't a multimedia O/S so many things we take for granted on XP simply don't exist.

Even the NT service packs didn't remedy that because many things we use in xp were simply not part of the core O/S design in NT.

Posted: Wed Dec 14, 2005 12:32 pm
by srod
xombie wrote:srod - the listicon coloring is thanks to you so thank *you*
Actually, I stole it from El-choni - :D
gnozal wrote:It's like my problems with xGrid and eGrid I tested on Win98SE. The only all OS working grid for me is CSGrid.
egrid now works on Win 98. The problem was in how Win 98 destroyed its controls - appearing not to be as 'slapdash' as Win XP!