#include "oohg.ch"
#include "i_windefs.ch"
#include "dSign.ch"
#include "dSignv.ch"

#xcommand TEXT INTO <v> => #pragma __text|<v>+=%s+HB_EOL();IF <v> == NIL ; <v> := ""; END

//#define WM_MBUTTONUP 0x0208
REQUEST HB_CODEPAGE_ESMWIN
REQUEST HB_LANG_ES

#define SC_MOVE 61808
Static oWait

MEMVAR oMain, TitleHeight, BorderHeight, BorderWidth, oStat, ;
      oCtrlBox, cPath, oWControl, oControlP, lDist, aUndo

   
Procedure Main(cFmg)
Local cProgName, cProgExt, hWnd
PUBLIC oMain, cPath, TitleHeight, BorderHeight, BorderWidth, oStat,;
      oCtrlBox, oWControl, oControlP, lDist, aUndo := {}

   hb_FNameSplit( hb_progname(), @cPath, @cProgName, @cProgExt )
   IF IsExeRunning( cProgName+cProgExt )           // Si el programa ya está corriendo 
      If (hWnd := FindWindow("dSign_dbf")) <> 0    // encontrar hWnd
         IF ISWINDOWMINIMIZED( hWnd )              // Si está minimizado
            Restore( hWnd )                        // Restaurar
         ELSE
            SetForeGroundWindow( hWnd )            // Traer al frente
         ENDIF
         If !EMPTY(cFmg)
            MemoWrit( cPath+"dSign.ss", cFmg )     // grabar nombre del FMG
            PostMessage(hWnd, WM_MBUTTONUP, 0,0)   // Enviar WM_MBUTTONUP al progama
         Endif
         Return
      Endif
   Endif

#ifdef DIST
	lDist := .T.
#Else
	lDist := .F.
#endif

	HB_CDPSELECT('ESMWIN')
	HB_LANGSELECT('ES')	
   ChangeClasses()
   _OOHG_WinReleaseSameOrder := .F.
   _OOHG_NestedSameEvent(.T.)
   SET AUTOADJUST ON
   SET ADJUSTFONT OFF
   SET EXACT ON
   SET NAVIGATION EXTENDED    // usar enter = tab

   DEFINE WINDOW dSign_dbf OBJ oMain AT 10, 10 WIDTH 400 HEIGHT 115 ;
      ICON 'IDE_EDIT';
      TITLE "dSign   Ver: " + TRANSFORM(_HBMK_BUILD_TIMESTAMP_, "@R 9999/99/99 99:99:99");
      ON INTERACTIVECLOSE MainClose()  ;  
      ON GOTFOCUS MainGotFocus() ;
      ON INIT Actualizar();
		NOSIZE NOMAXIMIZE;
      MAIN 

      oMain:OnMClick := {|| Open( MemoRead( cPath+"dSign.ss" )), FErase( cPath+"dSign.ss") }
      DEFINE MAIN MENU
         POPUP "Fmg"
            ITEM "Nuevo Ctrl+N"        ACTION AddWindow(, 200, 200, 400, 300):Activate(.T.)
            ITEM "Abrir Ctrl+F"        ACTION Open()
            ITEM "Guardar  Ctrl+S"     ACTION Save( oMain:Cargo)
            ITEM "Guardar como..."     ACTION Save( oMain:Cargo, .T.)
            ITEM "Agregar a Proyecto OIDE"  ACTION AddToProyect(oMain:Cargo)
*				SEPARATOR
*				ITEM "Ejecutar FMG"			ACTION Probar_FMG(oMain:Cargo)
*           ITEM "Write Hash  "        ACTION Write_Hash(cPath)
*           ITEM "Read Hash   "        ACTION Read_Hash(cPath)
         END POPUP
         POPUP "Controles"
            POPUP "&Alinear   CTRL+A"
               ITEM "&Derecha"            ACTION AlignControls(oMain:Cargo, 1)
               ITEM "&Izquierda"          ACTION AlignControls(oMain:Cargo, 2)
               ITEM "Arri&ba"             ACTION AlignControls(oMain:Cargo, 3)
               ITEM "&Abajo"              ACTION AlignControls(oMain:Cargo, 4)
            END POPUP
				POPUP "Espaciar   CTRL+E"
					ITEM "Verticalmente"			ACTION SpaceControls(oMain:Cargo, 1)	
					ITEM "Horizaontalmente"		ACTION SpaceControls(oMain:Cargo, 2)	
				END POPUP
				ITEM "Borrar   DEL"           ACTION DeleteControls(oMain:Cargo)
            ITEM "Duplicar CTRL+D"        ACTION Duplicar(oMain:Cargo)
				SEPARATOR
*				ITEM "Importar del portapapeles"              ACTION CreateFMG(GETCLIPBOARDTEXT(),, .T. )
         END POPUP
         POPUP "Ayuda"     
            ITEM "Documentación"     ACTION {|| ShellExecute( 0, "open", "hh.exe", cPath+"dSign.chm",, 1 ) }
            ITEM "Acerca de"         ACTION Acerca()
        END POPUP
      End Menu
      
      DEFINE TOOLBAR MainTool BUTTONSIZE 30,30 BORDER
         BUTTON Nuevo 		PICTURE 'IDE_NEW'  	TOOLTIP "Nuevo" ACTION AddWindow(, 200, 200, 400, 300):Activate(.T.) AUTOSIZE
         BUTTON Abrir 		PICTURE 'IDE_OPEN' 	TOOLTIP "Abrir" ACTION Open() AUTOSIZE
         BUTTON Guardar 	PICTURE 'IDE_SAVE' 	TOOLTIP "Guardar" ACTION Save( oMain:Cargo) AUTOSIZE
         BUTTON Guardarc 	PICTURE 'IDE_SAVEAS' TOOLTIP "Guardar como ..." ACTION Save(oMain:Cargo, .T.) AUTOSIZE
      END TOOLBAR
      
      SET APPLICATION KEY  CTRL+S ACTION {|| Save(oMain:Cargo) }
      SET APPLICATION KEY  CTRL+O ACTION {|| Open() }
//    SET APPLICATION KEY  CTRL+Z ACTION {|| SaveTemp(oMain:Cargo) } // No usar ctrl+z
      DEFINE TIMER Update INTERVAL 500 ACTION {|| NIL } DISABLED  
/*
      @ 14, 14 LABEL LABEL_1 VALUE 'Name' WIDTH 57 HEIGHT 21 
      @ 38, 15 LABEL LABEL_2 VALUE 'Row' WIDTH 49 HEIGHT 21 
      @ 38, 131 LABEL LABEL_3 VALUE 'Col' WIDTH 29 HEIGHT 21 
      @ 63, 15 LABEL LABEL_4 VALUE 'Width' WIDTH 43 HEIGHT 21 
      @ 63, 131 LABEL LABEL_5 VALUE 'Height' WIDTH 42 HEIGHT 21 

      @ 10,  80 TEXTBOX NOMBRE   WIDTH 120 HEIGHT 21 NOTABSTOP
      @ 33,  80 TEXTBOX RENGLON  WIDTH  40 HEIGHT 21 NOTABSTOP NUMERIC INPUTMASK "9999" ON ENTER UpdateSelected(oMain:Renglon:Value,,,, oMain:Cargo)
      @ 33, 177 TEXTBOX COLUMNA  WIDTH  40 HEIGHT 21 NOTABSTOP NUMERIC INPUTMASK "9999" 
      @ 57,  80 TEXTBOX ANCHO    WIDTH  40 HEIGHT 21 NOTABSTOP NUMERIC INPUTMASK "9999" 
      @ 57, 177 TEXTBOX ALTO     WIDTH  40 HEIGHT 21 NOTABSTOP NUMERIC INPUTMASK "9999" 
*/
      DEFINE STATUSBAR OBJ oStat
         STATUSITEM "R =   0 / C =   0" WIDTH 100
         STATUSITEM "" WIDTH 200
      END STATUSBAR

      
   END WINDOW
   TitleHeight    := GetTitleHeight()  // +20 del menu
   BorderHeight   := GetBorderheight()
   BorderWidth    := GetBorderWidth()

   If !EMPTY(cFmg)
      StartTimer(1000, {||Open(cFmg)})
   Endif

   oMain:AcceptFiles := .T.
   oMain:OnDropFiles := {|f| aEval( f, {|cFile| Open(cFile) })  }

   ContrlWnd()
   WWinControl()
   WControlProperties()
   oMain:Activate()
   
