File "nimoworld.bas"

Path: /nimoworld/nimoworld.bas
File size: 40.35 KB
MIME-type:
Charset: 8 bit


#COMPILE EXE "nimoworld.exe"
#DIM ALL

$EXE = "nimo world"
$VERSION = "1.0 (10-SEPT-2018)"
%ALLOW_ONLY_ONE_INSTANCE = 1

#RESOURCE "res\nimoworld.pbr"

' TODO:  [X] Appearance Manager + include settings during import
'        [X] Option to reset appearance + settings
'        [X] Globalize CfgFile
'        [X] Appearance by local script ...rely on IF Exist(local) THEN Copy(LocalAppData)
'        [X] Config by local script -> fix regression
'        [X] Share customized widget! :o))
'        [X] Pack & unpack customized widget
'        [X] Position each font/color etc. dialog as centered on the widget
'        [X] Packaged INI: reset X,Y
'        [X] Admin box
'        [X] "Share this widget" in context menu instead of CustoManager dialog
'        [X] Add a talk about Widget Share in About()
'        [X] Add a talk about Admin Box in About()
'        [X] Launch this widget when Windows starts?
'        [X] Checkbox 'Activate debug logs' in AdminBox
'        [X] Checkbox 'Run at Windows startup' in AdminBox
'        [X] Localization at first run
'        [X] Delete custo by hitting Del in Custo Manager
'        [O] Save font in custom widget + install it if not present

'--------------------------------------------------------------------------------
'   ** Includes **
'--------------------------------------------------------------------------------
#INCLUDE ONCE "inc\ContextMenu.inc"
#INCLUDE ONCE "inc\GdiPlus.inc"
#INCLUDE ONCE "inc\RTF.inc"
#INCLUDE ONCE "inc\ToolTip.inc"
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
'   ** Constants **
'--------------------------------------------------------------------------------
%ID_TIMER1      = %WM_USER + 400
%IDC_DUMMY      = 1000
%IDC_TIME_GFX   = 1001
%IDC_IMAGE_GFX  = 1002
%IDC_DAY_GFX    = 1003
'--------------------------------------------------------------------------------
%IDC_LBLH       =  998
%IDC_LBLV       =  999
%IDC_OPTION1    = 1001
%IDC_OPTION2    = 1002
%IDC_OPTION3    = 1003
%IDC_OPTION4    = 1004
%IDC_OPTION5    = 1005
%IDC_OPTION6    = 1006
%IDC_CHK_RSZ    = 1007
%IDC_OPT_HOR    = 1008
%IDC_OPT_VER    = 1009
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
'   ** Global variables **
'--------------------------------------------------------------------------------
GLOBAL ClockDescriptor AS DialogDescriptor
GLOBAL hDib AS DWORD                    ' handle to GDI+ image
GLOBAL PicPos AS LONG                   ' position of pic: -1=left/top ; +1=right/bottom
GLOBAL timediff AS LONG                 ' time difference, in minutes
GLOBAL tFont, tFont2 AS DWORD           ' fonts
GLOBAL PicRsz, PicHor, PicAli AS LONG   ' app settings
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB ReadSettings    ' from 'CfgFile' (defined in ContextMenu.inc)
    LOCAL a AS ASCIIZ * %MAX_PATH
    GetPrivateProfileString $EXE, "PicRsz", "1", a, %MAX_PATH, CfgFile : PicRsz = VAL(a)
    GetPrivateProfileString $EXE, "PicHor", "1", a, %MAX_PATH, CfgFile : PicHor = VAL(a)
    GetPrivateProfileString $EXE, "PicAli", "0", a, %MAX_PATH, CfgFile : PicAli = VAL(a)
END SUB
'--------------------------------------------------------------------------------
SUB WriteSettings   ' to 'CfgFile' (defined in ContextMenu.inc)
    WritePrivateProfileString $EXE, "PicRsz", STR$(PicRsz), CfgFile
    WritePrivateProfileString $EXE, "PicHor", STR$(PicHor), CfgFile
    WritePrivateProfileString $EXE, "PicAli", STR$(PicAli), CfgFile
END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
FUNCTION GetCurTime() AS STRING
' Return time in the form 'HH:MM*' taking into account 'timediff' (in minutes)
'       e.g. "09:26", "13:01+", "00:00-"
' "+" means day after today ; "-" means day before today
    LOCAL t AS STRING
    LOCAL hour, d_hour, minute, d_minute AS LONG
    d_minute = timediff MOD 60
    d_hour = (timediff - d_minute) \ 60
    t = LEFT$(TIME$,5)
    hour = VAL(LEFT$(t,2))
    minute = VAL(MID$(t,4,2)) + d_minute
    IF minute < 0  THEN hour-=1 : minute+=60
    IF minute >= 60 THEN hour+=1 : minute-=60
    t = ":" + FORMAT$(minute, "00")
    hour += d_hour
    IF hour < 0  THEN hour+=24 : t +="-" ' day before
    IF hour > 23 THEN hour-=24 : t +="+" ' day after
    FUNCTION = FORMAT$(hour, "00") + t
END FUNCTION
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
MACRO GetCurrentTimAndDay
' Same as above split into 'tim' ("HH:MM") and 'day' ("" or "+1" or "-1")
    LOCAL tim, day AS STRING
    tim = GetCurTime()
    IF RIGHT$(tim,1) = "+" OR RIGHT$(tim,1) = "-" THEN day = RIGHT$(tim,1) + "1" : tim = LEFT$(tim, -1)
END MACRO
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB SetToolTip(hDlg AS LONG)
  CALL tooltip_settooltip (GetDlgItem(hDlg, %IDC_IMAGE_GFX), IIF$(LNG="FR", _
       "Maintenez dplacez l'image pour bouger la fentre" + $CR + _
       "Cliquez droit pour ouvrir le menu d'option" + $CR + _
       "Faites menu >  propos pour les options de personnalisation" _
       , _
       "Drag and drop the picture to move the window" + $CR + _
       "Simple right click to open contextual menu" + $CR + _
       "Do menu > about to learn about customization options" _
       ))
