/* * TimeEditMod.prg * Control TTimeEdit moderno * * Copyright 2022 Ignacio Ortiz de Zúñiga * Copyright 2022 https://Xailer.com * All rights reserved * */ #include "Xailer.ch" CLASS XTimeEditMod FROM TEditMod PUBLISHED: PROPERTY nAlignment INIT taRIGHT PROPERTY cMinTime INIT "" PROPERTY cMaxTime INIT "" PROPERTY lMilliseconds INIT .F. WRITE INLINE (::FlMilliseconds := Value, ::SetTimePicture() ) PROPERTY lSeconds INIT .T. WRITE INLINE (::FlSeconds := Value, ::SetTimePicture() ) PUBLIC: METHOD Create( oParent ) CONSTRUCTOR METHOD Free() PROTECTED: PROPERTY cPicture INIT "" PROPERTY nMaxLength INIT 0 PROPERTY lDragAccepFiles PROPERTY lHScroll PROPERTY lTrimSpaces PROPERTY lZeroFill PROPERTY nBmpHeight PROPERTY nBmpWidth PROPERTY nCharcase PROPERTY oBitmaps PROPERTY nStyle INIT esUPDOWN EVENT OnBtnClick( oSender, nX, nY ) DATA oRepeatTimer METHOD RepeatTimer( lMode ) METHOD StartRepeat() RESERVED: METHOD SetValue( Value, lFocused, lWithEvent ) METHOD SetTimePicture() METHOD MoveValue( nDirection ) METHOD WMKeyDown( nKey, nFlags, hWnd ) ENDCLASS //-------------------------------------------------------------------------- METHOD Create( oParent ) CLASS XTimeEditMod UPDATE ::oParent TO oParent IF Empty( ::FcText ) ::FcText := Time() ENDIF ::Super:Create() ::SetTimePicture() ::OnBtnClick := {|o,x,y| ::MoveValue( - ::nClickedPos ) } RETURN Self //-------------------------------------------------------------------------- METHOD Free() CLASS XTimeEditMod IF ::oRepeatTimer != NIL ::oRepeatTimer:End() ENDIF RETURN ::Super:Free() //-------------------------------------------------------------------------- METHOD SetTimePicture() CLASS XTimeEditMod IF ::lMilliseconds ::cPicture := "99:99:99:99" ::nMaxLength := 11 ELSEIF ::lSeconds ::cPicture := "99:99:99" ::nMaxLength := 8 ELSE ::cPicture := "99:99" ::nMaxLength := 5 ENDIF ::SetValue( ::fcText ) RETURN nil //-------------------------------------------------------------------------- METHOD MoveValue( nDirection ) CLASS XTimeEditMod LOCAL nPos, nSecs IF Empty( nDirection ) IF ::oRepeatTimer != NIL ::oRepeatTimer:End() ENDIF RETURN nil ENDIF nPos := ::nPos IF nPos <= 2 nSecs := 3600 ELSEIF nPos <= 5 nSecs := 60 ELSE nSecs := 1 ENDIF nSecs := nSecs * IIF( nDirection >= 0, 1, -1 ) ::SetValue( AddSecs( ::Value, nSecs ),, .T. ) ::SetSel( --nPos, nPos ) RETURN NIL //-------------------------------------------------------------------------- METHOD SetValue( Value, lFocused, lWithEvent ) CLASS XTimeEditMod LOCAL nSecs, nTmp IF ValType( Value ) == "N" Value := SecToTime( Value, ::lMilliseconds ) ENDIF IF !TimeValid( Value ) Value := SecToTime( 0, ::lMilliseconds ) ENDIF nSecs := TimeToSec( Value ) IF TimeValid( ::cMinTime ) nSecs := Max( nSecs, TimeToSec( ::cMinTime ) ) ENDIF IF TimeValid( ::cMaxTime ) nSecs := Min( nSecs, TimeToSec( ::cMaxTime ) ) ENDIF Value := SecToTime( nSecs, ::lMilliseconds ) RETURN ::Super:SetValue( Value, lFocused, NIL, lWithEvent ) //------------------------------------------------------------------------------ METHOD WMKeyDown( nKey, nLParam, hWnd ) CLASS XTimeEditMod LOCAL nSecs ::oForm:SendMsg( WM_CHANGEUISTATE, MakeLong( UIS_CLEAR, UISF_HIDEFOCUS ) ) IF nKey == VK_UP IF lGetKeyState( VK_CONTROL ) // hours nSecs := 3600 ELSEIF lGetKeyState( VK_SHIFT ) // minutes nSecs := 60 ELSE nSecs := 1 ENDIF ::SetValue( AddSecs( ::Value, nSecs ),, .T. ) RETURN 0 ELSEIF nKey == VK_DOWN IF lGetKeyState( VK_CONTROL ) // hours nSecs := 3600 ELSEIF lGetKeyState( VK_SHIFT ) // minutes nSecs := 60 ELSE nSecs := 1 ENDIF ::SetValue( AddSecs( ::Value, - nSecs ),, .T. ) RETURN 0 ENDIF RETURN ::Super:WMKeyDown( nKey, nLParam, hWnd ) //---------------------------------------------------------------------------- METHOD RepeatTimer( lMode ) CLASS XTimeEditMod IF ::oRepeatTimer != NIL ::oRepeatTimer:End() ::oRepeatTimer := NIL ENDIF IF lMode ::oRepeatTimer := TTimer():Create( Self, 500, {|o| ::StartRepeat() } ) ::oRepeatTimer:Enable() ENDIF RETURN nil //---------------------------------------------------------------------------- METHOD StartRepeat() CLASS XTimeEditMod ::oRepeatTimer:End() ::oRepeatTimer := TTimer():Create( Self, 100,; {|o| ::MoveValue( - ::nClickedPos ) } ) ::oRepeatTimer:Enable() RETURN nil //---------------------------------------------------------------------------- STATIC FUNCTION AddSecs( cTime, nSecs ) LOCAL nTime := TimeToSec( cTime ) + nSecs IF nTime < 0 nTime += 86400 ENDIF RETURN SecToTime( nTime ) //---------------------------------------------------------------------------// STATIC FUNCTION TimeToSec( cTime ) // Borrowed from Harbour Tools LOCAL nSec := 0 LOCAL nLen, i, aLim, aMod, nInd, n IF cTime == NIL nSec := Seconds() ELSEIF HB_ISSTRING( cTime ) nLen := Len( cTime ) IF ( nLen + 1 ) % 3 == 0 .AND. nLen <= 11 nInd := 1 aLim := { 24, 60, 60, 100 } aMod := { 3600, 60, 1, 1 / 100 } FOR i := 1 TO nLen STEP 3 IF IsDigit( SubStr( cTime, i , 1 ) ) .AND. ; IsDigit( SubStr( cTime, i + 1, 1 ) ) .AND. ; ( i == nLen - 1 .OR. SubStr( cTime, i + 2, 1 ) == ":" ) .AND. ; ( n := Val( SubStr( cTime, i, 2 ) ) ) < aLim[ nInd ] nSec += n * aMod[ nInd ] ELSE nSec := 0 EXIT ENDIF ++nInd NEXT ENDIF ENDIF RETURN Round( nSec, 2 ) /* round FL val to be sure that you can compare it */ //---------------------------------------------------------------------------// STATIC FUNCTION SecToTime( nSec, lHundredth ) // Borrowed from Harbour Tools LOCAL i LOCAL n := iif( HB_ISNUMERIC( nSec ), nSec, Seconds() ) LOCAL h := iif( hb_defaultValue( lHundredth, .F. ), StrZero( ( n * 100 ) % 100, 2 ), "" ) n := Int( n % 86400 ) FOR i := 1 TO 3 h := StrZero( n % 60, 2 ) + iif( h == "", "", ":" + h ) n := Int( n / 60 ) NEXT RETURN h //---------------------------------------------------------------------------// STATIC FUNCTION TimeValid( cTime ) LOCAL cMax, cChar LOCAL nLen, nPos LOCAL lRet IF ValType( cTime ) != "C" RETURN .F. ENDIF cMax := "29:59:59:99" nLen := Len( cTime ) lRet := .F. IF Val( Left( cTime, 2 ) ) < 24 .AND. ; ( nLen == 2 .OR. nLen == 5 .OR. nLen == 8 .OR. nLen == 11 ) lRet := .T. FOR EACH cChar IN cTime nPos := cChar:__EnumIndex() IF nPos % 3 == 0 IF cChar != ":" lRet := .F. EXIT ENDIF ELSEIF !IsDigit( cChar ) .OR. cChar > SubStr( cMax, nPos, 1 ) lRet := .F. EXIT ENDIF NEXT ENDIF RETURN lRet