RETURN

Function SaveTemp(oWin)
Local cFileName
   If HB_IsObject(oWin)
      cFileName := oWin:cFileName
      oWin:cFileName := "t"+cFileName+".bak"
      Save(oWin, .F., .F.)
   Endif
Return cFileName

Procedure AddToProyect(oWin)
Local c, cText, i := 1, j, cFmg
   If Hb_IsObject(oWin)
      If EMPTY(oWin:cFileName)
         MsgStop("Guarde primero el FMG y después agregue a proyecto", "dSign")
         Return
      Endif
      c := PutFile({{"*.pmg","*.pmg"},{"*","*.*"}})   
      If Empty(c)             
         Return               
      Endif

      If !File(c)
         MsgStop("Proyecto "+c+" no existe."+HB_eol()+"Abortando","dSign")
         Return
      Endif
      
      hb_FNameSplit( oWin:cFileName, , @cFmg,  )
      /* Load pmg */
      cText := hb_aTokens(memoread(c), hb_osNewLine())

      While .T.
         If cText[i] == "[FMG]"
            j := i+1
            WHILE LEFT(cText[j],1) <> "["
               If UPPER(cText[j]) == UPPER(cFmg)
                  MsgStop("FMG "+cFmg+" ya se encuentra en el proyecto", "dSign")
                  Return
               Endif
               j++
            Enddo
            Hb_aIns(cText, j, cFmg, .T.)
            j := ""
            AEVAL(cText, {|a| If(!EMPTY(a), j += a+hb_eol(),)})
            MEMOWRIT(c, j)
            WaitWindow("FMG "+cFmg+" ha sido agregado a "+c,1)
            Exit
         Endif
         i++
      Enddo
   Endif
Return
            

Procedure MainGotFocus()
*  AEVAL(_OOHG_FormObjects(), {|o| SetWindowPos(o:hWnd, oMain:hWnd, o:Row, o:Col, o:Width, o:Height, 0x0010) })

*   If HB_IsObject(oMain:Cargo)
*      SetWindowPos(oMain:Cargo:hWnd, oMain:hWnd, oMain:Cargo:Row, oMain:Cargo:Col, oMain:Cargo:Width, oMain:Cargo:Height, 0x0010)
*   Endif
Return

STATIC Function MainClose()
Local oWin, lClose := .T.
   For Each oWin IN _OOHG_FormObjects()
      If HB_IsArray(oWin:Cargo)
         If MODIFIED
            SetFocus(oWin:hWnd)
            If !WCierre(oWin)
               lClose := .F.
               Exit
            Endif
         Endif
      Endif
   Next
Return lClose
         
Procedure oControlP_OnSize(o)
   With Object o
		:p_Control:SizePos( 30, 10, :pTabs:Width - 20, :pTabs:Height -40)
		:p_Control:ColumnWidth(1, 130)
		:p_Control:ColumnWidth(2, :p_Control:width - 160)

		:e_Control:SizePos( 30, 10, :pTabs:Width - 20, :pTabs:Height -40)
		:e_Control:ColumnWidth(1, 130)
		:e_Control:ColumnWidth(2, :e_Control:width - 160)
		
		:Column_P:SizePos( 60, 10, :pTabs:Width - 55, :pTabs:Height -155)
		:Column_P:ColumnWidth(1, 130)
		:Column_P:ColumnWidth(2, :Column_P:width - 160)
		
		:Column_E:SizePos( :pTabs:Height-85, 10, :pTabs:Width - 55, 80)
		:Column_E:ColumnWidth(1, 130)
		:Column_E:ColumnWidth(2, :Column_E:width - 160)
		
		:bMas:SizePos(70, :pTabs:Width - 40, 32, 32)
		:bDel:SizePos(103, :pTabs:Width - 40, 32, 32)
      
		:bAddColumn:SizePos(70, :pTabs:Width - 40, 32, 32)
		:bDelColumn:SizePos(103, :pTabs:Width - 40, 32, 32)
   End With
Return


/* Properties and events control window */
Procedure WControlProperties()
Local oProperties, oEvents
   DEFINE WINDOW TEMPLATE ;
      AT 343, 236 ;
      WIDTH 380 ;
      HEIGHT 450 ;
      TITLE 'Control' ;
      OBJ oControlP ;
      NOSHOW ;
      ON INTERACTIVECLOSE .F. ;
      ON SIZE {|| oControlP_OnSize(oControlP) };
      NOMAXIMIZE ;
      CHILD

      DEFINE TAB pTabs AT 5,5 WIDTH 363 HEIGHT 390 

      DEFINE PAGE "Properties"
         @ 30, 10 GRID P_Control OBJ oProperties;
				BACKCOLOR {233,255,255};
            WIDTH 335 ;
            HEIGHT 355 ;
            HEADERS { 'Property', 'Value'} ;
            WIDTHS { 130, 180 } ;
            JUSTIFY {0,1} ;
            EDIT INPLACE DELETE ;	//EDITLIKEEXCEL;
				DELETEWHEN {|| EraseData(oProperties, oProperties:Value[1]) };
				BEFOREAUTOFIT {|n| If(n=1, .F., .T.)};
            DYNAMICCONTROLS NAVIGATEBYCELL ;
; //NOSHOWALWAYS ;
            ON BEFOREEDITCELL {|r,c| EditProperties(r,c) };
            READONLY {.T.,.F.}

      END PAGE
		oControlP:P_Control:lAdjust := .F.
		oControlP:P_Control:lKeysOn := .F.
		
      DEFINE PAGE "Events"
         @ 30, 10 GRID E_Control OBJ oEvents;
				BACKCOLOR {233,255,255};
            WIDTH 335 ;
            HEIGHT 355 ;
            HEADERS { 'Event', 'Action'} ;
            WIDTHS { 110, 200 } ;
            JUSTIFY {0,1} ;
            EDIT INPLACE DELETE ;
				DELETEWHEN {|| EraseData(oEvents, oEvents:Value[1]) };
				FIXEDCONTROLS ;
				NAVIGATEBYCELL ;
            ON BEFOREEDITCELL {|| oMain:Update:Value := 30, ;
											oMain:Update:OnClick := {|| PutMemoTitle(oEvents:aEditControls[2]) }, ;
											oMain:Update:Enabled := .T. } ;
            ON EDITCELL {|r,c| EditEvents(r,c) };
            READONLY {.T.,.F.} ;
				COLUMNCONTROLS {,{'MEMO', , /*lCleanCRLF*/, 600 /*nWidth*/, /*nHeight*/, /*lSize*/ .T. } }
      END PAGE
		oControlP:E_Control:lAdjust := .F.
		oControlP:E_Control:lKeysOn := .F.

      DEFINE PAGE "Font"
         @ 30, 10 GRID Fonts ;
				BACKCOLOR {233,255,255};
            WIDTH 335 ;
            HEIGHT 350 ;
            HEADERS { 'Property', 'Action'} ;
            WIDTHS { 110, 200 } ;
            JUSTIFY {0,1} ;
            EDIT INPLACE ;
            DYNAMICCONTROLS NAVIGATEBYCELL ;