END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB ChangeLanguage(BYVAL DiDe AS DialogDescriptor POINTER)
  IF @DiDe.Handler = ClockDescriptor.Handler THEN ' Treat differently for each Dialog having a Context Menu
    SetToolTip @DiDe.Handler
  END IF
END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB GetDimensions (BYVAL hDlg AS DWORD, BYVAL tx AS STRING, BYVAL hFont AS DWORD, _ ' input parameters
                   BYREF w AS LONG, BYREF h AS LONG, BYREF y0 AS LONG)              ' output parameters
    LOCAL th, x, y, Px AS LONG
    CONTROL ADD GRAPHIC, hDlg, 999, "", -1024, -1024, 100, 100
    GRAPHIC ATTACH hDlg, 999
    GRAPHIC SET FONT hFont
    GRAPHIC TEXT SIZE tx TO w, th ' only useful for w (text width), th (text height) is not consistant!!
    CONTROL KILL hDlg, 999
    CONTROL ADD GRAPHIC, hDlg, 999, "", -1024-w, -1024-th, w, th
    GRAPHIC ATTACH hDlg, 999
    GRAPHIC COLOR %BLACK, %WHITE
    GRAPHIC CLEAR
    GRAPHIC SET FONT hFont
    GRAPHIC PRINT tx
    ' So we scan the graphic control for upper and botommer pixels
    FOR y = 0 TO th-1
      FOR x = 0 TO w-1
        GRAPHIC GET PIXEL (x, y) TO Px
        IF Px <> %WHITE THEN EXIT FOR
      NEXT x
      IF Px <> %WHITE THEN EXIT FOR
    NEXT y
    y0 = 1 - y ' Y-offset is set
    FOR y = th-1 TO 0 STEP -1
      FOR x = 0 TO w-1
        GRAPHIC GET PIXEL (x, y) TO Px
        IF Px <> %WHITE THEN EXIT FOR
      NEXT x
      IF Px <> %WHITE THEN EXIT FOR
    NEXT y
    h = y + 1 + y0 ' correct height is set
    CONTROL KILL hDlg, 999
