HOla amigos, Alguien logró que TCalex trabaje con MariaDb ? Gracias
HOla amigos, Alguien logró que TCalex trabaje con MariaDb ? Gracias
Estimado Willi,
Aqui lo tienes funcionando. Añadido a FWH 26.05 :idea:
https://fivetechsoft.github.io/FWH_docs/en/getting-started/whatsnew.html?v=3#new-samples
calexmaria.prg
// TCalex + MySQL/MariaDB sample
// Month, Week, Day views — full CRUD against MySQL or MariaDB backend
#include "fivewin.ch"
#include "calex.ch"
// Resource IDs — must match calexmaria.rc
#define ID_DLG_DATE 101
#define ID_DLG_HSTART 102
#define ID_DLG_MSTART 103
#define ID_DLG_HEND 104
#define ID_DLG_MEND 105
#define ID_DLG_SUBJ 106
#define ID_DLG_NOTES 107
#define ID_DLG_STATUS 108
REQUEST HB_LANG_ES
REQUEST HB_CODEPAGE_ESWIN
// --- Connection settings (docker-compose defaults) ---------------------------
#define CONN_HOST "127.0.0.1"
#define CONN_DATABASE "fwh"
#define CONN_USER "root"
#define CONN_PASSWORD ""
#define CONN_PORT 3306
// MySQL vs MariaDB: maria_Connect() works with BOTH MySQL and MariaDB servers.
// The MariaDB C connector (libmariadb/libmysql) is wire-protocol compatible.
// Just set MYSQL_* env vars or edit constants above — same function handles both.
//----------------------------------------------------------------------------//
function Main()
local oWnd
local oBrush
HB_CDPSELECT( "ESWIN" )
Hb_LangSelect( "ES" )
SET DATE FORMAT "DD/MM/YYYY"
SET CENTURY ON
DEFINE BRUSH oBrush COLOR CLR_HGRAY
DEFINE WINDOW oWnd TITLE "TCalex + MySQL/MariaDB" BRUSH oBrush MDI
ACTIVATE WINDOW oWnd MAXIMIZED ;
ON INIT CalexMaria():New( oWnd )
oBrush:End()
return nil
//----------------------------------------------------------------------------//
CLASS CalexMaria
DATA oCn // MariaDB connection
DATA oCalex
DATA oWnd
DATA cTable INIT "calex_appointments"
DATA oDrag // Appointment being moved (TCalInfo object)
METHOD New( oWnd )
METHOD ConnectDB()
METHOD CreateTable()
METHOD LoadFromDB( dStart, dEnd, nHourStart, nHourEnd )
METHOD AddAppt( oView, dDateFrom, dDateTo, nTimeFrom, nTimeTo )
METHOD DelAppt( oView, nIdx )
METHOD BuildDialog( oView, dDateFrom, dDateTo, nTimeFrom, nTimeTo, lNew )
METHOD DropAppt( oView, dDateTo, nTimeFrom, nTimeTo )
METHOD OnRightClick( nRow, nCol, oCalex )
ENDCLASS
//----------------------------------------------------------------------------//
METHOD New( oWnd ) CLASS CalexMaria
local oSelf := Self
::oWnd := oWnd
if ! ::ConnectDB()
return nil
endif
::CreateTable()
DEFINE WINDOW ::oWnd MDICHILD OF oWnd ;
TITLE "TCalex + MariaDB - Appointments" VSCROLL
DEFINE CALEX ::oCalex OF ::oWnd ;
FROM 1, 10 TO 600, 600 ;
FIRST_DATE 0 ALL
// Month view
DEFINE MONTH VIEW OF ::oCalex ;
ON SELECT VIEW ( oSelf:LoadFromDB( ::dStart, ::dEnd, ::nStartHour, ::nEndHour ) ) ;
ON SELECT DAY oSelf:AddAppt( Self, dDate, dDate, 800, 900 ) ;
ACTIVATE
// Day view
DEFINE DAY VIEW OF ::oCalex ;
INTERVAL 30 ;
START HOUR 8 ;
END HOUR 20 ;
ON SELECT VIEW ( oSelf:LoadFromDB( ::dStart, ::dEnd, ::nStartHour, ::nEndHour ) ) ;
ON LEFT CLICK oSelf:AddAppt( Self, dDateFrom, dDateTo, nTimeFrom, nTimeTo ) ;
ON DELETE oSelf:DelAppt( Self, nIdx ) ;
ON NEXT ( oSelf:LoadFromDB( ::dStart, ::dEnd, ::nStartHour, ::nEndHour ) ) ;
ON PREV ( oSelf:LoadFromDB( ::dStart, ::dEnd, ::nStartHour, ::nEndHour ) )
// Week view
DEFINE WEEK VIEW OF ::oCalex ;
INTERVAL 30 ;
START HOUR 8 ;
END HOUR 20 ;
ON SELECT VIEW ( oSelf:LoadFromDB( ::dStart, ::dEnd, ::nStartHour, ::nEndHour ) ) ;
ON LEFT CLICK oSelf:AddAppt( Self, dDateFrom, dDateTo, nTimeFrom, nTimeTo ) ;
ON DELETE oSelf:DelAppt( Self, nIdx ) ;
ON NEXT ( oSelf:LoadFromDB( ::dStart, ::dEnd, ::nStartHour, ::nEndHour ) ) ;
ON PREV ( oSelf:LoadFromDB( ::dStart, ::dEnd, ::nStartHour, ::nEndHour ) )
// bRClicked fires on WM_RBUTTONDOWN (TCalEx does NOT override RButtonDown)
::oCalex:bRClicked := { | nRow, nCol, nFlags, oCalex | oSelf:OnRightClick( nRow, nCol, oCalex ) }
ACTIVATE WINDOW ::oWnd MAXIMIZED ;
VALID ( ::oCn:Close(), .T. )
return Self
//----------------------------------------------------------------------------//
METHOD ConnectDB() CLASS CalexMaria
local cHost := GetEnv( "MYSQL_HOST" )
local cDatabase := GetEnv( "MYSQL_DATABASE" )
local cUser := GetEnv( "MYSQL_USER" )
local cPassword := GetEnv( "MYSQL_PASSWORD" )
local nPort := Val( GetEnv( "MYSQL_PORT" ) )
// Env vars override constants
if Empty( cHost ); cHost := CONN_HOST ; endif
if Empty( cDatabase ); cDatabase := CONN_DATABASE ; endif
if Empty( cUser ); cUser := CONN_USER ; endif
if Empty( cPassword ); cPassword := CONN_PASSWORD ; endif
if nPort == 0 ; nPort := CONN_PORT ; endif
? "Connecting to:", cHost + ":" + AllTrim( Str( nPort ) ), "/", cDatabase, "as", cUser
// maria_Connect() works with BOTH MySQL and MariaDB servers.
// Signature: maria_Connect( cHost, cDB, cUser, cPassword, nPort )
::oCn := maria_Connect( cHost, cDatabase, cUser, cPassword, nPort )
if ::oCn == nil
? "ERROR: Cannot connect."
? "Check: docker-compose up -d (from calexmaria\ folder)"
? "Or set MYSQL_HOST / MYSQL_DATABASE / MYSQL_USER / MYSQL_PASSWORD / MYSQL_PORT"
return .F.
endif
? "Connected:", ::oCn:cDB, "| Server:", ::oCn:cServerInfo
return .T.
//----------------------------------------------------------------------------//
METHOD CreateTable() CLASS CalexMaria
local cSql
TEXT INTO cSql
CREATE TABLE IF NOT EXISTS calex_appointments (
id INT AUTO_INCREMENT PRIMARY KEY,
dstart DATE NOT NULL,
dend DATE,
nstart INT NOT NULL DEFAULT 0 COMMENT 'HHMM military time',
nend INT NOT NULL DEFAULT 0,
subject VARCHAR(200),
text TEXT,
nstatus INT DEFAULT 1 COMMENT '1=green 2=blue 3=yellow 4=red 5=gray',
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_dstart (dstart),
INDEX idx_status (nstatus)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
ENDTEXT
::oCn:Execute( cSql )
? "Table ready:", ::cTable
return nil
//----------------------------------------------------------------------------//
METHOD LoadFromDB( dStart, dEnd, nHourStart, nHourEnd ) CLASS CalexMaria
local oRs
local aGrad, nClrT
local cSql
DEFAULT dStart := CToD( " / / " )
DEFAULT dEnd := CToD( " / / " )
DEFAULT nHourStart := 0
DEFAULT nHourEnd := 24
cSql := "SELECT id,nstart,nend,dstart,dend,subject,text,nstatus " + ;
"FROM " + ::cTable + " " + ;
"WHERE dstart BETWEEN ? AND ? ORDER BY dstart, nstart"
oRs := ::oCn:RowSet( cSql, { dStart, dEnd } )
// Fallback: if no scope set, load all
if oRs == nil .or. ( ValType( dStart ) != "D" .and. ValType( dEnd ) != "D" )
oRs := ::oCn:RowSet( "SELECT id,nstart,nend,dstart,dend,subject,text,nstatus FROM " + ::cTable + " ORDER BY dstart, nstart" )
endif
::oCalex:Reset()
if oRs != nil .and. oRs:RecCount() > 0
oRs:GoTop()
do while ! oRs:Eof()
aGrad := ColorData( oRs:nstatus )
nClrT := If( oRs:nstatus == 4, CLR_WHITE, CLR_BLACK )
::oCalex:LoadDates( oRs:nstart, oRs:nend, ;
oRs:dstart, oRs:dend, ;
oRs:text, oRs:subject, ;
oRs:id, , , , ;
aGrad, nClrT, oRs:nstatus )
oRs:Skip()
enddo
endif
::oCalex:oView:BuildDates()
::oCalex:Refresh()
return nil
//----------------------------------------------------------------------------//
METHOD AddAppt( oView, dDateFrom, dDateTo, nTimeFrom, nTimeTo ) CLASS CalexMaria
if ValType( dDateFrom ) != "D"
return nil
endif
DEFAULT dDateTo := dDateFrom
DEFAULT nTimeFrom := 800
DEFAULT nTimeTo := 900
LogFile( "calex.log", { "AddAppt", DToC(dDateFrom), If(::oDrag==nil,"no-drag","DRAG:"+::oDrag:cSubject) } )
// If carrying an appointment → drop it on target day
if ::oDrag != nil
::DropAppt( oView, dDateFrom, nTimeFrom, nTimeTo )
return nil
endif
// Nothing being dragged → NEW appointment
::BuildDialog( oView, dDateFrom, dDateTo, nTimeFrom, nTimeTo, .T. )
return nil
//----------------------------------------------------------------------------//
METHOD DelAppt( oView, nIdx ) CLASS CalexMaria
if ! MsgYesNo( "Delete appointment #" + AllTrim( Str( nIdx ) ) + "?" )
return nil
endif
::oCn:Execute( "DELETE FROM " + ::cTable + " WHERE id = ?", { nIdx } )
::LoadFromDB( oView:dStart, oView:dEnd, oView:nStartHour, oView:nEndHour )
return nil
//----------------------------------------------------------------------------//
METHOD DropAppt( oView, dDateTo, nTimeFrom, nTimeTo ) CLASS CalexMaria
local nDays, dEndTo
if ::oDrag == nil
return nil
endif
nDays := dDateTo - ::oDrag:dStart
dEndTo := If( Empty( ::oDrag:dEnd ), dDateTo, ::oDrag:dEnd + nDays )
::oCn:Execute( "UPDATE " + ::cTable + ;
" SET dstart=?, dend=? WHERE id=?", ;
{ ::oDrag:dStart + nDays, dEndTo, ::oDrag:nIdx } )
LogFile( "calex.log", { "Moved", ::oDrag:nIdx, DToC( ::oDrag:dStart ), "->", DToC( dDateTo ) } )
::oDrag := nil
::oCalex:UnSelectCalInfo()
::LoadFromDB( oView:dStart, oView:dEnd, oView:nStartHour, oView:nEndHour )
return nil
//----------------------------------------------------------------------------//
METHOD OnRightClick( nRow, nCol, oCalex ) CLASS CalexMaria
local oView := oCalex:oView
local lSelected := ( ::oCalex:oCalInfo != nil .or. ::oCalex:oCalInfoSelected != nil )
local oMenu, oSelf := Self
local aPos, dDate
LogFile( "calex.log", { "RMenu", oView:ClassName(), lSelected, If(::oCalex:oCalInfo==nil,"oCI=nil","oCI=OK") } )
dDate := ::oCalex:dDateSelected // default
// TMonthView MouseMove doesn't set oCalInfo — find manually
if oView:IsKindOf( "TMONTHVIEW" )
aPos := oView:GetPosition( nRow, nCol )
dDate := oView:GetDateFromPos( aPos[ 1 ], aPos[ 2 ] )
if ::oCalex:oCalInfo == nil .and. hb_HHASKEY( oView:hDays, DToS( dDate ) )
AEval( hb_HGET( oView:hDays, DToS( dDate ) ), { | oCalInfo | ;
If( oCalInfo:aCoords[ CI_TOP ] < nRow .and. oCalInfo:aCoords[ CI_BOTTOM ] > nRow .and. ;
oCalInfo:aCoords[ CI_LEFT ] < nCol .and. oCalInfo:aCoords[ CI_RIGHT ] > nCol, ;
( ::oCalex:oCalInfo := oCalInfo, lSelected := .T. ), ) } )
endif
endif
MENU oMenu POPUP
if ::oDrag != nil
MENUITEM "Drop here" ACTION oSelf:DropAppt( oView, ;
If( oView:IsKindOf( "TMONTHVIEW" ), dDate, ::oCalex:dDateSelected ), 800, 900 )
MENUITEM "Cancel move" ACTION ( ::oDrag := nil, ::oCalex:UnSelectCalInfo(), ::oCalex:Refresh() )
SEPARATOR
endif
if lSelected
MENUITEM "Edit..." ACTION ( ::oCalex:SelectCalInfo(), ;
::BuildDialog( oView, ::oCalex:oCalInfo:dStart, ::oCalex:oCalInfo:dStart, ;
::oCalex:oCalInfo:nStart, ::oCalex:oCalInfo:nEnd, .F. ) )
SEPARATOR
MENUITEM "Pick up (move)" ACTION ( ;
::oDrag := ::oCalex:oCalInfo, ;
::oCalex:UnSelectCalInfo(), ;
LogFile( "calex.log", { "Pickup", If(::oDrag==nil,"FAIL-oCalInfo-nil",Str(::oDrag:nIdx)), ::oDrag:cSubject } ) )
endif
if ! lSelected
MENUITEM "New appointment..." ACTION oSelf:BuildDialog( oView, ;
::oCalex:dDateSelected, ::oCalex:dDateSelected, 800, 900, .T. )
endif
if lSelected
MENUITEM "Delete" ACTION If( MsgYesNo( "Delete this appointment?" ), ;
( oSelf:DelAppt( oView, ::oCalex:oCalInfo:nIdx ), ;
oSelf:LoadFromDB( oView:dStart, oView:dEnd, oView:nStartHour, oView:nEndHour ) ), )
endif
ENDMENU
ACTIVATE POPUP oMenu AT nRow, nCol OF oCalex
return nil
//----------------------------------------------------------------------------//
METHOD BuildDialog( oView, dDateFrom, dDateTo, nTimeFrom, nTimeTo, lNew ) CLASS CalexMaria
local oDlg, oThis := Self
local cSubject := Space( 200 )
local cText := Space( 500 )
local cDate := DToC( dDateFrom )
local lSave := .F.
local nSt := 1
local nHStart, nMStart, nHEnd, nMEnd
local oBtnOk, oBtnCancel, oSayDate
local oGetSubj, oGetNotes, oCbxStatus
local oGetHStart, oGetMStart, oGetHEnd, oGetMEnd
local o
if ! lNew
o := ::oCalex:oCalInfoSelected
cSubject := o:cSubject
cText := o:cText
nSt := o:nStatus
nHStart := Int( o:nStart / 100 )
nMStart := o:nStart - nHStart * 100
nHEnd := Int( o:nEnd / 100 )
nMEnd := o:nEnd - nHEnd * 100
else
nHStart := Int( nTimeFrom / 100 )
nMStart := nTimeFrom - nHStart * 100
nHEnd := Int( nTimeTo / 100 )
nMEnd := nTimeTo - nHEnd * 100
endif
DEFINE DIALOG oDlg RESOURCE "CALEX_DLG" ;
TITLE If( lNew, "New Appointment", "Edit Appointment" )
REDEFINE SAY oSayDate VAR cDate ID ID_DLG_DATE OF oDlg
REDEFINE GET oGetHStart VAR nHStart ID ID_DLG_HSTART OF oDlg PICTURE "99" ;
VALID nHStart >= 0 .and. nHStart <= 23
REDEFINE GET oGetMStart VAR nMStart ID ID_DLG_MSTART OF oDlg PICTURE "99" ;
VALID nMStart >= 0 .and. nMStart <= 59
REDEFINE GET oGetHEnd VAR nHEnd ID ID_DLG_HEND OF oDlg PICTURE "99" ;
VALID nHEnd >= 0 .and. nHEnd <= 23
REDEFINE GET oGetMEnd VAR nMEnd ID ID_DLG_MEND OF oDlg PICTURE "99" ;
VALID nMEnd >= 0 .and. nMEnd <= 59
REDEFINE GET oGetSubj VAR cSubject ID ID_DLG_SUBJ OF oDlg
REDEFINE GET oGetNotes VAR cText ID ID_DLG_NOTES OF oDlg MEMO
REDEFINE COMBOBOX oCbxStatus VAR nSt ID ID_DLG_STATUS OF oDlg ;
ITEMS { "Green", "Blue", "Yellow", "Red", "Gray" }
REDEFINE BUTTON oBtnOk ID IDOK OF oDlg ACTION ( lSave := .T., oDlg:End() )
REDEFINE BUTTON oBtnCancel ID IDCANCEL OF oDlg ACTION oDlg:End()
ACTIVATE DIALOG oDlg CENTERED
if ! lSave
return nil
endif
// Rebuild time values from hh:mm fields
nTimeFrom := nHStart * 100 + nMStart
nTimeTo := nHEnd * 100 + nMEnd
// Trim trailing spaces
cSubject := AllTrim( cSubject )
cText := AllTrim( cText )
if lNew
// INSERT new appointment
::oCn:Execute( "INSERT INTO " + ::cTable + ;
" (dstart, dend, nstart, nend, subject, text, nstatus) VALUES (?,?,?,?,?,?,?)", ;
{ dDateFrom, dDateTo, nTimeFrom, nTimeTo, cSubject, cText, nSt } )
else
// UPDATE existing appointment
::oCn:Execute( "UPDATE " + ::cTable + ;
" SET dstart=?, dend=?, nstart=?, nend=?, subject=?, text=?, nstatus=? WHERE id=?", ;
{ dDateFrom, dDateTo, nTimeFrom, nTimeTo, cSubject, cText, nSt, o:nIdx } )
endif
// Reload data to refresh calendar
::LoadFromDB( oView:dStart, oView:dEnd, oView:nStartHour, oView:nEndHour )
return nil
//----------------------------------------------------------------------------//Gracias Antonio,,,
al compilar sale este error
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbct\hbct.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\xhb\xhb.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbct\hbct.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbtip\hbtip.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbfship\hbfship.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbxpp\hbxpp.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbwin\hbwin.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbziparc\hbziparc.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbmzip\hbmzip.hbc
hbmk2: Processing:
D:\SOFT\FWXH2512\HARBOUR\contrib\hbmzip\3rd\minizip\minizip.hbc
Harbour 3.2.0dev (r2407221137)
Copyright (c) 1999-2021, https://harbour.github.io/
Compiling 'calendar.PRG'...
calendar.PRG(114) Warning W0001 Ambiguous reference 'DDATETO'
400
No code generated.De donde sale ese calendar.prg ?
Hola Antonio,
calendar.prg es el nombre del archivo donde esta contenido el ejemplo que me enviaste---
Ya le puse su nombre < calexmaria.prg >
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbct\hbct.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\xhb\xhb.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbct\hbct.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbtip\hbtip.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbfship\hbfship.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbxpp\hbxpp.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbwin\hbwin.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbziparc\hbziparc.hbc
hbmk2: Processing: D:\SOFT\FWXH2512\HARBOUR\contrib\hbmzip\hbmzip.hbc
hbmk2: Processing:
D:\SOFT\FWXH2512\HARBOUR\contrib\hbmzip\3rd\minizip\minizip.hbc
Harbour 3.2.0dev (r2407221137)
Copyright (c) 1999-2021, https://harbour.github.io/
Compiling 'calexmaria.PRG'...
calexmaria.PRG(114) Warning W0001 Ambiguous reference 'DDATETO'
400
No code generated.Hay que hacer una modificación en calex.ch
calex.ch lÃnea 108 — añadido dDateTo al template de DAY VIEW ON LEFT CLICK. Ahora alineado con WEEK VIEW y con lo que LButtonUpView realmente envÃa.
// Antes (bug): { | nRow, nCol, Self, dDateFrom, nTimeFrom, nTimeTo | <uSelected> }
// Ahora (fix): { | nRow, nCol, Self, dDateFrom, dDateTo, nTimeFrom, nTimeTo | <uSelected> }
GRacias..... funcionando correctamente---