;//            ON EDITCELL {|r,c| EditEvents(r,c) };
            READONLY {.T.,.F.}
      END PAGE

      DEFINE PAGE "Items"
         @  80, 81 LABEL LABEL_1 WIDTH 33 HEIGHT 17 RIGHTALIGN VALUE 'Width' 
         @ 143, 70 LABEL LABEL_2 WIDTH 44 HEIGHT 19 RIGHTALIGN VALUE 'Tooltip' 
         @ 110, 85 LABEL LABEL_3 WIDTH 29 HEIGHT 19 RIGHTALIGN VALUE 'Icon' 
         @ 179, 84 LABEL LABEL_4 WIDTH 30 HEIGHT 18 RIGHTALIGN VALUE 'Style' 
         @ 211, 78 LABEL LABEL_5 WIDTH 36 HEIGHT 18 RIGHTALIGN VALUE 'Align' 
         @ 242, 12 LABEL LABEL_6 WIDTH 46 HEIGHT 20 RIGHTALIGN VALUE 'Action' 

         @ 31,   27 COMBOBOX ComboItems WIDTH 225 DISPLAYEDIT
         @ 70,  132 TEXTBOX iW WIDTH 120 HEIGHT 24 NUMERIC INPUTMASK "9999"
         @ 103,  132 TEXTBOX iIcon WIDTH 120 HEIGHT 24 
         @ 136, 132 TEXTBOX iToolTip WIDTH 120 HEIGHT 24 
         @ 171, 134 COMBOBOX iStyle WIDTH 120 HEIGHT 100 ITEMS {"FLAT","RAISED"} VALUESOURCE ({"FLAT","RAISED"})
         @ 204, 134 COMBOBOX iAlign WIDTH 120 HEIGHT 100 ITEMS {"LEFT","CENTER","RIGHT"} VALUESOURCE ({"LEFT","CENTER","RIGHT"})
         @ 235,  83 TEXTBOX iAction WIDTH 172 HEIGHT 24 
         
         @ 70, 280 IMAGE bmas WIDTh 32 HEIGHT 32 PICTURE "IDE_PLUS" TOOLTIP "Agregar Status Item"
         @ 103, 280 IMAGE bdel WIDTh 32 HEIGHT 32 PICTURE "IDE_MINUS" TOOLTIP "Borrar Status Item"
		oControlP:bmas:lAdjust := .F.
		oControlP:bdel:lAdjust := .F.
         
      END PAGE

      DEFINE PAGE "Columns"
         @  31, 10 COMBOBOX ComboHeaders WIDTH 225 DISPLAYEDIT
         @  60, 10 GRID Column_P ;
				BACKCOLOR {233,255,255};
            WIDTH 300 ;
            HEIGHT 240 ;
            HEADERS { 'Property', 'Value'} ;
            WIDTHS { 130, 145 } ;
            JUSTIFY {0,1} ;
            EDIT INPLACE DELETE;
				DELETEWHEN {|| EraseColumnData(oControlP:Column_P, oControlP:Column_P:Value[1]) };
            DYNAMICCONTROLS ;
				NAVIGATEBYCELL ;
            ON BEFOREEDITCELL {|r,c| EditColumnProperties(r,c) };
            READONLY {.T.,.F.}
		oControlP:Column_P:lAdjust := .F.
		
         @ 305, 10 GRID Column_E ;
				BACKCOLOR {233,255,255};
            WIDTH 300 ;
            HEIGHT 80 ;
            HEADERS { 'Event', 'Action'} ;
            WIDTHS { 130, 160 } ;
            JUSTIFY {0,1} ;
            EDIT INPLACE DELETE;
				DELETEWHEN {|| EraseColumnData(oControlP:Column_E, oControlP:Column_E:Value[1]) };
            FIXEDCONTROLS NAVIGATEBYCELL ;
            ON BEFOREEDITCELL {|| oMain:Update:Value := 30, ;
											oMain:Update:OnClick := {|| PutMemoTitle(oControlP:Column_E:aEditControls[2]) }, ;
											oMain:Update:Enabled := .T. } ;
            ON EDITCELL {|r,c| EditColumnEvents(r,c) };
            READONLY {.T.,.F.} ;
 				COLUMNCONTROLS {,{'MEMO', , /*lCleanCRLF*/, /*nWidth*/, /*nHeight*/, /*lSize*/ .T. } }
		oControlP:Column_E:lAdjust := .F.

         @  70, 315 IMAGE bAddColumn WIDTh 32 HEIGHT 32 PICTURE "IDE_PLUS" TOOLTIP "Agregar Columna"
         @ 103, 315 IMAGE bDelColumn WIDTh 32 HEIGHT 32 PICTURE "IDE_MINUS" TOOLTIP "Borrar Columna"
		oControlP:bAddColumn:lAdjust := .F.
		oControlP:bDelColumn:lAdjust := .F.

      END PAGE

      END TAB
      oControlP:pTabs:HidePage(3)

   END WINDOW
   oControlP:SizePos(450, GetDeskTopWidth()-385)
   oControlP:Activate(.T.)
Return

*-----------------------------------------------------------------------------*
Procedure PutMemoTitle(oControl)
	If Hb_IsObject(oControl:oWindow) .AND. ValidHandler(oControl:oWindow:hWnd)
		oControl:oWindow:Title := oControl:oGrid:Item(oControl:oGrid:Value[1])[1]
		oMain:Update:Enabled := .F.
	Endif
Return

*-----------------------------------------------------------------------------*
Function EditProperties(nRow, nCol)
Local EditControl, uValue, Self := _OOHG_ThisControl, oControl, n, OldValue, lRet := .F.

   oControl := oControlP:Cargo            // Get Control from window cargo
   n := ASCAN(oControl:aDatas, {|a| a[1] == ::Cargo[nRow, 1]} )   // Search for position in adatas of control
   OldValue := oControl:aDatas[n,2]                               // Get OldValue from Control:aDatas

   If RIGHT(::Cargo[nRow,1],5) == "COLOR"    // COLOR Dialog
      OldValue := IF(EMPTY(OldValue), "", OldValue)               // conform OldValue to parameter type
      If (uValue := GetColor(OldValue))[1] <> nil
         ::Cell( nRow, nCol, xToString(uValue) )
         lRet := .T.
      Endif
      
   ElseIf ::Cargo[nRow,1] == "FONT"          // FONT dialog
      If !EMPTY(uValue := GetFont(oControl:cFontName, oControl:nFontSize, oControl:Bold, oControl:Italic, ;
				oControl:FontColor, oControl:Underline, oControl:StrikeOut, oControl:FntCharset))
*         oControl:aDatas[n,2] := uValue[1]                              // Save value in Control:aDatas
*			oControl:SetFont( uValue[1], uValue[2], uValue[3], uValue[4], uValue[6], uValue[7] )			
*			oControl:FontColor := uValue[5]
			::Cell( nRow, nCol, uValue[1] )
*        lRet := .F.
      Endif
      
   Elseif VALTYPE(OldValue) = "L"
		uValue := !OldValue
		oControl:aDatas[n,2] := uValue
		If (lRet := ValidControlProperty(oControl, n, uValue))
			::Cell( nRow, nCol, hb_cStr(uValue) )
		Endif

   Else

      EditControl := ::Cargo[nRow,3]   //GridControlObjectByType( uValue, Self )
		If !HB_IsObject(EditControl)
			MsgBox("Hubo algún problema creado EditControl", "Error en dSign")
			Return .F.
		Endif

		// Asignar valor original para ser editado
		uValue := OldValue := ::Cell(nRow, nCol)

      If ::EditCell2( @nRow, @nCol, @EditControl, OldValue , @uValue, /*cMemVar*/,/* nPosition */  )
         If VALTYPE(OldValue) <> VALTYPE(uValue) .or. OldValue <> uValue
				If (lRet := ValidControlProperty(oControl, n, @uValue))
					::Cargo[nRow,2] := uValue                               // Set new value in Grid Cargo
					::Cell( nRow, nCol, hb_cStr(uValue) )
				Endif
         Endif
      Endif
   Endif
   If lRet
      // Have control execute the value changed
      ChangeProperty(oControl, n, uValue)
	Else
		oControl:aDatas[n,2] := OldValue
   Endif

Return .F.

*-----------------------------------------------------------------------------*
Static Function EraseData(oGrid, nRow)
Local  oControl := oGrid:Parent:Cargo
Local n := ASCAN(oControl:aDatas, {|a| a[1] == oGrid:Cargo[nRow, 1]} )   // Search for position in adatas of control
Local uValue := oControl:aDatas[n,2]

	SWITCH VALTYPE(uValue)
		CASE "C"
			uValue := Blank(uValue)
			EXIT
		CASE "N"
			uValue := 0
			EXIT
		CASE "L"
			uValue := .F.
			EXIT
		CASE 'A'
			uValue := {}
	END

	If ValidControlProperty(oControl, n, @uValue)
		oControl:aDatas[n,2] := uValue
		oGrid:Cell( nRow, 2, xToString(oControl:aDatas[n, 2]) )
		ChangeProperty(oControl, n, uValue)
	Endif
RETURN .F.	
		