END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB RefreshDialog(BYVAL DiDe AS DialogDescriptor POINTER)
  LOCAL iw, ih, ix0, iy0 AS LONG ' image width, height and X,Y-offset
  LOCAL tw, th, tx0, ty0 AS LONG ' same for time
  LOCAL ow, oh, ox0, oy0 AS LONG ' same for day-offset (+/-1)
  LOCAL dX, dY AS LONG ' desktop offset

  IF @DiDe.Handler = ClockDescriptor.Handler THEN ' Treat differently for each Dialog having a Context Menu
    IF tFont  = 0 THEN FONT NEW @DiDe.FontName, @DiDe.FontSize * 4, @DiDe.FontAttr TO tFont
    IF tFont2 = 0 THEN FONT NEW @DiDe.FontName, @DiDe.FontSize, @DiDe.FontAttr TO tFont2

    ' Get current time 'tim' at foreign location (including timediff) and day-offset 'day'
    GetCurrentTimAndDay()

    ' Get 'image' dimensions (iy0 = 0)
    GdipGetImageWidth hDib, iw
    GdipGetImageHeight hDib, ih

    ' Get *EXACT* 'tim' and 'day' graphic strings dimensions & coordinate (Y-offset)
    GetDimensions @DiDe.Handler, tim, tFont,  tw, th, ty0
    IF LEN(day) > 0 THEN GetDimensions @DiDe.Handler, day, tFont2, ow, oh, oy0

    ' Adapt picture and clock dimensions (and respect ratio while resizing)
    IF PicRsz=1 THEN
        IF PicHor=1 THEN                    ' HOR + pic resized
            iw *= th / ih
            ih = th
        ELSE                                ' VER + pic resized
            ih *= (tw+ow) / iw
            iw = (tw+ow)
        END IF
    ELSE
        IF PicHor=1 THEN
            IF PicAli=1 THEN                ' HOR + pic untouched + BOTTOM aligned
                IF th<ih THEN
                    ty0 += (ih-th)          ' pic is bigger in height > offset clock+day
                    oy0 += (ih-th)
                ELSE                        ' clock is bigger in height > offset picture
                    iy0 += (th-ih)
                END IF
            ELSEIF PicAli=2 THEN            ' HOR + pic untouched + V-CENTERED
                IF th<ih THEN
                    ty0 += (ih-th)\2        ' pic is bigger in height > offset clock+day
                    oy0 += (ih-th)\2
                ELSE                        ' clock is bigger in height > offset picture
                    iy0 += (th-ih)\2
                END IF
            END IF
        ELSE
            IF PicAli=1 THEN                ' VER + pic untouched + RIGHT aligned
                IF (tw+ow)<iw THEN
                    tx0 += (iw-(tw+ow))     ' pic is bigger in width > offset clock+day
                    ox0 += (iw-(tw+ow))
                ELSE                        ' clock is bigger in width > offset picture
                    ix0 += ((tw+ow)-iw)
                END IF
            ELSEIF PicAli=2 THEN            ' VER + pic untouched + CENTERED
                IF (tw+ow)<iw THEN
                    tx0 += (iw-(tw+ow))\2   ' pic is bigger in width > offset clock+day
                    ox0 += (iw-(tw+ow))\2
                ELSE                        ' clock is bigger in width > offset picture
                    ix0 += ((tw+ow)-iw)\2
                END IF
            END IF
        END IF
    END IF

    ' Calculate dialog offset if necessary
    dX = 0
    dY = 0
    IF ISTRUE(@DiDe.Caption) THEN
      dX = 5
      IF ISTRUE(@DiDe.TaskBar) THEN dY = TaskBarHeight ELSE dY = CaptionHeight
    END IF

    ' Kill all controls
    CONTROL KILL @DiDe.Handler, %IDC_IMAGE_GFX
    CONTROL KILL @DiDe.Handler, %IDC_TIME_GFX
    CONTROL KILL @DiDe.Handler, %IDC_DAY_GFX

    ' Adapt dialog size to the graphic-controls inside it
    IF PicHor=1 THEN
        DIALOG SET SIZE  @DiDe.Handler, dX + (iw+tw+ow), dY + MAX(ih,th) ' HOR
    ELSE
        DIALOG SET SIZE  @DiDe.Handler, dX + MAX(tw+ow,iw), dY + (ih+th) ' VER
    END IF
    DIALOG SET COLOR @DiDe.Handler, -1, @DiDe.BgndCol

    ' Recreate graphic controls at the good size and position
    IF PicHor=1 THEN
        IF PicPos < 0 THEN   ' HOR + picture at the LEFT
            CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_IMAGE_GFX, "", 0, 0, iw, MAX(ih,th), %SS_NOTIFY
            CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_TIME_GFX, "", iw, 0, tw, MAX(ih,th)
            IF LEN(day) > 0 THEN CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_DAY_GFX, "", iw+tw, 0, ow, MAX(ih,th)
        ELSE                 ' HOR + picture at the RIGHT
            CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_TIME_GFX, "", 0, 0, tw, MAX(ih,th)
            IF LEN(day) > 0 THEN CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_DAY_GFX, "", tw, 0, ow, MAX(ih,th)
            CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_IMAGE_GFX, "", tw+ow, 0, iw, MAX(ih,th), %SS_NOTIFY
        END IF
    ELSE
        LOCAL tstart AS LONG
        IF PicPos < 0 THEN   ' VER + picture at the TOP
            CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_IMAGE_GFX, "", 0, 0, MAX(iw,tw+ow), ih, %SS_NOTIFY
            CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_TIME_GFX, "", 0, ih, MAX(iw,tw+ow)-ow, th
            IF LEN(day) > 0 THEN CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_DAY_GFX, "", tw, ih, ow, th
        ELSE                 ' VER + picture at the BOTTOM
            CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_TIME_GFX, "", 0, 0, MAX(iw,tw+ow)-ow, th
            IF LEN(day) > 0 THEN CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_DAY_GFX, "", tw, 0, ow, th
            CONTROL ADD GRAPHIC, @DiDe.Handler, %IDC_IMAGE_GFX, "", 0, th, MAX(iw,tw+ow), ih, %SS_NOTIFY
        END IF
    END IF

    ' Refresh content of image graphic control
    GRAPHIC ATTACH @DiDe.Handler, %IDC_IMAGE_GFX
    GRAPHIC COLOR @DiDe.FgndCol, @DiDe.BgndCol
    GRAPHIC CLEAR
    GdipDrawImageRect hGdip(), hDib, ix0, iy0, iw, ih

    ' Refresh content of time graphic control
    GRAPHIC ATTACH @DiDe.Handler, %IDC_TIME_GFX
    GRAPHIC COLOR @DiDe.FgndCol, @DiDe.BgndCol
    GRAPHIC SET FONT tFont
    GRAPHIC CLEAR
    GRAPHIC SET POS (tx0, ty0)
    GRAPHIC PRINT tim

    ' Refresh content of day offset graphic control (if any)
    IF LEN(day) > 0 THEN
        GRAPHIC ATTACH @DiDe.Handler, %IDC_DAY_GFX
        GRAPHIC COLOR @DiDe.FgndCol, @DiDe.BgndCol
        GRAPHIC SET FONT tFont2
        GRAPHIC CLEAR
        GRAPHIC SET POS (ox0, oy0)
        GRAPHIC PRINT day
    END IF

    ' Set tool tip and force redraw
    SetToolTip @DiDe.Handler
    DIALOG REDRAW @DiDe.Handler

  END IF

END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB ChangeFont(BYVAL DiDe AS DialogDescriptor POINTER)

  IF @DiDe.Handler = ClockDescriptor.Handler THEN ' Treat differently for each Dialog having a Context Menu
      FONT END tFont  : tFont  = 0
      FONT END tFont2 : tFont2 = 0
  END IF

END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB DefaultFont(BYVAL DiDe AS DialogDescriptor POINTER)

  IF @DiDe.Handler = ClockDescriptor.Handler THEN ' Treat differently for each Dialog having a Context Menu
    @DiDe.FontName = "Tahoma"
    @DiDe.FontSize = 12
    @DiDe.FontAttr = 1 ' 1 = "Bold"
  END IF

END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB DefaultColors(BYVAL DiDe AS DialogDescriptor POINTER)

  IF @DiDe.Handler = ClockDescriptor.Handler THEN ' Treat differently for each Dialog having a Context Menu
    @DiDe.FgndCol = %BLACK
    @DiDe.BgndCol = RGB(224,223,227)
  END IF

END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
MACRO SwitchToHor
    CONTROL ENABLE CB.HNDL, %IDC_LBLH
    CONTROL ENABLE CB.HNDL, %IDC_OPTION1
    CONTROL ENABLE CB.HNDL, %IDC_OPTION2
    CONTROL ENABLE CB.HNDL, %IDC_OPTION3
    CONTROL DISABLE CB.HNDL, %IDC_LBLV
    CONTROL DISABLE CB.HNDL, %IDC_OPTION4
    CONTROL DISABLE CB.HNDL, %IDC_OPTION5
    CONTROL DISABLE CB.HNDL, %IDC_OPTION6
    CONTROL SET OPTION CB.HNDL, %IDC_OPT_HOR, %IDC_OPT_HOR, %IDC_OPT_VER