*-----------------------------------------------------------------------------*
Static Function EraseColumnData(oGrid, nRow)
Local  oControl := oControlP:Cargo 
Local uValue := oGrid:Cargo[nRow, 2]
//db_text(uValue)
	SWITCH VALTYPE(uValue)
		CASE "C"
			uValue := Blank(uValue)
			EXIT
		CASE "N"
			uValue := 0
			EXIT
		CASE "L"
			uValue := .F.
			EXIT
		CASE 'A'
			uValue := {}
	END
	oGrid:Cell( nRow, 2, hb_cStr(uValue) )
   ChangeColumnProperties(oControl, nRow, uValue)
RETURN .F.	
		
*-----------------------------------------------------------------------------*
Function EditColumnProperties(nRow, nCol)
Local EditControl, uValue, Self := _OOHG_ThisControl, oControl, n, OldValue

   oControl := oControlP:Cargo            // Get Control from window cargo
   n := ::Parent:ComboHeaders:Value

   OldValue := ::Cargo[nRow, 2]				//   OldValue := oControlP:Column_P:Cargo[nRow, 2]
   If n > 0 .AND. n <= LEN(oControl:aHeaders)
      EditControl := ::Cargo[nRow, 4]    // My_GridControlObjectByType()
		// Put title on memo window
		If UPPER(Editcontrol:ClassName) == "TGRIDCONTROLMEMO"
			oMain:Update:Value := 30
			oMain:Update:OnClick := {|| PutMemoTitle(EditControl) }
			oMain:Update:Enabled := .T. 
		Endif

		If VALTYPE(OldValue) = "L"
			uValue := !OldValue
			::Cargo[nRow,2] := uValue		// Asignar valor a aDatas
			::Cell( nRow, nCol, hb_cStr(uValue) )
         ChangeColumnProperties(oControl, nRow, uValue)
      ElseIf ::EditCell2( @nRow, @nCol, @EditControl, , @uValue, ,  )
         If OldValue <> uValue
				::Cargo[nRow,2] := uValue		// Asignar valor a aDatas
				::Cell( nRow, nCol, hb_cStr(uValue) )
            ChangeColumnProperties(oControl, nRow, uValue)
         Endif
      Endif
   EndIf 

Return .F.

*-----------------------------------------------------------------------------*
Function EditColumnEvents(nRow)
Local oControl

   oControl := oControlP:Cargo            // Get Control from window cargo
   DO CASE
   CASE nRow = 1
      oControl:MyUpdate("caOnHeadClick", oControlP:ComboHeaders:Value, _OOHG_ThisItemCellValue)
   CASE nRow = 2
      oControl:MyUpdate("caOnHeadDblClick", oControlP:ComboHeaders:Value, _OOHG_ThisItemCellValue)
   
   END CASe
Return .F.

*-----------------------------------------------------------------------------*
Function EditEvents(nRow)
Local Self := _OOHG_ThisControl, oControl, n

   oControl := oControlP:Cargo            // Get Control from window cargo
   n := ASCAN(oControl:aEvents, {|a| a[1] == ::Cargo[nRow, 1]} )  // Search for position in adatas of control
   If n > 0
      oControl:aEvents[n,2] := hb_StrReplace(_OOHG_ThisItemCellValue, hb_eol(), " ")
   Else
      n := ASCAN(oControl:aDatas, {|a| a[1] == ::Cargo[nRow, 1]} )   // Search for position in adatas of control
      If n > 0
         oControl:aDatas[n,2] := hb_StrReplace(_OOHG_ThisItemCellValue, hb_eol(), " ")
      Else
         msgbox("Evento no encontrado", ::Cargo[nRow,1])
      Endif
   Endif
Return .F.


/********************************************************************/
Procedure WWinControl()
Local o

   DEFINE WINDOW TEMPLATE ;
      AT 243, 236 ;
      OBJ oWControl ;
      WIDTH 500 ;
      HEIGHT 441 ;
      TITLE 'Form Controls Order' ;
      NOSHOW ;
      NOMAXIMIZE ;
      NOSIZE ;
      ON INTERACTIVECLOSE .F. ;
      CHILD

      @ 8, 10 GRID mControl ;
         OBJ o ;
         WIDTH 450 ;
         HEIGHT 372 ;
         BACKCOLOR {233,255,255};
         HEADERS { 'Name', 'Row', 'Col', 'Width', 'Height', 'Type', "Pos" } ;
         WIDTHS { 121, 40, 40, 44, 48, 100,35 } ;
         JUSTIFY {0,1,1,1,1,0,1 } ;
         INPUTMASK {,"9999","9999","9999","9999",,"999"};
         EDIT INPLACE ;
			ON BEFOREEDITCELL {|r,c| GCPreEdit(r, c)};
         ON EDITCELL {|nRow, nCol| GCEdit(nRow, nCol)};
         READONLY {.F.,.F.,.F.,.F.,.F.,.T.,.T.};
         OnClick GetControl() 
         
         o:ONRCLICK := {|...| Mover_a(...)}

      @ 20, 463 BUTTON Sig    WIDTH 25 HEIGHT 25 NO3DCOLORS PICTURE "IDE_UP"   ACTION MoveOrder(0) OBJ o
      o:lAdjust := .F.
      @ 50, 463 BUTTON Ant    WIDTH 25 HEIGHT 25 NO3DCOLORS PICTURE "IDE_DOWN" ACTION MoveOrder(1) OBJ o
      o:lAdjust := .F.
      
   END WINDOW
   oWControl:mControl:Cargo := {}
   oWControl:SizePos(10, GetDeskTopWidth()-530)
   oWControl:Activate(.T.)
Return

Static Proc Mover_A(...)
Local nPos, nRow := hb_aParams()[3], oWin := oMain:Cargo, n, cName, oCtrl
	GetControl(nRow)
	nPos := VAL(InputBox( "Move to", "Position ", nPos ))
	If nPos <> 0 .AND. nPos <> nRow		
		cName := oWControl:mControl:Cell(nPos,1)
		n := ASCAN(oWin:aControlsNames, Upper( AllTrim( cName ) ) + Chr( 255 ))
		oCtrl := aSELECTED[1]
		ReleaseControls(oWin)
		TabIndex(oCtrl, oWin:aControls[n]:Cargo[TABINDEX])
		UpdateWControls(oMain:Cargo)
		SelectControl(oCtrl)
	Endif
Return
*-----------------------------------------------------------------------------*
Procedure GetControl(nItem, lSelect)
Local oGrid := oWControl:mControl, n, lAdd := .F. , oWin := oMain:Cargo, cName
   DEFAULT nItem TO oGrid:Value
   DEFAULT lSelect TO .T.

   oMain:StatusBar:Item(2,"Item = "+LTRIM(STR(nItem)))
   If GetKeyState(VK_SHIFT) < 0 
      lAdd := .T.
   Endif
   If nItem > 0
      cName := ListViewGetItem( oGrid:hWnd, nItem, Len( oGrid:aHeaders ) )[1]
      n := ASCAN(oWin:aControlsNames, Upper( AllTrim( cName ) ) + Chr( 255 ))
      If n <> 0
         SelectControl( oWin:aControls[n], lAdd)
			CheckSelected(oWin:aControls[n]:Cargo[GRIDPOS], oWin:aControls[n]:Cargo[lSELECTED])
      Endif
   Endif
Return

*-----------------------------------------------------------------------------*
Function CheckSelected(nItem, lSelect)
Local oGrid := oWControl:mControl
   DEFAULT nItem TO oGrid:Value
   If nItem > 0
      If lSelect
*        oGrid:GridSelectedColors[1] := BLACK
*        oGrid:GridSelectedColors[2] := GREEN
         oGrid:GridForeColor[nItem] := {GRAY,GRAY,GRAY,GRAY,GRAY,GRAY}
         oGrid:GridBackColor[nItem] := {GREEN,GREEN,GREEN,GREEN,GREEN,GREEN}
      Else
         oGrid:GridSelectedColors := ACLONE(oGrid:aSelectedColors)
         oGrid:GridForeColor[nItem] := {BLACK,BLACK,BLACK,BLACK,BLACK,BLACK}
         oGrid:GridBackColor[nItem] := {{233,255,255},{233,255,255},{233,255,255},{233,255,255},{233,255,255},{233,255,255}}
      Endif
      ListView_SetCursel( oGrid:hWnd, nItem )
		oGrid:Value := nItem
   Endif