END MACRO
'--------------------------------------------------------------------------------
MACRO SwitchToVer
    CONTROL DISABLE CB.HNDL, %IDC_LBLH
    CONTROL DISABLE CB.HNDL, %IDC_OPTION1
    CONTROL DISABLE CB.HNDL, %IDC_OPTION2
    CONTROL DISABLE CB.HNDL, %IDC_OPTION3
    CONTROL ENABLE CB.HNDL, %IDC_LBLV
    CONTROL ENABLE CB.HNDL, %IDC_OPTION4
    CONTROL ENABLE CB.HNDL, %IDC_OPTION5
    CONTROL ENABLE CB.HNDL, %IDC_OPTION6
    CONTROL SET OPTION CB.HNDL, %IDC_OPT_VER, %IDC_OPT_HOR, %IDC_OPT_VER
END MACRO
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB Settings()
    LOCAL hD  AS DWORD

    DIALOG NEW ClockDescriptor.Handler, _
      IIF$(LNG="EN",$EXE+" settings","Options de "+$EXE), -30, -60, 191, 110, _
      %DS_MODALFRAME OR %WS_CAPTION OR %WS_POPUP OR %WS_SYSMENU, TO hD
    DIALOG SET ICON       hD, "ICO1"

    CONTROL ADD CHECKBOX, hD, %IDC_CHK_RSZ, IIF$(LNG="EN", _
        "&Resize picture based on clock size", _
        "&Redimensionner l'image sur l'heure"), _
        5, 5, 175, 10, %WS_CHILD OR %WS_VISIBLE OR %WS_GROUP OR _
        %WS_TABSTOP OR %BS_TEXT OR %BS_AUTOCHECKBOX OR %BS_LEFT OR _
        %BS_VCENTER, %WS_EX_LEFT OR %WS_EX_LTRREADING

    CONTROL ADD OPTION,   hD, %IDC_OPT_HOR, IIF$(LNG="EN", _
        "&Horizontal mode (picture on left/right)", _
        "Mode &horizontal (im   age  gauche/droite)"), _
        5, 20, 175, 10, %WS_CHILD OR %WS_VISIBLE OR %WS_GROUP _
        OR %WS_TABSTOP OR %BS_TEXT OR %BS_AUTORADIOBUTTON OR %BS_LEFT OR _
        %BS_VCENTER, %WS_EX_LEFT OR %WS_EX_LTRREADING
    CONTROL ADD OPTION,   hD, %IDC_OPT_VER, IIF$(LNG="EN", _
        "&Vertical mode (picture on top/bottom)", _
        "Mode &vertical (image en haut/bas)"), _
        5, 50, 175, 10

    CONTROL ADD BUTTON,   hD, %IDCANCEL, IIF$(LNG="EN","Cancel","Annuler"), 65, 90, 60, 15, _
        %WS_CHILD OR %WS_VISIBLE OR %WS_GROUP OR %WS_TABSTOP OR %BS_TEXT OR _
        %BS_PUSHBUTTON OR %BS_CENTER OR %BS_VCENTER, %WS_EX_LEFT OR _
        %WS_EX_LTRREADING
    CONTROL ADD BUTTON,   hD, %IDOK, "OK", 135, 90, 45, 15
    CONTROL ADD LABEL,    hD, %IDC_LBLH, IIF$(LNG="EN","Alignment:","Alignement :"), 10, 35, 45, 10
    CONTROL ADD LABEL,    hD, %IDC_LBLV, IIF$(LNG="EN","Alignment:","Alignement :"), 10, 65, 45, 10

    CONTROL ADD OPTION,   hD, %IDC_OPTION1, IIF$(LNG="EN","&Top","H&aut"), 60, 35, 40, 10, _
        %WS_CHILD OR %WS_VISIBLE OR %WS_GROUP OR %WS_TABSTOP OR %BS_TEXT OR _
        %BS_AUTORADIOBUTTON OR %BS_LEFT OR %BS_VCENTER, %WS_EX_LEFT OR _
        %WS_EX_LTRREADING
    CONTROL ADD OPTION,   hD, %IDC_OPTION2, IIF$(LNG="EN","&Bottom","&Bas"), 100, 35, 45, 10
    CONTROL ADD OPTION,   hD, %IDC_OPTION3, IIF$(LNG="EN","V-Center","&Centr-V"), 145, 35, 45, 10
    CONTROL ADD OPTION,   hD, %IDC_OPTION4, IIF$(LNG="EN","&Left","&Gauche"), 60, 64, 40, 10
    CONTROL ADD OPTION,   hD, %IDC_OPTION5, IIF$(LNG="EN","&Right","&Droite"), 100, 64, 45, 10
    CONTROL ADD OPTION,   hD, %IDC_OPTION6, IIF$(LNG="EN","Center","C&entr"), 145, 64, 46, 10

    DIALOG SHOW MODAL     hD, CALL ProcSettings

END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
CALLBACK FUNCTION ProcSettings
    IF CB.MSG = %WM_INITDIALOG THEN
        CONTROL SET CHECK CB.HNDL, %IDC_CHK_RSZ, PicRsz
        IF PicHor=1 THEN
            SwitchToHor
            CONTROL SET OPTION CB.HNDL, %IDC_OPTION1+PicAli, %IDC_OPTION1, %IDC_OPTION6
        ELSE
            SwitchToVer
            CONTROL SET OPTION CB.HNDL, %IDC_OPTION4+PicAli, %IDC_OPTION1, %IDC_OPTION6
        END IF
    ELSEIF CB.MSG = %WM_COMMAND THEN
        SELECT CASE AS LONG CB.CTL
            CASE %IDC_OPT_HOR
                CONTROL SET OPTION CB.HNDL, %IDC_OPTION1, %IDC_OPTION1, %IDC_OPTION6
                SwitchToHor
            CASE %IDC_OPT_VER
                CONTROL SET OPTION CB.HNDL, %IDC_OPTION4, %IDC_OPTION1, %IDC_OPTION6
                SwitchToVer
            CASE %IDC_OPTION1, %IDC_OPTION2, %IDC_OPTION3
                CONTROL SET OPTION CB.HNDL, %IDC_OPT_HOR, %IDC_OPT_HOR, %IDC_OPT_VER
            CASE %IDC_OPTION4, %IDC_OPTION5, %IDC_OPTION6
                CONTROL SET OPTION CB.HNDL, %IDC_OPT_VER, %IDC_OPT_HOR, %IDC_OPT_VER
            CASE %IDCANCEL
                DIALOG END CB.HNDL
            CASE %IDOK
                CONTROL GET CHECK CB.HNDL, %IDC_CHK_RSZ TO PicRsz
                CONTROL GET CHECK CB.HNDL, %IDC_OPT_HOR TO PicHor
                LOCAL i, k AS LONG
                FOR i = %IDC_OPTION1 TO %IDC_OPTION6
                    CONTROL GET CHECK CB.HNDL, i TO k
                    IF k=1 THEN PicAli = (i-%IDC_OPTION1) MOD 3 : EXIT FOR
                NEXT
                WriteSettings()
                RefreshDialog VARPTR(ClockDescriptor)
                DIALOG END CB.HNDL
        END SELECT
    END IF