Return NIL

*-----------------------------------------------------------------------------*
STATIC Procedure MoveOrder(x)
Local nItem, n, oWin := oMain:Cargo, oCtrl
	If EMPTY(aSELECTED) .OR. LEN(aSELECTED) > 1
		Return
	ENDIF
	oCtrl := aSELECTED[1]
   WITH OBJECT oWControl:mControl
      nItem := :Value
		n := oCtrl:Cargo[TABINDEX]
      If x = 0 
         If nItem > 1   //Up
				ReleaseControls(oWin)
*            ListViewDeleteString( :hWnd, nItem )
*            INSERTLISTVIEWITEM( :hWnd, aRow, nItem-1)
*            LISTVIEW_UPDATE(:hWnd, nItem)
*            LISTVIEW_UPDATE(:hWnd, nItem-1)
				TabIndex(oCtrl, n-1)
				UpdateWControls(oMain:Cargo)
				SelectControl(oCtrl)
         Endif
      ElseIf nItem > 0 .AND. nItem < oWControl:mControl:ItemCount()
			ReleaseControls(oWin)
			TabIndex(oCtrl, n+1)
			UpdateWControls(oMain:Cargo)
			SelectControl(oCtrl)
      Endif
   END WITH
Return

// Cambia el orden de los tabs y posición de acontrols
// nPos es la nueva posición
// n es posición actual
// Para cambiar el orden, es necesario poner el control en aControls en el orden correcto
Function TabIndex(o, nPos)
Local oWin := o:Parent, n, x
	n := o:Cargo[TABINDEX]						// Position in aCtrlsTabIndxs
	nPos := MIN(nPos, LEN(oWin:aControls))	// POSITION CANT BE GREATER THAN ACONTROLS
	If n <> nPos
		x := oWin:aControlsNames[n]
		hb_aDel(oWin:aControlsNames, n)
		hb_aIns(oWin:aControlsNames, nPos, x)

		x := oWin:aControls[n]
		hb_aDel(oWin:aControls, n)
		hb_aIns(oWin:aControls, nPos, x)

		For x := 2 to Len(oWin:aControls)
			SetTabAfter( oWin:aControls[ x ]:hWnd, oWin:aControls[ x - 1 ]:hWnd )
		Next x
		// Update TABINDEX in cargo array
		o:Cargo[TABINDEX] := nPos
	Endif
Return nPos

Function Swap(a, x, y)
Local z := a[x]
   a[x] := a[y]
   a[y] := z
Return a

STATIC Function GCPreEdit(nRow, nCol)
Local oWin := oMain:Cargo, oControl, n
Local lRet := .T.
	// Find Control based on GridPos
   n := ASCAN(oWin:aControls, {|o| If(EMPTY(o:Cargo), .F., o:Cargo[2] == nRow)})
   oControl := oWin:aControls[n]
	If nCol > 1		// Row
		If ASCAN({"STATUSBAR","TOOLBAR","TOOLBUTTON"}, oControl:Type) > 0
			lRet := .F.
		Endif
	Endif
Return lRet

STATIC Procedure GCEdit(nRow, nCol)
Local oWin := oMain:Cargo, oControl, n
	MODIFIED := .T.
	// Find Control based on GridPos
   n := ASCAN(oWin:aControls, {|o| If(EMPTY(o:Cargo), .F., o:Cargo[2] == nRow)})

   oControl := oWin:aControls[n]
   If nCol = 1		// Control Name
      _OOHG_ThisItemCellValue := oControl:aDatas[NAME,2] := UPPER(_OOHG_ThisItemCellValue)
      If __objHasData( oControl, "TOOLTIP" ) //.and. !EMPTY(cName)   
			oControl:ToolTip := "["+ oControl:aDatas[NAME,2] +"]  "+ IF(HB_IsSTRING(oControl:aDatas[dTOOLTIP,2]),oControl:aDatas[dTOOLTIP,2],"")
      Endif
		oControlP:P_Control:Cell(1,2, _OOHG_ThisItemCellValue)	// Actualizar en Propiedades
oWControl:mControl:Cell(nRow, nCol, _OOHG_ThisItemCellValue)
   ElseIf nCol = 2	// Control Row
		Invalidate(oWin)
      oControl:SizePos(VAL(_OOHG_ThisItemCellValue))
   ElseIf nCol = 3	// Control Column
		Invalidate(oWin)
      oControl:SizePos(,VAL(_OOHG_ThisItemCellValue))
   ElseIf nCol = 4	// Control Width
		Invalidate(oWin)
      oControl:SizePos(,,VAL(_OOHG_ThisItemCellValue))
*      oControl:aDatas[dWIDTH,2] := VAL(_OOHG_ThisItemCellValue)
   ElseIf nCol = 5	// Control Height
		Invalidate(oWin)
      oControl:SizePos(,,,VAL(_OOHG_ThisItemCellValue))
		If oControl:Type == "COMBO"
		// Unica manera encontrada para actualizar la altura de un combobox es realizando la asignación a dHEIGHT y usarla al guardar
			oControl:aDatas[dHEIGHT,2] := VAL(_OOHG_ThisItemCellValue)
		Endif
   Endif
	DrawB(oWin)
Return

Procedure WinControl(aItems)
DEFAULT aItems TO {{"",0,0,0,0, ""}}
   oWControl:mControl:aItems := aItems
// oWControl:mControl:Refresh()
   If !oWControl:Visible
      oWControl:Show()
   Endif
Return
      
Procedure ChangeClasses()
Local oClass
   oClass := TWindow()
   _objAddData(oClass, "aDatas", {})      // to save aDatas
   _objAddData(oClass, "aEvents", {})     // to save aEvents
   __objAddData(oClass, "cFileName")   	// File Name of FMG
   __objAddData(oClass, "cPath")       	// Path of FMG
   __objAddData(oClass, "cBeFore", "")    // to save stuff before the window/control
   __objAddData(oClass, "cAfter", "")     // to save stuff after the window/control
   __objAddData(oClass, "lDone")    		// Saved flag
   __objAddData(oClass, "lHasFocus")   

  oClass := TRadioItem()
   __objDelMethod( oClass, "Events" )
   __objAddMethod( oClass, "Events", @MyEvents())
   
Return

#include "hbclass.ch"
#include "error.ch"
FUNCTION _objAddData( oObject, cSymbol, xInit )		
   LOCAL nSeq, hClass

   IF ! ISOBJECT( oObject ) .OR. ! ISCHARACTER( cSymbol )
      __errRT_BASE( EG_ARG, 3101, NIL, ProcName( 0 ) )
   ELSEIF !__objHasMsg( oObject, cSymbol ) .AND. !__objHasMsg( oObject, "_" + cSymbol )
      hClass := oObject:ClassH
      nSeq   := __cls_IncData( hClass )         // Allocate new Seq#
      __clsAddMsg( hClass,       cSymbol, nSeq, HB_OO_MSG_ACCESS, xInit, 1 )
      __clsAddMsg( hClass, "_" + cSymbol, nSeq, HB_OO_MSG_ASSIGN, xInit, 1 )
   ENDIF

RETURN oObject


Procedure StartTimer(nValue, bBlock)
   oMain:Update:OnClick := {|| oMain:Update:Enabled := .F., EVAL(bBlock) }
   oMain:Update:Value := nValue
   oMain:Update:Enabled := .T.
Return


Procedure ContrlWnd()
Static o

   #include 'dcontrols.fmg'
   o := oCtrlBox:Control_01
   oCtrlBox:Activate(.T.)
Return 

Procedure Acerca()
Local oWin, cStr

   If _IsWindowDefined("Acerca")
      GetFormObject( "Acerca" ):SetFocus()
   Else
      DEFINE WINDOW Acerca OBJ oWin;
         AT 0,0 ;
         WIDTH 420 ;
         HEIGHT 400 ;
         TITLE 'Acerca de...' 

Text into cStr

En software, muy raramente partimos de requisitos con sentido.
Incluso teniéndolos, la única medida del éxito que importa es 
si nuestra solución resuelve la cambiante idea que el cliente 
tiene de lo que es su problema
* Jeff Atwood  *