END FUNCTION
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
SUB About()
    LOCAL hD AS DWORD
    LOCAL richtext AS STRING

    LoadLibrary("RICHED32.DLL")

    DIALOG NEW PIXELS, ClockDescriptor.Handler, _
      IIF$(LNG="FR","A propos","About"), -200, -200, 500, 400, _
      %DS_MODALFRAME OR %WS_CAPTION OR %WS_POPUP OR _
      %WS_SYSMENU OR %WS_THICKFRAME OR %WS_MAXIMIZEBOX, _
      TO hD
    DIALOG SET ICON hD, "ICO1"
    CONTROL ADD "RichEdit", hD, 1000, "", 2, 2, 496, 396, _
      %WS_CHILD OR %WS_VISIBLE OR %ES_MULTILINE OR %ES_READONLY _
      OR %WS_VSCROLL

    SELECT CASE LNG
      CASE "EN"
        richtext  = "[black][c]"
        richtext += "[h:fuschia][font:a,11][b]" + $EXE + "[/b] " + $VERSION + "[/h][eol]"
        richtext += "[h:yellow][font:a,9]Instance: " + EXE.FULL$ + "[/h][eop][l]"

        richtext += "[font:a,12][eol][green][b][i]Widget interaction:[/b][/i][eop]"
        richtext += "[font:a,9][black][l][eol]"
        richtext += "[*][h:aqua]Right-clic[/h] = appearance menu[eol]"
        richtext += "[*][h:aqua]Drag-n-drop[/h] = move the widget[eol]"
        richtext += "[*][h:aqua]Mouse-wheel[/h] = transparency[eol]
        richtext += "[*][b]Re-launching[/b] the executable while the widget is already running will display "
        richtext +=     "the [h:aqua]Admin Box[/h]: it can be used if the widget is off-screen or not responding...[eol]"
        richtext += "[eop]"

        richtext += "[font:a,12][eol][green][b][i]Basic widget customization:[/b][/i][eop]"
        richtext += "[font:a,9][black][l][eol]"
        richtext += "To change the [h:aqua]time-zone[/h] and [h:aqua]picture[/h], you need to exit the widget and rename the executable.[eol]"
        richtext += "The executable name must be composed of 3 parts, separated by underscores:[eol]"
        richtext += "[*]1: ""nimoworld"" (immutable, always in first place)[eol]"
        richtext += "[*]2: the time difference e.g. [red]+6[black], or [red]-8h30[black][eol]"
        richtext += "[*]3: the name of a local picture e.g. [blue]usa_ca.jpg[black], or [blue]india.png[black][eol]"
        richtext += "[eol]"
        richtext += "Parts 2 and 3 can be swapped, depending if you want your picture respectively "
        richtext += "on the left/top or on the right/bottom of the clock: [eol]"
        richtext += "[*]""[b]nimoworld_[red]-9[black]_[blue]usa_ca.jpg[black].exe[/b]"" will display usa_ca.jpg on the right/bottom of the clock[eol]"
        richtext += "[*]""[b]nimoworld_[blue]india.png[black]_[red]+3h30[black].exe[/b]"" will display india.png on the left/top of the clock[eol]"
        richtext += "[eol]"
        richtext += "You can configure [h:aqua]picture mode[/h] (horizontal/vertical) and resizing, as well as alignment with a "
        richtext += "[blue][b]right click[/b][black] > [blue][b]nimo world settings[/b][black].[eol]"
        richtext += "[eol]"
        richtext += "Once your widget is customized, you can save and [h:lime]share it[/h] as a single executable file with a "
        richtext += "[blue][b]right click[/b][black] > [blue][b]Share this widget![/b][black][eol]"
        richtext += "[eop]"

        richtext += "[font:a,12][eol][green][b][i]Advanced widget customization:[/b][/i][eop]"
        richtext += "[font:a,9][black][l][eol]"
        richtext += "Right click on the widget to open the popup menu. There are 6 options to chose from:[eol]"
        richtext += "[*]1: ""[h:aqua]Always on top[/h]"" will keep your widget above all programs, even if it does not have the focus[eol]"
        richtext += "[*]2: ""[h:aqua]Never off the screen[/h]"" will re-center your widget if it is off-screen (e.g. on a dual display "
        richtext +=        "and you unplug the 2nd screen)[eol]"
        richtext += "[*]3: ""[h:aqua]Title bar & border[/h]"" will switch from widget look to standard Windows executable look[eol]"
        richtext += "[*]4: ""[h:aqua]Colors & Font[/h]"" > ""[h:aqua]Foreground/Background color[/h]"" will allow you to change the colors "
        richtext +=        "of your widget[eol]"
        richtext += "[*]5: ""[h:aqua]Colors & Font[/h]"" > ""[h:aqua]Choose font[/h]"" allows you to change the text font of the widget[eol]"
        richtext += "[*]6: ""[h:aqua]Transparency[/h]"" menu allows you to set background or foreground color as transparent (can cause "
        richtext +=        "glitches when widget is refreshed)[eol]"
        richtext += "[eol]"
        richtext += "Again, once you have set full appearance, time-zone, and custom picture in your widget, [h:lime]you can share [u]all of those[/u][/h] "
        richtext += "with a [blue][b]right click[/b][black] > [blue][b]Share this widget![/b][black][eol]"
        richtext += "[eop]"

        richtext += "[eol]"
        richtext += "[c][b][maroon]C[red]r[fuschia]e[purple]a[blue]t[teal]e[green]d [lime]b[grey]y [maroon]m[red]o[fuschia]u[purple]g[blue]i[teal]n[green]o[lime]"
        richtext += " - http://mougino.free.fr[/b]"
        richtext += "[eol]"
        richtext += "[eop]"

      '======================================================================================================================================================================
      CASE "FR"
        richtext  = "[black][c]"
        richtext += "[h:fuschia][font:a,11][b]" + $EXE + "[/b] " + $VERSION + "[/h][eol]"
        richtext += "[h:yellow][font:a,9]Session : " + EXE.FULL$ + "[/h][eop][l]"

        richtext += "[font:a,12][eol][green][b][i]Interaction avec la widget :[/b][/i][eop]"
        richtext += "[font:a,9][black][l][eol]"
        richtext += "[*][h:aqua]Clic bouton droit[/h] = menu d'apparence[eol]"
        richtext += "[*][h:aqua]Glisser-dplacer[/h] = bouger la widget[eol]"
        richtext += "[*][h:aqua]Ascenseur de la souris[/h] = transparence[eol]"
        richtext += "[*][b]Relancer[/b] l'excutable quand la widget tourne dj permet d'afficher "
        richtext +=     "la [h:aqua]Boite d'Administration[/h] :  utiliser si la widget est hors de l'cran ou ne rpond plus...[eol]"
        richtext += "[eop]"

        richtext += "[font:a,12][eol][green][b][i]Personnalisation de base de la widget :[/b][/i][eop]"
        richtext += "[font:a,9][black][l][eol]"
        richtext += "Pour changer le [h:aqua]fuseau horaire[/h] et l'[h:aqua]image affiche[/h], vous devez quitter la widget et renommer l'excutable.[eol]"
        richtext += "Le nom de l'excutable doit tre compos de 3 parties, spares par des tirets-bas:[eol]"
        richtext += "[*]1: ""nimoworld"" (fixe, toujours en premire place)[eol]"
        richtext += "[*]2: la diffrence d'heure ex: [red]+6[black], ou [red]-8h30[black][eol]"
        richtext += "[*]3: le nom d'une image en local ex: [blue]usa_ca.jpg[black], ou [blue]india.png[black][eol]"
        richtext += "[eol]"
        richtext += "Les parties 2 et 3 peuvent tre changes, en fonction de si vous voulez l'image "
        richtext += " gauche ou  droite de l'horloge:[eol]"
        richtext += "[*]""[b]nimoworld_[red]-9[black]_[blue]usa_ca.jpg[black].exe[/b]"" affichera usa_ca.jpg  droite/en bas de l'horloge[eol]"
        richtext += "[*]""[b]nimoworld_[blue]india.png[black]_[red]+3h30[black].exe[/b]"" affichera india.png  gauche/en haut de l'horloge[eol]"
        richtext += "[eol]"
        richtext += "Vous pouvez configurer la [h:aqua]disposition d'image[/h] (horizontal/vertical) son redimensionnement et l'alignement via "
        richtext += "[blue][b]clic droit[/b][black] > [blue][b]Options de nimo world[/b][black].[eol]"
        richtext += "[eol]"
        richtext += "Une fois que votre widget est personnalise, vous pouvez la sauver [h:lime]et la partager[/h] en tant qu'excutable "
        richtext += "indpendant via un [blue][b]clic droit[/b][black] > [blue][b]Partager cette widget ![/b][black][eol]"
        richtext += "[eop]"

        richtext += "[font:a,12][eol][green][b][i]Personnalisation avance de la widget :[/b][/i][eop]"
        richtext += "[font:a,9][black][l][eol]"
        richtext += "Cliquez droit sur la widget pour ouvrir le menu. Vous pouvez choisir parmi 6 options :[eol]"
        richtext += "[*]1: ""[h:aqua]Toujours au dessus[/h]"" gardera votre widget par dessus les autres programmes, mme si elle n'a pas le focus[eol]"
        richtext += "[*]2: ""[h:aqua]Jamais hors de l'cran[/h]"" va re-centrer votre widget si celle-ci se retrouve en dehors de l'cran (ex. "
        richtext +=        "en cas d'affichage multiple et de dbranchement du 2me cran)[eol]"
        richtext += "[*]3: ""[h:aqua]Barre de titre et bordure[/h]"" permet de passer de l'aspect widget  l'apparence standard d'un excutable Windows[eol]"
        richtext += "[*]4: ""[h:aqua]Couleurs & Police[/h]"" > ""[h:aqua]Couleur des critures/du fond[/h]"" permet de changer les couleurs "
        richtext +=        "de votre widget[eol]"
        richtext += "[*]5: ""[h:aqua]Couleurs & Police[/h]"" > ""[h:aqua]Choisir police d'criture[/h]"" change la police de texte de la widget[eol]"
        richtext += "[*]6: Le menu ""[h:aqua]Transparence[/h]"" permet de dfinir la couleur de fond ou des critures comme transparente (peut aboutir "
        richtext +=        " des problmes graphiques lorsque la widget se raffraichit)[eol]"
        richtext += "[eol]"
        richtext += "Rappel : une fois que vous avez fini de changer l'apparence, le fuseau horaire, et votre propre image pour la widget, "
        richtext += "[h:lime]vous pouvez partager [u]l'ensemble de ces lments[/u][/h] avec un simple [blue][b]clic droit[/b][black] > "
        richtext += "[blue][b]Partager cette widget ![/b][black][eol]"
        richtext += "[eop]"

        richtext += "[eol]"
        richtext += "[c][b][maroon]R[red][fuschia]a[purple]l[blue]i[teal]s[green] [lime]p[grey]a[olive]r [maroon]m[red]o[fuschia]u[purple]g[blue]i[teal]n[green]o[lime]"
        richtext += " - http://mougino.free.fr[/b]"
        richtext += "[eol]"
        richtext += "[eop]"

    END SELECT

    RTF_SET hD, 1000, richtext
    DIALOG SHOW MODELESS hD CALL ProcAbout
END SUB
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
CALLBACK FUNCTION ProcAbout
    IF CB.MSG = %WM_NOTIFY AND CB.NMID = 1000 AND CB.NMCODE = %EN_LINK THEN
      RTF_hyperlink (CB.HNDL, 1000, CB.LPARAM)
    ELSEIF CB.MSG = %WM_EXITSIZEMOVE OR CB.MSG = %WM_SIZE THEN
      IF CB.WPARAM = %SIZE_MAXIMIZED OR CB.WPARAM = %SIZE_RESTORED THEN
        LOCAL w, h AS LONG
        DIALOG GET CLIENT CB.HNDL TO w, h
        CONTROL SET SIZE CB.HNDL, 1000, w-4, h-4
      END IF
    END IF
END FUNCTION
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
FUNCTION ToMinutes(tim AS STRING) AS LONG ' tim = "+3h30" or "-10h15"
    LOCAL t AS STRING
    LOCAL i, mn, sg AS LONG
    IF LEFT$(tim,1) = "-" THEN sg=-1 ELSE sg=+1
    t = LCASE$(MID$(tim,2))
    i = INSTR(t, "h")
    IF i = 0 THEN mn = 60*VAL(t) ELSE mn = 60*VAL(LEFT$(t,i-1)) + VAL(MID$(t,i+1))
    FUNCTION = sg * mn