EndText

         @ 10, 10 LABEL 0 WIDTH 380 CENTERALIGN VALUE "Compu Art MX"
         @ 30, 10 LABEL 0 WIDTH 380 CENTERALIGN VALUE "David B. Field"
         @ 50, 10 LABEL 0 WIDTH 380 CENTERALIGN VALUE "Cel: +52 33 1051 7921"
         @ 70, 10 LABEL 0 WIDTH 380 CENTERALIGN VALUE "Version: "+TRANSFORM(_HBMK_BUILD_TIMESTAMP_, "@R 9999/99/99 99:99:99")
         @ 100, 10 LABEL 0 WIDTH 380 CENTERALIGN VALUE cStr HEIGHT 100 BORDER BACKCOLOR WHITE
         
         @ 210, 10 LABEL 0 WIDTH 380 CENTERALIGN VALUE "Click abajo para descargar"
         @ 230,85 HYPERLINK 0 WIDTH 380 HANDCURSOR VALUE "www.compuartmx.com/descargas/dSignSetup.exe" ;
            ADDRESS "http://compuartmx.com/descargas/dSignSetup.exe"
         @ 250,10 LABEL 0 WIDTH 380 CENTERALIGN VALUE hb_BuildInfo(5)   // Harbour Version()
         @ 270,10 LABEL 0 WIDTH 380 CENTERALIGN VALUE hb_BuildInfo(7)   // Mingw Version()
         @ 290,10 LABEL 0 WIDTH 380 CENTERALIGN VALUE OOHGVersion()
         @ 320,150 BUTTON Aceptar CAPTION "Cerrar" ACTION oWin:Release()
      END WINDOW
      oWin:Center()
      oWin:Activate(.T.)
   Endif
Return


/* Definicion de aControls
1 = Tipo de control (Textbox, combobox, checkbox, etc)
2 = Texto del control
3 = Valor inicial 
4 = Picture / Tamaño para Combobox / longitud de control
5 = Numero de caracteres maximo (MAXLENGTH) o array en caso de combobox
*/
Function MyInput(cTitle, aControls, Row, Col, aButOKCancelCaptions, lModal)
Local oDlg, i, l
Local ControlRow, e := 0, LN, CN, r, c, nHeight, nWidth, diff, Control
Local nButtonWidth := 100, nLabelWidth := 60, ControlCol, aResult, nSep, nControlWidth

DEFAULT aButOKCancelCaptions TO .T.
DEFAULT lModal TO .F.
//DEFAULT nControlWidth TO 100

* msgbox("Definiendo nControlWidth")
   aControls := AEVAL(aControls, {|a| ASIZE(a, 5)})
   
   nControlWidth := 100
   l := Len(aControls)
   For i := 1 TO l
      If EMPTY(aControls[i][1])
         If !(aControls[i][3] = NIL)
            aControls[i][1] := {"CHECKBOX","DATEPICKER","DATEPICKER","TEXTBOXN","TEXTBOX"}[ AT(VALTYPE(aControls[i][3]), "LDTNC") ]
         Endif
      Endif 
   Next i
   
   aResult := Array( l )

   For i := 1 to l
      If (UPPER(aControls[i][1]) == "COMBOBOX" .or. UPPER(aControls[i][1]) == "CHECKLIST") .and. aControls[i][4] <> NIL
         nControlWidth := MAX( aControls[i][4], nControlWidth)
      ElseIf hb_IsNumeric(aControls[i,4] )
         nControlWidth := MAX(nControlWidth, aControls[i][4]*5)
      Endif
      If UPPER(aControls[i][1]) = "CHECKLIST" //.or. (aControls[i][5] <> NIL .and. if(hb_IsArray(aControls[i][5]), .F., aControls[1,5] > 32) )
         e := e+1
     Elseif VALTYPE(aControls[i][4]) = "N"
         nControlWidth := MAX(nControlWidth, aControls[i][4])
   Endif
      nLabelWidth := MAX(LEN(aControls[i][2])*6, nLabelWidth)
   Next i

   If ! HB_IsNumeric( row ) .or. ! HB_IsNumeric( col )
      r := 0
      c := 0
   Else
      r := row
      c := col
   EndIf

   nHeight := ( l * 30 ) + If(aButOKCancelCaptions, 90,0) + ( e * 60 ) + 20

   If r + nHeight > GetDeskTopHeight()
      diff :=  r + nHeight - GetDeskTopHeight()
      r := r - diff
   EndIf

   nWidth := Max( nLabelWidth + nControlWidth + 30, If(aButOKCancelCaptions, nButtonWidth * 2 + 30, 0) )

   ControlCol := nLabelWidth + 15
If lModal
   DEFINE WINDOW TEMPLATE OBJ oDlg ;
      AT r,c ;
      CLIENTAREA ;
      WIDTH nWidth ;
      HEIGHT nHeight ;  //( l * 30 ) + If(aButOKCancelCaptions, 90,0) + ( e * 60 ) + 20 ;
      TITLE cTitle ;
      MODAL ;
      NOSIZE ;
      BACKCOLOR ( GetFormObjectByHandle( GetActiveWindow() ):BackColor )
Else
   DEFINE WINDOW TEMPLATE OBJ oDlg ;
      AT r,c ;
      CLIENTAREA ;
      WIDTH nWidth ;
      HEIGHT nHeight ;  //( l * 30 ) + If(aButOKCancelCaptions, 90,0) + ( e * 60 ) + 20 ;
      TITLE cTitle ;
;//      MODAL ;
      NOSIZE ;
      BACKCOLOR ( GetFormObjectByHandle( GetActiveWindow() ):BackColor )
Endif
      ON KEY ESCAPE ACTION {|| aResult[1] := NIL, oDlg:Release() }

      ControlRow :=  10

      For i := 1 to l
         LN := 'Label_' + Alltrim( Str( i, 2, 0 ) )
         CN := 'Control_' + Alltrim( Str( i, 2, 0 ) )
         Control := UPPER(aControls[i][1])

         @ ControlRow+4, 10 LABEL (LN) VALUE aControls[i][2] WIDTH nLabelWidth NOWORDWRAP // RIGHTALIGN

         Do Case
         Case Control = "CHECKBOX"
            @ ControlRow, ControlCol CHECKBOX (CN) CAPTION '' VALUE aControls[i][3] WIDTH nControlWidth

         Case Control = "DATEPICKER"
            If EMPTY( aControls[i][3] )
               @ ControlRow, ControlCol+nControlWidth-110 DATEPICKER (CN) VALUE aControls[i][3] WIDTH 110 SHOWNONE
            Else
               @ ControlRow, ControlCol+nControlWidth-100 DATEPICKER (CN) VALUE aControls[i][3] WIDTH 100   //nControlWidth
            EndIf

         Case Control = "COMBOBOXW"    // combo creado de una dbf {WorkArea and Field to display, Field to return}
            @ ControlRow, ControlCol COMBOBOX (CN) INCREMENTAL; 
               ITEMSOURCE (aControls[i][5][1]) ;
               VALUESOURCE (aControls[i][5][2]) ;
               WIDTH nControlWidth  FONT 'Arial' SIZE 10 SEARCHLAPSE 1800

         Case Control = "COMBOBOX"
            If LEN(aControls[i][5]) > 1 .and. HB_ISARRAY(aControls[i][5][2]) //.and. HB_ISARRAY(aControls[i][3][2])
               @ ControlRow, ControlCol COMBOBOX (CN) INCREMENTAL ;
                  ITEMS aControls[i][5][1] VALUESOURCE (aControls[i][5][2]);
                  VALUE aControls[i][3] WIDTH nControlWidth  FONT 'Arial' SIZE 10 SEARCHLAPSE 1800
            Else
               @ ControlRow, ControlCol COMBOBOX (CN) INCREMENTAL ITEMS aControls[i][5] ;
                  VALUE aControls[i][3] WIDTH nControlWidth  FONT 'Arial' SIZE 10 SEARCHLAPSE 1800
            Endif

         Case Control = "CHECKLIST"
               @ ControlRow, ControlCol CHECKLIST (CN) ITEMS aControls[i][5] VALUE aControls[i,3];
                  WIDTH nControlWidth  FONT 'Arial' SIZE 10
               ControlRow += ATAIL(oDlg:aControls):Height -32
               
         Case Control = "TEXTBOXN"
            @ ControlRow, ControlCol+nControlWidth-100 TEXTBOX (CN) VALUE aControls[i][3] WIDTH 100 FONT 'Arial' SIZE 10 NUMERIC INPUTMASK aControls[i][4]

         Case Control = "TEXTBOX"
            If VALTYPE(aControls[i][4]) = "C" .and. At( '9', aControls[i][4] ) > 0