END FUNCTION
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
FUNCTION PBMAIN()
    LOCAL pic AS STRING
    LOCAL i AS LONG

    ' Initialize GDI+ library
    GdipInitialize()

    ' Is data packaged in the current executable? If yes retrieve it to sharedData()
    DIM sharedData(1 TO 3) ' when packaging a custom widget, we share: 1.pic, 2.timediff, 3.picpos
    GetSharedData()

    ' Parse name of current executable to find pic timediff & picpos (will overwrite ExeData info, if any)
    i = TALLY(EXE.NAME$, "_")
    IF i >= 2 THEN
        i = INSTR(-1, EXE.NAME$, "_")
        IF  (MID$(EXE.NAME$, i+1, 1) = "+" _
          OR MID$(EXE.NAME$, i+1, 1) = "-") THEN  ' >>> nimoworld_mypic.jpg_+10 <<<
            PicPos = -1
            timediff = ToMinutes(MID$(EXE.NAME$, i+1))
            pic = LEFT$(EXE.NAME$, i-1)
            i = INSTR(pic, "_")
            pic = EXE.PATH$ + MID$(pic, i+1)
            GdipLoadImageFromFile UCODE$(pic), hDib

        ELSE                                      ' >>> nimoworld_+10_mypic.jpg <<<
            i = INSTR(EXE.NAME$, "_")             ' Try time-offset on the left (image on the right)
            pic = MID$(EXE.NAME$, i+1)
            i = INSTR(pic, "_")
            IF  (LEFT$(pic, 1) = "+" _
              OR LEFT$(pic, 1) = "-") THEN
                PicPos = +1
                timediff = ToMinutes(LEFT$(pic, i-1))
                pic = EXE.PATH$ + MID$(pic, i+1)
                GdipLoadImageFromFile UCODE$(pic), hDib
            END IF
        END IF
    END IF

    ' Handle first sharedData/setting: picture
    IF hDib = 0 AND sharedData(1) <> "" THEN      ' No/bad pic in exe name > take the one packaged
        pic = LocalAppData & EXE.NAME$ & ".png"
        SetFile sharedData(1), pic
        GdipLoadImageFromFile UCODE$(pic), hDib
    ELSEIF hDib <> 0 THEN                         ' Store local pic in sharedData to make custom widget
        sharedData(1) = GetFile(pic)
    ELSE
        hDib = GdipLoadPngFromResource("WORLD")   ' In last resort load pic from EXE resource
    END IF

    ' Handle second sharedData/setting: timediff
    IF timediff = 0 AND sharedData(2) <> "" THEN  ' No timediff in exe name > take the one packaged
        timediff = VAL(sharedData(2))
    ELSE
        sharedData(2) = FORMAT$(timediff)         ' Store timediff in sharedData to make custom widget
    END IF

    ' Handle third sharedData/setting: picpos
    IF PicPos = 0 AND sharedData(3) <> "" THEN    ' No picpos in exe name > take the one packaged
       PicPos = VAL(sharedData(3))
    ELSE
       IF PicPos = 0 THEN PicPos = -1
       sharedData(3) = FORMAT$(PicPos)            ' Store picpos in sharedData to make custom widget
    END IF

    ' Read config ; replace by local script, if any
    IF EXIST(EXE.PATH$ & EXE.NAME$ & ".cfg") THEN
      KILL CfgFile
      NAME EXE.PATH$ & EXE.NAME$ & ".cfg" AS CfgFile
    END IF
    ReadSettings()

    ShowClockDialog %HWND_DESKTOP

END FUNCTION
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
'   ** CallBacks **
'--------------------------------------------------------------------------------
CALLBACK FUNCTION ProcClockDialog()
  ' MAIN DIALOG'S CALLBACK PROCEDURE
  ' A 1s timer is created under %WM_INITDIALOG
  ' and it will trigger a %WM_TIMER message every 1 second
  ' which will update the clock

  LOCAL mouseEvent AS LONG
  LOCAL pt AS POINTAPI
  STATIC time AS STRING
  STATIC idEvent, LBtnDn AS LONG

  ' Handle CallBack Messages linked to Context Menu
  HandleContextCbMsg VARPTR(ClockDescriptor), CB.MSG, CB.CTL, CB.WPARAM, CB.LPARAM

  ' Start handling other CallBack Messages
  SELECT CASE CB.MSG

    ' CallBack Message sent right before the dialog is displayed
    CASE %WM_INITDIALOG
      idEvent = SetTimer(CB.HNDL, %ID_TIMER1, 1000, BYVAL %NULL)
      DIALOG POST CB.HNDL, %WM_TIMER, %ID_TIMER1, 0

    ' Change cursor to four-pointer arrow when hovering over image
    CASE %WM_SETCURSOR
      CONTROL SET FOCUS CB.HNDL, %IDC_DUMMY ' to be able to process mouse wheel
      IF GetDlgCtrlId(CB.WPARAM) = %IDC_IMAGE_GFX THEN
        SetCursor LoadCursor(%NULL, BYVAL %IDC_SIZEALL)
        SetWindowLong CB.HNDL, %dwl_msgresult, 1
        FUNCTION = 1
        mouseEvent = HI(WORD, CB.LPARAM)
        IF mouseEvent = %WM_LBUTTONDOWN THEN LBtnDn = -1
        IF mouseEvent = %WM_LBUTTONUP THEN LBtnDn = 0
        IF LBtnDn THEN SendMessage CB.HNDL, %WM_LBUTTONDOWN, %MK_LBUTTON, 0
      END IF

    ' CallBack Message sent when user drags the dialog (i.e. moves the mouse over it while pressing left button)
    CASE %WM_LBUTTONDOWN
      GetCursorPos pt
      ScreenToClient CB.HNDL, pt
      IF CB.WPARAM = %MK_LBUTTON THEN SendMessage CB.HNDL, %WM_NCLBUTTONDOWN, %HTCaption, BYVAL %Null ' force drag

    ' CallBack Message is a Timer event (every 1000 ms)
    CASE %WM_TIMER
      IF CB.WPARAM = %ID_TIMER1 THEN ' Make sure it's the correct timer id
        IF GetCurTime() <> time THEN ' only refresh time when it changes
          time = GetCurTime()
          RefreshDialog VARPTR(ClockDescriptor)
        END IF
      END IF

    ' CallBack Message sent when computer wakes up from standby (sleep) or hibernate (deep sleep) mode
    CASE %WM_PowerBroadcast
        IF (CB.WPARAM = %PBT_APMRESUMESUSPEND OR _
           CB.WPARAM = %PBT_APMRESUMESTANDBY OR _
           CB.WPARAM = %PBT_APMRESUMECRITICAL) THEN RefreshDialog VARPTR(ClockDescriptor)

    ' CallBack Message sent when the dialog is being destroyed
    CASE %WM_DESTROY
      ' If a timer identifier exists make sure to stop the timer events
      IF idEvent THEN KillTimer CB.HNDL, idEvent

  END SELECT

END FUNCTION
'--------------------------------------------------------------------------------

'--------------------------------------------------------------------------------
'   ** Dialogs **
'--------------------------------------------------------------------------------
FUNCTION ShowClockDialog(BYVAL hParent AS DWORD) AS LONG

    LOCAL lRslt AS LONG
    LOCAL hDlg AS DWORD

    DIALOG NEW PIXELS, hParent, EXE.NAME$,,, 130, 50 TO hDlg

    DIALOG SET ICON hDlg, "ICO1"

    ClockDescriptor.Handler = hDlg                ' dialog handle
    ClockDescriptor.AllowMinimize = 0             ' icon "_" in caption (title bar) / "minimize" in context menu
    ClockDescriptor.AllowMaximize = 0             ' icon "[]" in caption (title bar) / "maximize" in context menu
    ClockDescriptor.AllowResize = 0               ' dialog can be resized by user
    ClockDescriptor.OnTop = 1                     ' dialog is always on top
    ClockDescriptor.InScr = 0                     ' dialog can be off screen
    ClockDescriptor.Caption = 1                   ' dialog has a caption (title bar) and a border
    ClockDescriptor.TaskBar = 0                   ' dialog appears in Task Bar
    ClockDescriptor.SysTray = 0                   ' dialog appears in SysTray
    ClockDescriptor.Transparency = 255            ' dialog transparency from 0 (invisible) to 255 (plain dialog)
    ClockDescriptor.FgndTrspt = 0                 ' writings are completely transparent
    ClockDescriptor.BgndTrspt = 0                 ' dialog background is completely transparent
    ClockDescriptor.FgndCol = %BLACK              ' font color
    ClockDescriptor.BgndCol = RGB(224,223,227)    ' background color
    ClockDescriptor.FontName  = "Tahoma"          ' font family
    ClockDescriptor.FontSize = 12                 ' font size
    ClockDescriptor.FontAttr = 1                  ' font attribute (0 = Normal ; 1 = Bold ...)
    ClockDescriptor.SettingsEntry = 1             ' enable "$EXE settings..." entry in context menu
    CreateSystray EXE.NAME$, "ICO1", _            ' SysTray label and icon
        VARPTR(ClockDescriptor)

    CONTROL ADD LABEL,   hDlg, %IDC_DUMMY, "", 0, 0, 0, 0
    CONTROL ADD GRAPHIC, hDlg, %IDC_IMAGE_GFX, "", 0, 0, 0, 0
    CONTROL ADD GRAPHIC, hDlg, %IDC_TIME_GFX, "", 0, 0, 0, 0
    CONTROL ADD GRAPHIC, hDlg, %IDC_DAY_GFX, "", 0, 0, 0, 0

    DIALOG SHOW MODAL hDlg, CALL ProcClockDialog TO lRslt

    FUNCTION = lRslt

END FUNCTION
'--------------------------------------------------------------------------------