//             @ ControlRow, ControlCol+nControlWidth-100 TEXTBOX (CN) VALUE aControls[i][3] WIDTH 100 FONT 'Arial' SIZE 10 NUMERIC INPUTMASK aControls[i][4]
               @ ControlRow, ControlCol TEXTBOX (CN) VALUE aControls[i][3] WIDTH nControlWidth FONT 'Arial' SIZE 10 NUMERIC INPUTMASK aControls[i][4]
            
            ElseIf VALTYPE(aControls[i][5]) = "N"  // MAXLENGTH en aControls[i][4]
               @ ControlRow, ControlCol ; //+ If(VALTYPE(aControls[i][4]) = "N", nControlWidth-aControls[i][4], 0);
                  TEXTBOX (CN) WIDTH nControlWidth ; //If(VALTYPE(aControls[i][4]) = "N", aControls[i][4], nControlWidth);
                  VALUE aControls[i][3] FONT 'Arial' SIZE 10 MAXLENGTH aControls[i][5]
            Else
               @ ControlRow, ControlCol + If(VALTYPE(aControls[i][4]) = "N", nControlWidth-aControls[i][4], 0);
                  TEXTBOX (CN) WIDTH If(VALTYPE(aControls[i][4]) = "N", aControls[i][4], nControlWidth);
                  VALUE aControls[i][3] FONT 'Arial' SIZE 10 
            EndIf

         Case Control = "EDITBOX"
            @ ControlRow, ControlCol EDITBOX (CN) WIDTH nControlWidth HEIGHT 90 VALUE aControls[i][3] FONT 'Arial' SIZE 10 MAXLENGTH aControls[i][5]
            ControlRow += 64

         EndCase
         ControlRow += 30
      Next i

      nSep := int( ( nWidth - 2 * nButtonWidth ) / 3 )

      if aButOKCancelCaptions
         @ ControlRow + 10, nSep BUTTON BUTTON_1 ;
            WIDTH nButtonWidth ;
            CAPTION _OOHG_Messages( 1, 6 ) ;
            ACTION {|| AEVAL(aResult, {|x,n| aResult[ n ] := oDlg:Control( 'Control_' + Alltrim( Str( n ) ) ):Value, x }), oDlg:Release() }

         @ ControlRow + 10, ( nWidth - nSep - nButtonWidth) BUTTON BUTTON_2 ;
            WIDTH nButtonWidth ;
            CAPTION _OOHG_Messages( 1, 7 ) ;
            ACTION {|| aResult[1] := NIL, oDlg:Release() }

         oDlg:ClientHeight := ControlRow + 10 + oDlg:BUTTON_1:Height + 10
      Else
         ATAIL(oDlg:aControls):OnEnter := {|| AEVAL(aResult, {|x,n| aResult[ n ] := oDlg:Control( 'Control_' + Alltrim( Str( n ) ) ):Value, x }), oDlg:Release() }
      endif

//      oDlg:Control_1:SetFocus()
   END WINDOW

   If ! HB_IsNumeric( row ) .or. ! HB_IsNumeric( col )
      oDlg:Center()
   EndIf

   oDlg:Activate()

Return aResult

Procedure WaitWindow(cMessage, nSec)
   IF oWait = NIL
      DEFINE WINDOW _WaitWindow OBJ oWait AT 0,0 WIDTH 500 HEIGHT 60 ;
          NOSIZE NOMAXIMIZE NOSYSMENU //MODAL
         @ 5, 20 LABEL Msg WIDTH 480 HEIGHT 25 VALUE cMessage CENTERALIGN
         If nSec <> NIL
            DEFINE TIMER Det INTERVAL nSec * 1000 ACTION {|| CloseWait() }
         Endif
      END WINDOW
      oWait:Center()
      If nSec <> NIL
         oWait:Activate()
      Else
         oWait:Activate(.T.)
      Endif
   Else
      oWait:Msg:Value := cMessage
      _processmess()
   ENDIF
Return

Procedure WaitMessage(cMessage, nSec)
DEFAULT nSec TO 0
   IF HB_ISOBJECT(oWait)
      oWait:Msg:Value := cMessage
      _processmess()
      If nSec > 0
         Sleep(nSec)
      Endif
   ENDIF
Return 

Procedure CloseWait(nSec)
DEFAULT nSec TO 0
   IF HB_ISOBJECT(oWait)
      If nSec > 0
         Sleep(nSec)
      Endif
      oWait:Release()
   ENDIF
   oWait := Nil
Return 

Procedure Actualizar()
Local cProg, cPath, cTemp
If IsConnected()
   // Verificar si existe actualización
   HB_FNameSplit(Exename(), @cPath, @cProg)
   If DownLoad("http://compuartmx.com/descargas/"+cProg+"v.ch", cPath+cProg+"1.ch", .F. )
      cTemp := Left(Right(Memoread(cPath+cProg+"1.ch"),19),17)
      FERASE(cPath+cProg+"1.ch")
      If VAL(cTemp) > VAL(_HBMK_BUILD_TIMESTAMP_)
         If MsgYesNo("   Existe una nueva versión de dSgn.exe   "+hb_eol()+;
                     "               ¿Desea descargar?          ",;
                     "Versión: "+TRANSFORM(cTemp, "@R 9999/99/99 99:99:99"))
            If Download("http://compuartmx.com/descargas/"+cProg+"Setup.exe", cPath+cProg+"Setup.exe", .T., 400000000)
               If HB_OpenProcess(cPath+cProg+"Setup.exe") = -1
                  WaitWindow("Error en la instalación. Error "+STR(FError()), 2 )
               Else
                  WaitWindow( "La instalación comenzará en un momento...", 1 )
                  Quit
               Endif
            Endif
        Endif    
      Endif
   Endif
Endif
Return

FUNCTION IsConnected( nTimeout )
LOCAL aAddr := hb_socketResolveINetAddr( "www.google.com", 80 )

RETURN !Empty( aAddr ) .and. hb_socketConnect( hb_socketOpen(), ;
															  aAddr, ;
															  hb_defaultValue( nTimeout, 2000 ) )

FUNCTION Download(cUrl, cFile, lGadge)
LOCAL oUrl, oHTTP, lRetVal //, oP
DEFAULT cFile TO ATAIL(hb_aTokens(cUrl,"/"))
   oUrl := TUrl():New( cUrl )
   oHTTP := TipClientHTTP():New( oUrl )
   IF ( lRetVal := oHTTP:Open() )
      IF lGadge
         oHTTP:exGauge := {|a| WaitWindow("Descargando "+TRANS(a, "9,999,999,999")+" kb"), .T.}  // requiere regresar .T. para continuar descargando
      ENDIF
      lRetVal := oHTTP:ReadToFile( cFile )
      IF lGadge
         Closewait()
      ENDIF
      oHTTP:Close()
   Else
*     msgbox("Código de error: "+str(oHTTP:lastErrorCode)+CRLF+oHTTP:lastErrorMessage(oHTTP:SocketCon), "Error en descarga")
   ENDIF
RETURN lRetVal

#pragma BEGINDUMP
#include "oohg.h"
#include "hbapi.h"
#include "hbapiitm.h"
#include "hbvm.h"
#include "hbstack.h"
#include <windows.h>

//#define HB_STORNL( n, x, y )   hb_stornl( n, x, y )

HB_FUNC(DRAWFOCUSRECT)		// hDC, aRect  Draws pointed rectangle to see what you are selecting
{
   HDC hdc = ( HDC ) HB_PARNL( 1 );
   PHB_ITEM pArrayRect = hb_param( 2, HB_IT_ARRAY );
   RECT rect;

   rect.top    = hb_arrayGetNL( pArrayRect, 1 );
   rect.left   = hb_arrayGetNL( pArrayRect, 2 );
   rect.bottom = hb_arrayGetNL( pArrayRect, 3 );
   rect.right  = hb_arrayGetNL( pArrayRect, 4 );
	hb_retnl( DrawFocusRect( hdc, &rect ) );
}

HB_FUNC(FINDWINDOW)
{
   HWNDret( FindWindowA(hb_parc( 1 ), NULL) );
}

HB_FUNC( GETBKCOLOR )   //hdc
{
   HDC hdc = ( HDC ) HB_PARNL( 1 );

   hb_retnl( ( LONG ) GetBkColor( hdc ) );
}

HB_FUNC( VALIDATERECT )    // ( hWnd, [ { x_left, y_top, x_right, y_bottom } ])
{
   RECT     rect;
   PHB_ITEM pArrayRect;

   if( ! HB_ISARRAY( 2 ) )
       hb_retl( ValidateRect( HWNDparam( 1 ), NULL ) );    // Invalidate all client area
   else
   {
      pArrayRect = hb_param( 2, HB_IT_ARRAY );

      if( hb_arrayLen( pArrayRect ) == 4 )
      {
         rect.left   = HB_ARRAYGETNL( pArrayRect, 1 );
         rect.top    = HB_ARRAYGETNL( pArrayRect, 2 );
         rect.right  = HB_ARRAYGETNL( pArrayRect, 3 );
         rect.bottom = HB_ARRAYGETNL( pArrayRect, 4 );
         hb_retl( ValidateRect( HWNDparam( 1 ), &rect ) );    // Invalidate specific rectangle of client area
      }
      else
         hb_retl( FALSE );
   }
}
   
HB_FUNC( INVALIDATERECT )    // ( hWnd, [ { x_left, y_top, x_right, y_bottom } ], lEraseBackground )
{
   RECT     rect;
   PHB_ITEM pArrayRect;

   if( ! HB_ISARRAY( 2 ) )
       hb_retl( InvalidateRect( HWNDparam( 1 ), NULL, hb_parl( 3 ) ) );    // Invalidate all client area
   else
   {
      pArrayRect = hb_param( 2, HB_IT_ARRAY );

      if( hb_arrayLen( pArrayRect ) == 4 )
      {
         rect.left   = HB_ARRAYGETNL( pArrayRect, 1 );
         rect.top    = HB_ARRAYGETNL( pArrayRect, 2 );
         rect.right  = HB_ARRAYGETNL( pArrayRect, 3 );
         rect.bottom = HB_ARRAYGETNL( pArrayRect, 4 );
         hb_retl( InvalidateRect( HWNDparam( 1 ), &rect, hb_parl( 3 ) ) );    // Invalidate specific rectangle of client area
      }
      else
         hb_retl( FALSE );
   }
}

/*
HB_FUNC (CLIENTTOSCREEN)
{
  HWND hWnd = (HWND) hb_parnl (1);
  PHB_ITEM array = hb_param (2, HB_IT_ARRAY);

  POINT pt;

  pt.x = hb_arrayGetNL (array, 1);
  pt.y = hb_arrayGetNL (array, 2);

  BOOL ret = ClientToScreen (hWnd, &pt);

  HB_STORNL (pt.x, 2, 1);
  HB_STORNL (pt.y, 2, 2);

  hb_retl (ret ? TRUE : FALSE);
}

HB_FUNC (SCREENTOCLIENT)
{
  HWND hWnd = (HWND) hb_parnl (1);
  PHB_ITEM array = hb_param (2, HB_IT_ARRAY);

  POINT pt;

  pt.x = hb_arrayGetNL (array, 1);
  pt.y = hb_arrayGetNL (array, 2);

  BOOL ret = ScreenToClient (hWnd, &pt);

  HB_STORNI (pt.x, 2, 1);
  HB_STORNI (pt.y, 2, 2);

  hb_retl (ret ? TRUE : FALSE);
}
*/
#pragma ENDDUMP

Procedure db_text(ctext, cFile)
Local nHandle
If lDist
	Return
Endif
   DEFAULT cFile TO cPath+"Message.txt"
	If !(ValType(cText) $ 'CM')
		cText := hb_dumpVar(cText)
	Endif
   IF FILE(cFile)
      nHandle := FOPEN(cFile, 65)
   ELSE
      nHandle := FCREATE(cFile)
   ENDIF
   IF nHandle < 0
      msgbox("Error abriendo "+cFile)
   ENDIF
   FSEEK(nHandle, 0, 2) //go to end of file
   FWRITE(nHandle, RTRIM(ctext)+hb_eol())
   FCLOSE(nHandle)
Return

FUNCTION db_trace(nStart, nEnd)
LOCAL aStack := ""
   nStart := IF(nStart = NIL, 2, nStart)     // empezamos en 2 para que no salga esta rutina
   nEnd := IF(nEnd = NIL, 999, nEnd)

   DO WHILE .NOT. Empty( ProcName(nStart) ) .and. nEnd >= nStart
      aStack +=  ProcFile(nStart)+"  "+ ProcName(nStart) +"  "+ str( ProcLine(nStart)) +HB_osNewLine()
      nStart ++
   ENDDO
RETURN aStack

FUNCTION My_GridControlObjectByType( oGrid, uValue, uOptions, lMemo )
Local oGridControl := Nil	//, cMask, nPos
DEFAULT lMemo TO .F.

	If !lMemo .AND. EMPTY(uOptions) .AND. Hb_IsArray(uValue)
		lMemo := .T.
	Endif

   Do Case
   Case lMemo
//		METHOD New( cTitle, lCleanCRLF, oGrid, nWidth, nHeight, lSize, lNoHScroll, nTimeOut )
      oGridControl := TGridControlMemo():New( /*Title*/,/*lCleanCRLF*/ , oGrid, 600 /*nWidth*/, /*nHeight*/, /*lSize*/.T., /*lNoHScroll, nTimeOut*/ )
	
	Case hb_IsArray( uOptions ) .and. LEN( uOptions ) > 0 .AND. hb_IsArray(uOptions[1])
// This combo gets 2 arrays in uOptions => [1] is aItems other is aValues
		//New( aItems, oGrid, aValues, cRetValType, lButtons, aImages, lNoModal, nTimeOut ) CLASS TGridControlComboBox              // TODO: Add delayedload
      oGridControl := TGridControlComboBox():New( uOptions[1], oGrid, uOptions[2], VALTYPE(uOptions[2,1]), , , .T. )

	Case hb_IsArray( uOptions ) .and. LEN( uOptions ) > 0 .AND. hb_IsString(uOptions[1])
		//New( aItems, oGrid, aValues, cRetValType, lButtons, aImages, lNoModal, nTimeOut ) CLASS TGridControlComboBox              // TODO: Add delayedload
      oGridControl := TGridControlComboBox():New( uOptions, oGrid, uOptions, "C", , , .T. )

   Case HB_IsNumeric( uValue )
// New( cPicture, cFunction, cType, nOnFocusPos, lButtons, aImages, oGrid, lLikeExcel, cEditKey, lNoModal, lAutoSkip, nTimeOut ) 
		oGridControl := TGridControlTextBox():New( /*cMask*/, , "N", , , , oGrid, .F. )

   Case HB_IsLogical( uValue )
//New( cTrue, cFalse, lButtons, aImages, oGrid, lNoModal, nTimeOut )
      oGridControl := TGridControlLComboBox():New( ".T.", ".F.", , , oGrid, .T. )

   Case HB_IsDate( uValue )
      oGridControl := TGridControlTextBox():New( "@D", , "D", , , , oGrid )

//   Case ValType( uValue ) == "C"
	Otherwise
	// Se puede usar este control para dar la opción de editar en un EditBox
// TGridControlTextBoxAction:
//        New( cPicture, cFunction, cType, nOnFocusPos, aImages, oGrid, lLikeExcel, cEditKey, lNoModal, bAction, bAction2, lAutoSkip, nTimeOut ) CLASS TGridControlTextBoxAction
*      oGridControl := TGridControlTextBoxAction():New( , , "C", -2, {'IMG_3DOTS'}, oGrid, .T., , .T., {|| msgbox("Action")} )

// METHOD New( cPicture, cFunction, cType, nOnFocusPos, lButtons, aImages, oGrid, lLikeExcel, cEditKey, lNoModal, lAutoSkip, nTimeOut ) CLASS TGridControlTextBox
      oGridControl := TGridControlTextBox():New( , , "C",-2, /*.T.*/, , oGrid, .F., , .T. )
//   OtherWise
      // Unimplemented data type!!!
   EndCase

   Return oGridControl
