|
Ghost in the Machine Clock
This is a piece of clock software that takes over your mouse and "writes" the time onto your screen like a ghost. It includes a character editor as well. It can also write with transparent mouse trails, which is done by taking a screen capture, pasting it into a borderless window, making it transparent, and drawing to that. I had lots of fun writing this.
More documentation on my blog here: http://www.zentastic.com/blog/2009/1...-beta-version/
Program source code:
Code:
#COMPILE EXE
#DIM ALL
'Compress via: \work\upx304w\upx.exe -9 \work\scrollclock\gitm-clock.exe
#INCLUDE ONCE "WIN32API.INC"
#INCLUDE ONCE "COMMCTRL.INC"
#INCLUDE ONCE "PBForms.INC"
#RESOURCE "gitm-clock.pbr" 'Icon and Default Charset
TYPE CustomColors
c(15) AS LONG
END TYPE
'Globals
GLOBAL hMainDialog AS DWORD 'The handle of the main/settings dialog window
GLOBAL hCreatorDialog AS DWORD 'The handle of the creator dialog window
GLOBAL graphicX AS LONG 'The X-location of the character editor graphic control
GLOBAL graphicY AS LONG 'The Y-location of the character editor graphic control
GLOBAL graphicW AS LONG 'The width of the character editor graphic control
GLOBAL graphicH AS LONG 'The height of the character editor graphic control
GLOBAL isRecording AS LONG 'Are we currently recording a character path?
GLOBAL recordingWhat AS LONG 'What character is currently being recorded?
GLOBAL pointList$() 'This is a list of 12 (0-9, :, AM, PM) lists of points for all the characters
GLOBAL lastDrawing AS LONG 'What was the last drawing that we rendered (since if it's the same, it's drawn with the mouse animation)
GLOBAL mouseRunning AS LONG 'Is the mouse currently moving under program control?
GLOBAL unsavedChars AS LONG 'Do we have unsaved characters that we've created in the creator module?
GLOBAL ClockStyle$ 'How to render the clock; valid options are "HH:MM AM (12hr)", "HH:MM (12hr)", "HHMM AM (12hr)", "HHMM (12hr)", "HH:MM (24hr)", "HHMM (24hr)"
GLOBAL ShortcutKeyModifier$ 'Shortcut key, shift part; valid options are "Ctrl", "Alt", "Ctrl+Alt", "Ctrl+Shift", "Alt+Shift", "Ctrl+Alt+Shift"
GLOBAL ShortcutKey$ 'Shortcut key, actual key; valid shortcut keys are 0-9,A-Z,F1-F12
GLOBAL CharacterSet$ 'Filename of the character set to use; "Default.set" is stored internally, others are loaded from disk
GLOBAL MaxPixelHeight AS LONG 'Default is 323, with a width of 230, and it scales from there
GLOBAL LeaveMouseTrails AS LONG '(T/F) Are we drawing mouse trails on the screen, or just moving the cursor
GLOBAL SlowdownFactor AS LONG '(1 - 200); the number of milliseconds pause between points
GLOBAL AccelerationFactor AS LONG '(1 - 10); where 1 = Normal, 2 = Play every 2nd point, 3 = Play every 3rd point, etc.
GLOBAL theCanvasX AS LONG 'Canvas X of top-left while in use
GLOBAL theCanvasY AS LONG 'Canvas Y of top-left while in use
GLOBAL CustomColorList AS CustomColors 'Stores 16 user-defined colors
GLOBAL trailsColor AS LONG 'This is the color of the trails
'List of Dialog Constants
%IDD_CREATOR_DIALOG = 101 'This is the character creator dialog
%IDC_GRAPHIC = 1001
%IDC_RECORD_0 = 1500
%IDC_RECORD_1 = 1501
%IDC_RECORD_2 = 1502
%IDC_RECORD_3 = 1503
%IDC_RECORD_4 = 1504
%IDC_RECORD_5 = 1505
%IDC_RECORD_6 = 1506
%IDC_RECORD_7 = 1507
%IDC_RECORD_8 = 1508
%IDC_RECORD_9 = 1509
%IDC_RECORD_COLON = 1510
%IDC_RECORD_AM = 1511
%IDC_RECORD_PM = 1512
%IDC_SHOW_0 = 1600
%IDC_SHOW_1 = 1601
%IDC_SHOW_2 = 1602
%IDC_SHOW_3 = 1603
%IDC_SHOW_4 = 1604
%IDC_SHOW_5 = 1605
%IDC_SHOW_6 = 1606
%IDC_SHOW_7 = 1607
%IDC_SHOW_8 = 1608
%IDC_SHOW_9 = 1609
%IDC_SHOW_COLON = 1610
%IDC_SHOW_AM = 1611
%IDC_SHOW_PM = 1612
%IDC_BUTTON_STOP = 1023
%IDC_BUTTON_QUIT = 1033
%IDC_BUTTON_LOAD = 1030
%IDC_BUTTON_NEW = 1031
%IDC_BUTTON_SAVE = 1032
%IDC_MANUAL = 1034
%IDD_MAINDIALOG = 201 'The 2-series is the main/settings dialog
%IDC_LABEL_CLOCKSTYLE = 2001
%IDC_COMBOBOX_CLOCKSTYLE = 2002
%IDC_COMBOBOX_SHORTCUTKEY_MODIFIER = 2003
%IDC_LABEL_SHORTCUTKEY = 2004
%IDC_COMBOBOX_SHORTCUT_KEY = 2005
%IDC_LABEL_CHARACTERSET = 2008
%IDC_TEXTBOX_CHARACTERSET = 2010
%IDC_BUTTON_BROWSE_CS = 2011
%IDC_BUTTON_EDIT_CS = 2013
%IDC_TEXTBOX_PIXELHEIGHT = 2015
%IDC_LABEL_PIXELHEIGHT = 2016
%IDC_TEXTBOX_SLOWDOWN = 2017
%IDC_LABEL_SLOWDOWN = 2018
%IDC_TEXTBOX_ACCELERATION = 2019
%IDC_LABEL_ACCELERATION = 2020
%IDC_CHECKBOX_LEAVE_MOUSE_TRAILS = 2021
%IDC_BUTTON_SHOW_NOW = 2022
%IDC_LABEL_CURRENTLYLOOKSLIKE = 2023
%IDC_LABEL_CLOCKSAMPLE = 2024
%IDC_BUTTON_APPLY_SETTINGS = 2025
%IDC_BUTTON_RESTORE_DEFAULTS = 2026
%IDC_BUTTON_QUIT_PROGRAM = 2027
%IDC_LINE = 2028
%IDC_SAMPLECLOCK = 2029
%IDC_HOTKEY = 2030
%IDC_COLORPICKER = 2031
%IDD_FAKEBACKGROUND = 301
%IDC_GRAPHIC_FAKE = 3001
'Declarations
DECLARE FUNCTION DrawDigit(whatDigit AS LONG, whereX AS LONG, whereY AS LONG, lineIt AS LONG, OPTIONAL ratio AS DOUBLE) AS LONG 'Using the mouse, draw a digit
DECLARE CALLBACK FUNCTION ShowCREATOR_DIALOGProc()
DECLARE FUNCTION ShowCREATOR_DIALOG(BYVAL hParent AS DWORD) AS LONG
DECLARE SUB WriteManual() 'Write/reset the manual text to %IDC_MANUAL
DECLARE CALLBACK FUNCTION ShowMAINDIALOGProc()
DECLARE FUNCTION ShowMAINDIALOG(BYVAL hParent AS DWORD) AS LONG
DECLARE SUB WriteSettings() 'Write the INI file to disk
DECLARE SUB LoadSettings() 'Load the INI file
DECLARE SUB WriteSettingsToScreen() 'Write all the settings to the dialog
DECLARE SUB SetProgramDefaults() 'Set default of program
DECLARE FUNCTION LoadPoints(pointFilename$, pointsLoaded AS LONG) AS LONG 'Load a point set, return the number of characers and points loaded
DECLARE SUB UpdateShowButtons() 'Update the show buttons with bold/non-bold on the creator form to represent if they have data
DECLARE FUNCTION GetCurrentTime$() 'Return the current time, in the current clock style
DECLARE SUB wrapRegisterHotkey() 'Register the hotkey for the program
DECLARE SUB wrapUnregisterHotkey() 'Free the hotkey
DECLARE SUB CalculateWorkspace(Digits AS LONG, canvasX AS LONG, canvasY AS LONG, canvasW AS LONG, canvasH AS LONG, charW AS LONG, charH AS LONG, ration AS DOUBLE) 'Calculate the size of characters and canvas
FUNCTION PBMAIN() 'Main function
PBFormsInitComCtls (%ICC_WIN95_CLASSES OR %ICC_DATE_CLASSES OR %ICC_INTERNET_CLASSES)
DIM PointList$(0 TO 12) 'This stores the current character set
recordingWhat = -1 'What character is currently being recorded (nothing is -1, since 0 is a valid option)
lastDrawing = -1 'What's the last drawing that we viewed in the creator graphic
ShowMAINDIALOG %HWND_DESKTOP 'Start with the main/settings dialog
END FUNCTION
CALLBACK FUNCTION ShowCREATOR_DIALOGProc()
LOCAL dummyThread???, res AS LONG, dummy AS DWORD, charsLoaded AS LONG, pointsLoaded AS LONG
LOCAL hFont1 AS DWORD, drawingWhat AS LONG, ctr AS LONG, pointCount AS LONG
LOCAL thisX AS LONG, thisY AS LONG, lastDrawX AS LONG, lastDrawY AS LONG, drawX AS LONG, drawY AS LONG
LOCAL mousePt AS POINT, ff AS LONG, thisCheck AS LONG, sFileSpec$, TempFilename$
SELECT CASE AS LONG CB.MSG
CASE %WM_INITDIALOG
'Initialization handler
CASE %WM_NCACTIVATE
STATIC hWndSaveFocus AS DWORD
IF ISFALSE CB.WPARAM THEN
hWndSaveFocus = GetFocus() 'Save control focus
ELSEIF hWndSaveFocus THEN
SetFocus(hWndSaveFocus) 'Restore control focus
hWndSaveFocus = 0
END IF
CASE %WM_COMMAND
SELECT CASE AS LONG CB.CTL
CASE %IDC_GRAPHIC
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
isRecording = %FALSE 'If they click on the graphic window, then stop recording
END IF
CASE %IDC_RECORD_0 TO %IDC_RECORD_PM 'Clicked a record button
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
IF recordingWhat = -1 THEN 'Make sure we're not recording anything
isRecording = %TRUE 'Set that we are currently recording
recordingWhat = CB.CTL - %IDC_RECORD_0 'Mark what we're recording, based on the control ID
hFont1 = PBFormsMakeFont("MS Sans Serif", 10, 700, %FALSE, %FALSE, %FALSE, %ANSI_CHARSET) 'Create a slightly bigger, bold font
CONTROL SEND hCreatorDialog, %IDC_RECORD_0 + recordingWhat, %WM_SETFONT, hFont1, 0 'Set the font of the button we clicked
CONTROL REDRAW hCreatorDialog, %IDC_RECORD_0 + recordingWhat 'And redraw it
THREAD CREATE doRecord(dummy) TO dummyThread??? 'Start the recording thread
THREAD CLOSE dummyThread??? TO res 'And kill the handle since we don't need it
END IF
END IF
CASE %IDC_SHOW_0 TO %IDC_SHOW_PM 'Clicked a show button
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
GRAPHIC ATTACH hCreatorDialog, %IDC_GRAPHIC, REDRAW 'Attach to the graphic window
GRAPHIC CLEAR %RGB_WHITE 'Clear the window with a white background
drawingWhat = CB.CTL - %IDC_SHOW_0 'Set which character is being drawn
pointCount = PARSECOUNT(pointList$(drawingWhat)) 'How many points are on our list?
IF pointCount > 4 THEN 'Are there more than four? (actually, more than 2)
thisX = VAL(PARSE$(pointList$(drawingWhat), 1)) 'Get the first point X...
thisY = VAL(PARSE$(pointList$(drawingWhat), 2)) ' ...and Y
DIALOG PIXELS hCreatorDialog, thisX, thisY TO UNITS lastDrawX, lastDrawY 'Convert this to units for the dialog
FOR ctr = 3 TO pointCount STEP 2 'Go through all the points
thisX = VAL(PARSE$(pointList$(drawingWhat), ctr)) 'Grab this point's X...
thisY = VAL(PARSE$(pointList$(drawingWhat), ctr + 1)) ' ...and Y values
DIALOG PIXELS hCreatorDialog, thisX, thisY TO UNITS drawX, drawY 'Convert it to units as well
GRAPHIC LINE (lastDrawX, lastDrawY) - (drawX, drawY), %RGB_BLUE 'And draw a line connecting it to the previous point
lastDrawX = drawX 'Set the previous point...
lastDrawY = drawY ' ...to the value of the current point
IF lastDrawing = drawingWhat THEN 'Is this drawing the second time we've done it?
DIALOG DOEVENTS 1 'Pause for a moment to keep the dialog from freezing
SLEEP 10 'And sleep for a moment to keep it from being instant
GRAPHIC REDRAW 'Redraw the graphic window
'Now move mouse to match
DIALOG UNITS hCreatorDialog, drawX + 5, drawY + 5 TO PIXELS thisX, thisY 'Figure out the pixel location of this point
mousePt.x = thisX 'And set it inside a...
mousePt.y = thisY ' ...windows style point
ClientToScreen hCreatorDialog, mousePt 'Convert this client-relative pixel to a screen-relative pixel
SetCursorPos mousePt.X, mousePt.Y 'And move the cursor to this location
END IF
NEXT
END IF
lastDrawing = drawingWhat 'Set what we just drew
GRAPHIC REDRAW 'Redraw the graphic
GRAPHIC DETACH 'And detatch from it since we're done
END IF
CASE %IDC_BUTTON_STOP
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
isRecording = %FALSE 'Stop recording
END IF
CASE %IDC_BUTTON_QUIT
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
DIALOG END CBHNDL 'Quit this dialog
END IF
CASE %IDC_BUTTON_LOAD
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
IF unsavedChars <> %FALSE THEN 'If we have unsaved characters, warn them
thisCheck = MSGBOX("You have unsaved characters. Would you like to load anyway and lose this work?", %MB_SYSTEMMODAL OR %MB_YESNO, "Warning")
IF thisCheck <> %IDYES THEN 'Abort if they said no
EXIT SELECT
END IF
END IF
sFileSpec$ = "" 'No default filename, next line executes the windows load dialog
DISPLAY OPENFILE hCreatorDialog, , , "Please select a file to load from", EXE.PATH$, _
"All mouse movement sets (*.set)" & CHR$(0) & "*.set" & CHR$(0), sFileSpec$, "set", _
%OFN_FILEMUSTEXIST OR %OFN_PATHMUSTEXIST OR %OFN_ENABLESIZING OR %OFN_OVERWRITEPROMPT OR %OFN_CREATEPROMPT TO TempFilename$
TempFilename$ = TRIM$(TempFilename$) 'Clean up
IF DIR$(TempFilename$) <> "" THEN 'Valid file, we can open it
charsLoaded = LoadPoints(tempFilename$, pointsLoaded) 'Try and load the points from disk
UpdateShowButtons() 'Update the "show" buttons to show which characters are done
MSGBOX "Number of data sets of mouse motion digits loaded is " & FORMAT$(charsLoaded) + " with " & FORMAT$(pointsLoaded) & " individual coordinates", %MB_ICONINFORMATION, "Loaded Successfully" 'Tell them
unsavedChars = %FALSE 'We don't have unsaved characters on a fresh load
CONTROL SET TEXT hMainDialog, %IDC_TEXTBOX_CHARACTERSET, TempFilename$ 'Update the filename on the main dialog
WriteSettings() 'And save it as our current default
END IF
END IF
CASE %IDC_BUTTON_NEW 'NEW character set (clear basically)
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
IF unsavedChars <> %FALSE THEN 'If we have unsaved characters, warn them before clearing
thisCheck = MSGBOX("You have unsaved characters. Would you like to start over and lose this work?", %MB_SYSTEMMODAL OR %MB_YESNO, "Warning")
IF thisCheck <> %IDYES THEN 'If they said No, abort
EXIT SELECT
END IF
END IF
hFont1 = PBFormsMakeFont("MS Sans Serif", 8, 400, %FALSE, %FALSE, %FALSE, %ANSI_CHARSET) 'This is the normal font
FOR ctr = 0 TO 12 'Set fonts of buttons to indicate whether they exist or not
pointList$(ctr) = "" 'Clear the data on this character
CONTROL SEND hCreatorDialog, %IDC_SHOW_0 + ctr, %WM_SETFONT, hFont1, 0 'And clear the show button to match
CONTROL REDRAW hCreatorDialog, %IDC_SHOW_0 + ctr 'Redraw the button
NEXT
unsavedChars = %FALSE 'We have no unsaved buttons on a cleared list
END IF
CASE %IDC_BUTTON_SAVE 'SAVE the current character set
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
sFileSpec$ = "" 'No default filename. Next line executes the windows save dialog
DISPLAY SAVEFILE hCreatorDialog, , , "Please select a file to save to", EXE.PATH$, _
"All mouse movement sets (*.set)" & CHR$(0) & "*.set" & CHR$(0), sFileSpec$, "set", _
%OFN_PATHMUSTEXIST OR %OFN_ENABLESIZING OR %OFN_OVERWRITEPROMPT OR %OFN_CREATEPROMPT TO TempFilename$
TempFilename$ = TRIM$(TempFilename$) 'Clean up the filename (not sure if this is ever needed)
IF TempFilename$ <> "" THEN 'Did they enter a filename? (If not they cancelled)
ff = FREEFILE 'Get the next free handle
OPEN TempFilename$ FOR OUTPUT AS #ff 'Open it
FOR ctr = 0 TO 12 'Go through all the point sets in order
PRINT #ff, pointList$(ctr) 'And write them, one per line
NEXT
CLOSE #ff 'And we're done, so (next) tell the user
MSGBOX "Your data set of mouse motion digits has been saved to " & TempFilename$, %MB_ICONINFORMATION, "Saved Successfully"
unsavedChars = %FALSE 'We have nothing unsaved now
CharacterSet$ = TempFilename$ 'Set our current character set to the one we just saved
CONTROL SET TEXT hMainDialog, %IDC_TEXTBOX_CHARACTERSET, TempFilename$ 'Update the main dialog
WriteSettings() 'And update the settings file
END IF
END IF
END SELECT
END SELECT
END FUNCTION
FUNCTION ShowCREATOR_DIALOG(BYVAL hParent AS DWORD) AS LONG
LOCAL lRslt AS LONG, hInstance AS DOUBLE
LOCAL hAccel AS DWORD, AccelTable() AS ACCELAPI
DIM AccelTable(0 TO 14) AS ACCELAPI
DIALOG NEW hParent, "Mouse Motion Capture Character Creator", 199, 112, _
380, 211, %WS_POPUP OR %WS_BORDER OR %WS_DLGFRAME OR %WS_CAPTION OR _
%WS_SYSMENU OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR _
%DS_CENTER OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, _
%WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR _
%WS_EX_RIGHTSCROLLBAR, TO hCreatorDialog 'Create the creator window
CONTROL ADD GRAPHIC, hCreatorDialog, %IDC_GRAPHIC, "", 5, 5, 155, 200, %WS_BORDER OR %SS_NOTIFY 'Drawing area
DIALOG UNITS hCreatorDialog, 5, 5 TO PIXELS graphicX, graphicY 'Figure out the pixel size of the drawing area
DIALOG UNITS hCreatorDialog, 155, 200 TO PIXELS graphicW, graphicH
'Add all the record buttons
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_0, "0", 170, 5, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_1, "1", 170, 20, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_2, "2", 170, 35, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_3, "3", 170, 50, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_4, "4", 170, 65, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_5, "5", 170, 80, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_6, "6", 170, 95, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_7, "7", 170, 110, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_8, "8", 170, 125, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_9, "9", 170, 140, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_COLON, ":", 170, 160, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_AM, "AM", 170, 175, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_RECORD_PM, "PM", 170, 190, 25, 15
'Add all the view/show buttons
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_0, "0", 205, 5, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_1, "1", 205, 20, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_2, "2", 205, 35, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_3, "3", 205, 50, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_4, "4", 205, 65, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_5, "5", 205, 80, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_6, "6", 205, 95, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_7, "7", 205, 110, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_8, "8", 205, 125, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_9, "9", 205, 140, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_COLON, ":", 205, 160, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_AM, "AM", 205, 175, 25, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_SHOW_PM, "PM", 205, 190, 25, 15
'Add the manual/help text
CONTROL ADD LABEL, hCreatorDialog, %IDC_MANUAL, "", 240, 5, 135, 140
writeManual() 'Write the manual to this window (separated into a function in case I later want to multi-purpose this area)
'Add control buttons
CONTROL ADD BUTTON, hCreatorDialog, %IDC_BUTTON_STOP, "Stop Recording", 240, 150, 65, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_BUTTON_QUIT, "Quit Editor", 240, 190, 65, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_BUTTON_LOAD, "Load Set", 310, 150, 65, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_BUTTON_NEW, "New Set", 310, 170, 65, 15
CONTROL ADD BUTTON, hCreatorDialog, %IDC_BUTTON_SAVE, "Save Set", 310, 190, 65, 15
'Set up accelerator keys for all the record buttons
AccelTable(0).fvirt = %FVIRTKEY : AccelTable(0).key = %VK_0 : AccelTable(0).cmd = %IDC_RECORD_0
AccelTable(1).fvirt = %FVIRTKEY : AccelTable(1).key = %VK_1 : AccelTable(1).cmd = %IDC_RECORD_1
AccelTable(2).fvirt = %FVIRTKEY : AccelTable(2).key = %VK_2 : AccelTable(2).cmd = %IDC_RECORD_2
AccelTable(3).fvirt = %FVIRTKEY : AccelTable(3).key = %VK_3 : AccelTable(3).cmd = %IDC_RECORD_3
AccelTable(4).fvirt = %FVIRTKEY : AccelTable(4).key = %VK_4 : AccelTable(4).cmd = %IDC_RECORD_4
AccelTable(5).fvirt = %FVIRTKEY : AccelTable(5).key = %VK_5 : AccelTable(5).cmd = %IDC_RECORD_5
AccelTable(6).fvirt = %FVIRTKEY : AccelTable(6).key = %VK_6 : AccelTable(6).cmd = %IDC_RECORD_6
AccelTable(7).fvirt = %FVIRTKEY : AccelTable(7).key = %VK_7 : AccelTable(7).cmd = %IDC_RECORD_7
AccelTable(8).fvirt = %FVIRTKEY : AccelTable(8).key = %VK_8 : AccelTable(8).cmd = %IDC_RECORD_8
AccelTable(9).fvirt = %FVIRTKEY : AccelTable(9).key = %VK_9 : AccelTable(9).cmd = %IDC_RECORD_9
AccelTable(10).fvirt = %FVIRTKEY: AccelTable(10).key = %VK_OEM_1: AccelTable(10).cmd = %IDC_RECORD_COLON 'Should be ";" or ":" on US keyboards
AccelTable(11).fvirt = %FVIRTKEY: AccelTable(11).key = %VK_OEM_MINUS: AccelTable(11).cmd = %IDC_RECORD_COLON 'Should be "-" on all keyboards
AccelTable(12).fvirt = %FVIRTKEY: AccelTable(12).key = %VK_A : AccelTable(12).cmd = %IDC_RECORD_AM
AccelTable(13).fvirt = %FVIRTKEY: AccelTable(13).key = %VK_P : AccelTable(13).cmd = %IDC_RECORD_PM
AccelTable(14).fvirt = %FVIRTKEY: AccelTable(14).key = %VK_SPACE: AccelTable(14).cmd = %IDC_BUTTON_STOP
ACCEL ATTACH hCreatorDialog, AccelTable() TO hAccel
'Make sure that the show buttons visually match any preloaded data
UpdateShowButtons()
'Add the program icon
hInstance = GetModuleHandle("")
DIALOG SEND hCreatorDialog, %WM_SETICON, %ICON_BIG, LoadIcon(hInstance, "0L")
'Make the dialog visible
DIALOG SHOW MODAL hCreatorDialog, CALL ShowCREATOR_DIALOGProc TO lRslt
FUNCTION = lRslt
END FUNCTION
SUB WriteManual() 'Write/reset the manual text to %IDC_MANUAL
CONTROL SET TEXT hCreatorDialog, %IDC_MANUAL, "Instead of pushing the left column " + _
"of buttons, press ""0"" through ""9"" as well as "":"", ""A"", and " + _
"""P"" to begin recording a character (it will actually start the " + _
"instant you move the mouse), and then the space bar to stop " + _
"recording (or click the mouse button to stop). Note that all pauses and drawing speed will be recorded " + _
"as well as just the motion. Afterwards, pressing the buttons in the " + _
"right column let you view them (click a second time to view with animation). " + _
"The new, save, and load buttons let you create new sets, " + _
"save the current set, or load an older one."
END SUB
FUNCTION doRecord(BYVAL dummy AS DWORD) AS DWORD 'Record a sequence of mouse movements
LOCAL mousePt AS POINT
LOCAL startX AS LONG, startY AS LONG
LOCAL thisX AS LONG, thisY AS LONG
LOCAL drawX AS LONG, drawY AS LONG
LOCAL lastDrawX AS LONG, lastDrawY AS LONG
LOCAL temp$
LOCAL hFont1 AS DWORD
GRAPHIC ATTACH hCreatorDialog, %IDC_GRAPHIC, REDRAW 'Attach to the graphic window for drawing
GRAPHIC CLEAR %RGB_WHITE 'Clear it with a white background
GetCursorPos mousePt 'Get the current mouse location
ScreenToClient hCreatorDialog, mousePt 'And convert it to being relative to this dialog
startX = mousePt.x - graphicX 'Now convert it to being relative to the graphic control
startY = mousePt.y - graphicY 'And mark that as the start point
'Wait for first mouse movement
DO UNTIL isRecording = %FALSE 'Start loop and wait for first move
GetCursorPos mousePt 'Get current pouse point
ScreenToClient hCreatorDialog, mousePt 'Convert to being relative to the dialog
thisX = mousePt.x - graphicX 'And convert to being relative to the control
thisY = mousePt.y - graphicY
IF startX <> thisX OR startY <> thisY THEN 'Has the mouse moved?
pointList$(recordingWhat) = FORMAT$(thisX, "0") & "," & FORMAT$(thisY, "0") 'Add the starting point to the point set
EXIT DO 'And continue on to the main recording loop
END IF
LOOP
DIALOG PIXELS hCreatorDialog, thisX, thisY TO UNITS lastDrawX, lastDrawY 'Convert our start point to a unit value for drawing
DO UNTIL isRecording = %FALSE 'Record all mouse movement until something kills the thread
SLEEP 10 'Pause for just a moment: 1/100 of a second
GetCursorPos mousePt 'Get the current mouse location
ScreenToClient hCreatorDialog, mousePt 'Convert to dialog relative
thisX = mousePt.x - graphicX 'Convert to control relative
thisY = mousePt.y - graphicY
IF (thisX > 0 AND thisX < graphicW) AND (thisY > 0 AND thisY < graphicH) THEN 'Are we inside the drawing space?
pointList$(recordingWhat) = pointList$(recordingWhat) & ", " & FORMAT$(thisX, "0") & "," & FORMAT$(thisY, "0") 'Add this motion to the point set
DIALOG PIXELS hCreatorDialog, thisX, thisY TO UNITS drawX, drawY 'Convert pixel recording to dialog units for drawing
'graphic set pixel (drawX, drawY), %RGB_RED
GRAPHIC LINE (lastDrawX, lastDrawY) - (drawX, drawY), %RGB_RED 'Draw line, in red
lastDrawX = drawX 'And set last point to the current point
lastDrawY = drawY
GRAPHIC REDRAW 'Redraw the graphic window
END IF
LOOP
'Mark as done on view button
hFont1 = PBFormsMakeFont("MS Sans Serif", 10, 700, %FALSE, %FALSE, %FALSE, %ANSI_CHARSET) 'Create a bold font
CONTROL SEND hCreatorDialog, %IDC_SHOW_0 + recordingWhat, %WM_SETFONT, hFont1, 0 'Turn font to bold
CONTROL REDRAW hCreatorDialog, %IDC_SHOW_0 + recordingWhat 'On recording button
'Write stats to graphic window
hFont1 = PBFormsMakeFont("MS Sans Serif", 8, 400, %FALSE, %FALSE, %FALSE, %ANSI_CHARSET) 'Create a normal font
GRAPHIC SET FONT hFont1 'Make this our active font
GRAPHIC SET POS (5, 5) 'Start writing at top left
GRAPHIC COLOR %RGB_BLACK, -2 'Set color; -2 is "don't draw" for the background color
GRAPHIC PRINT "Done: " & FORMAT$(PARSECOUNT(pointList$(recordingWhat)) \ 2) & " points." 'Write text with point count
GRAPHIC REDRAW 'And update screen
'Restore button and other stuff
CONTROL SEND hCreatorDialog, %IDC_RECORD_0 + recordingWhat, %WM_SETFONT, hFont1, 0 'Set the recording button to normal font since we're done
CONTROL REDRAW hCreatorDialog, %IDC_RECORD_0 + recordingWhat 'Redraw that button
GRAPHIC DETACH 'And detach from graphic control
recordingWhat = -1 'We're no longer recording
lastDrawing = -1 'And drawing viewing is reset as well
unsavedChars = %TRUE 'And we definitely have unsaved characters (this one)
END FUNCTION
CALLBACK FUNCTION ShowMAINDIALOGProc() 'Deal with action in the main dialog
LOCAL temp$, res AS LONG, sFileSpec$, TempFilename$
LOCAL charsLoaded AS LONG, pointsLoaded AS LONG
LOCAL dummy AS LONG, dummyThread???
SELECT CASE AS LONG CB.MSG
CASE %WM_INITDIALOG
SetTimer(CBHNDL, %IDC_SAMPLECLOCK, 1000, BYVAL %NULL) 'Ping the timer once a second to update the clock
CASE %WM_CLOSE 'Window closed
KillTimer CBHNDL, %IDC_SAMPLECLOCK 'Kill timer
wrapUnregisterHotkey() 'Free the hotkey
CASE %WM_TIMER
SELECT CASE CBWPARAM
CASE %IDC_SAMPLECLOCK 'Update the clock display
CONTROL SET TEXT hMainDialog, %IDC_LABEL_CLOCKSAMPLE, GetCurrentTime$() 'Write current time
END SELECT
CASE %WM_HotKey
SELECT CASE CB.WPARAM
CASE %IDC_HOTKEY 'They hit the show time hotkey
THREAD CREATE doPlay(dummy) TO dummyThread??? 'Create the time player thread
THREAD CLOSE dummyThread??? TO res 'and throw away the handle
END SELECT
CASE %WM_NCACTIVATE
STATIC hWndSaveFocus AS DWORD
IF ISFALSE CB.WPARAM THEN
hWndSaveFocus = GetFocus() 'Save control focus
ELSEIF hWndSaveFocus THEN
SetFocus(hWndSaveFocus) 'Restore control focus
hWndSaveFocus = 0
END IF
CASE %WM_COMMAND, %WM_NOTIFY
SELECT CASE AS LONG CB.CTL
CASE %IDC_COMBOBOX_CLOCKSTYLE 'Clock style combobox
IF CB.CTLMSG = %CBN_SELENDOK THEN 'Clock format has been updated
CONTROL GET TEXT CBHNDL, %IDC_COMBOBOX_CLOCKSTYLE TO ClockStyle$ 'Grab the new style
WriteSettings() 'Update settings file
END IF
CASE %IDC_COMBOBOX_SHORTCUTKEY_MODIFIER 'Shortkey modifier combobox
IF CB.CTLMSG = %CBN_SELENDOK THEN 'Shortkey modifier has been updated
CONTROL GET TEXT CBHNDL, %IDC_COMBOBOX_SHORTCUTKEY_MODIFIER TO ShortcutKeyModifier$ 'Grab it
wrapUnregisterHotkey() 'Dump the old hotkey
wrapRegisterHotkey() 'And create the new hotkey
WriteSettings() 'Update settings file
END IF
CASE %IDC_COMBOBOX_SHORTCUT_KEY 'Shortcut key combobox
IF CB.CTLMSG = %CBN_SELENDOK THEN 'Shortcut key has been updated
CONTROL GET TEXT CBHNDL, %IDC_COMBOBOX_SHORTCUT_KEY TO ShortcutKey$ 'Grab it
wrapUnregisterHotkey() 'Dump the old hotkey
wrapRegisterHotkey() 'And create the new hotkey
WriteSettings() 'Update settings file
END IF
CASE %IDC_BUTTON_SHOW_NOW 'Show time now button
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
THREAD CREATE doPlay(dummy) TO dummyThread??? 'Create time display thread
THREAD CLOSE dummyThread??? TO res 'And dump the handle
END IF
CASE %IDC_BUTTON_BROWSE_CS 'Browsing for a new character set
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
sFileSpec$ = "" 'No default; next line opens the file
DISPLAY OPENFILE hCreatorDialog, , , "Please select a file to load from", EXE.PATH$, _
"All mouse movement sets (*.set)" & CHR$(0) & "*.set" & CHR$(0), sFileSpec$, "set", _
%OFN_FILEMUSTEXIST OR %OFN_PATHMUSTEXIST OR %OFN_ENABLESIZING OR %OFN_OVERWRITEPROMPT OR %OFN_CREATEPROMPT TO TempFilename$
TempFilename$ = TRIM$(TempFilename$) 'Clean file
IF DIR$(TempFilename$) <> "" THEN 'Valid file (it exists), we can open it
CONTROL SET TEXT CBHNDL, %IDC_TEXTBOX_CHARACTERSET, TempFilename$ 'Update the dialog with this valid filename
charsLoaded = LoadPoints(tempFilename$, pointsLoaded) 'Try and load the file, and then tell the user the results of the load
MSGBOX "Number of data sets of mouse motion digits loaded is " & FORMAT$(charsLoaded) + " with " & FORMAT$(pointsLoaded) & " individual coordinates", %MB_ICONINFORMATION, "Loaded Successfully"
WriteSettings() 'Update settings file
END IF
END IF
CASE %IDC_BUTTON_EDIT_CS 'Edit character set button
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
ShowCREATOR_DIALOG hMainDialog 'Open the character editor
END IF
CASE %IDC_TEXTBOX_PIXELHEIGHT 'Character pixel height box
IF CB.CTLMSG = %EN_KILLFOCUS THEN 'Has it just lost edit focus?
CONTROL GET TEXT CBHNDL, %IDC_TEXTBOX_PIXELHEIGHT TO temp$ 'Grab the value
IF VAL(temp$) <> MaxPixelHeight THEN 'Has the value changed from the current setting?
MaxPixelHeight = VAL(temp$) 'Grab the new value
IF MaxPixelHeight < 50 THEN 'Is it too small?
MaxPixelHeight = 50 'Set to minimum
ELSEIF MaxPixelHeight > 800 THEN 'Is it too big?
MaxPixelHeight = 800 'Set to maximum
END IF
temp$ = FORMAT$(MaxPixelHeight, "0") 'Convert to string
CONTROL SET TEXT CBHNDL, %IDC_TEXTBOX_PIXELHEIGHT, temp$ 'And write back to dialog
WriteSettings() 'Update settings file
END IF
END IF
CASE %IDC_CHECKBOX_LEAVE_MOUSE_TRAILS 'Mouse trails checkbox
IF CB.CTLMSG = %BN_CLICKED THEN 'Has it just been clicked on?
CONTROL GET CHECK CBHNDL, %IDC_CHECKBOX_LEAVE_MOUSE_TRAILS TO res 'Grab the current value
IF res <> %FALSE THEN res = %TRUE 'Massage for consistency
IF res <> LeaveMouseTrails THEN 'Have they changed the value?
LeaveMouseTrails = res 'Update setting
WriteSettings() 'Update settings file
END IF
END IF
CASE %IDC_TEXTBOX_SLOWDOWN 'Slowdown textbox
IF CB.CTLMSG = %EN_KILLFOCUS THEN 'Has it just lost edit focus?
CONTROL GET TEXT CBHNDL, %IDC_TEXTBOX_SLOWDOWN TO temp$ 'Grab the value
IF VAL(temp$) <> SlowdownFactor THEN 'Has the value changed from the current setting?
SlowdownFactor = VAL(temp$) 'Grab the new value
IF SlowdownFactor < 1 THEN 'Is it too fast?
SlowdownFactor = 1 'Set to minimum
ELSEIF SlowdownFactor > 200 THEN 'Is it too slow?
SlowdownFactor = 200 'Set to maximum
END IF
temp$ = FORMAT$(SlowdownFactor, "0") 'Convert to string
CONTROL SET TEXT CBHNDL, %IDC_TEXTBOX_SLOWDOWN, temp$ 'And write back to dialog
WriteSettings() 'Update settings file
END IF
END IF
CASE %IDC_COLORPICKER 'Line color picker box
IF CB.CTLMSG = %BN_CLICKED THEN
DISPLAY COLOR CBHNDL, 100, 100, 0, CustomColorList, 0 TO trailsColor 'Open color picker dialog
GRAPHIC ATTACH CBHNDL, %IDC_COLORPICKER 'Attach to the box
GRAPHIC CLEAR trailsColor 'Fill it with the selected color
GRAPHIC DETACH 'Detach from the box
WriteSettings() 'Save the settings
END IF
CASE %IDC_TEXTBOX_ACCELERATION 'Acceleration textbox
IF CB.CTLMSG = %EN_KILLFOCUS THEN 'Has it just lost edit focus?
CONTROL GET TEXT CBHNDL, %IDC_TEXTBOX_ACCELERATION TO temp$ 'Grab the value
IF VAL(temp$) <> AccelerationFactor THEN 'Has the value changed from the current setting?
AccelerationFactor = VAL(temp$) 'Grab the new value
IF AccelerationFactor < 1 THEN 'Is it too slow?
AccelerationFactor = 1 'Set to minimum
ELSEIF AccelerationFactor > 10 THEN 'Is it too fast?
AccelerationFactor = 10 'Set to maximum
END IF
temp$ = FORMAT$(AccelerationFactor, "0") 'Convert to string
CONTROL SET TEXT CBHNDL, %IDC_TEXTBOX_ACCELERATION, temp$ 'Write to dialog
WriteSettings() 'Update settings file
END IF
END IF
CASE %IDC_BUTTON_RESTORE_DEFAULTS 'Restore defaults button
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
SetProgramDefaults() 'Set to defaults
wrapUnregisterHotkey() 'Dump the hotkey
wrapRegisterHotkey() 'Turn the hotkey back on
WriteSettingsToScreen() 'Update the settings visually
WriteSettings() 'And save the defaults to disk
END IF
CASE %IDC_BUTTON_QUIT_PROGRAM 'Quit button
IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
DIALOG END CBHNDL 'Quit
END IF
END SELECT
END SELECT
END FUNCTION
FUNCTION ShowMAINDIALOG(BYVAL hParent AS DWORD) AS LONG
LOCAL lRslt AS LONG, hFont1 AS DWORD, ctr AS LONG, hInstance AS DOUBLE
LOCAL ClockstyleArray$(), ShortcutModifier$(),ShortcutKey$()
'Set up the contents of the combo boxes
DIM ClockstyleArray$(5)
ARRAY ASSIGN ClockstyleArray$() = "HH:MM AM (12hr)", "HH:MM (12hr)", "HHMM AM (12hr)", "HHMM (12hr)", "HH:MM (24hr)", "HHMM (24hr)"
DIM ShortcutModifier$(5)
ARRAY ASSIGN ShortcutModifier$() = "Ctrl", "Alt", "Ctrl+Alt", "Ctrl+Shift", "Alt+Shift", "Ctrl+Alt+Shift"
DIM ShortcutKey$(48)
FOR ctr = 0 TO 9
ShortcutKey$(ctr) = FORMAT$(ctr, "0")
NEXT
FOR ctr = 65 TO 90
ShortcutKey$(ctr - 55) = CHR$(ctr)
NEXT
FOR ctr = 1 TO 12
ShortcutKey$(ctr + 35) = "F" & FORMAT$(ctr)
NEXT
'Create the window
DIALOG NEW hParent, "The Ghost in the Machine Clock", 251, 116, 304, 137, %WS_POPUP OR %WS_BORDER OR %WS_DLGFRAME OR %WS_CAPTION OR _
%WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_CENTER OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _
%DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hMainDialog
'Add the interface
CONTROL ADD LABEL, hMainDialog, %IDC_LABEL_CLOCKSTYLE, "Clock style:", 5, 10, 60, 10, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT, %WS_EX_LEFT OR %WS_EX_LTRREADING
CONTROL ADD COMBOBOX, hMainDialog, %IDC_COMBOBOX_CLOCKSTYLE, ClockstyleArray$(), 70, 8, 75, 75, %CBS_DROPDOWNLIST OR %CBS_HASSTRINGS
CONTROL ADD LABEL, hMainDialog, %IDC_LABEL_CURRENTLYLOOKSLIKE, "Currently looks like:", 150, 10, 80, 10, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT, %WS_EX_LEFT OR %WS_EX_LTRREADING
CONTROL ADD LABEL, hMainDialog, %IDC_LABEL_CLOCKSAMPLE, "HH:MM AM", 235, 8, 60, 10, %WS_CHILD OR %WS_VISIBLE, %WS_EX_LEFT OR %WS_EX_LTRREADING
CONTROL ADD LABEL, hMainDialog, %IDC_LABEL_SHORTCUTKEY, "Shortcut key:", 5, 27, 60, 10, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT, %WS_EX_LEFT OR %WS_EX_LTRREADING
CONTROL ADD COMBOBOX, hMainDialog, %IDC_COMBOBOX_SHORTCUTKEY_MODIFIER, ShortcutModifier$(), 70, 25, 75, 75, %CBS_DROPDOWNLIST OR %CBS_HASSTRINGS
CONTROL ADD COMBOBOX, hMainDialog, %IDC_COMBOBOX_SHORTCUT_KEY, ShortcutKey$(), 150, 25, 40, 155, %CBS_DROPDOWNLIST OR %CBS_HASSTRINGS OR %WS_VSCROLL
CONTROL ADD BUTTON, hMainDialog, %IDC_BUTTON_SHOW_NOW, "Show Now", 235, 25, 60, 15
CONTROL ADD LABEL, hMainDialog, %IDC_LABEL_CHARACTERSET, "Character set:", 5, 47, 60, 10, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT, %WS_EX_LEFT OR %WS_EX_LTRREADING
CONTROL ADD TEXTBOX, hMainDialog, %IDC_TEXTBOX_CHARACTERSET, "", 70, 45, 100, 15, %ES_READONLY OR %WS_BORDER
CONTROL ADD BUTTON, hMainDialog, %IDC_BUTTON_BROWSE_CS, "Browse", 175, 45, 40, 15
CONTROL ADD BUTTON, hMainDialog, %IDC_BUTTON_EDIT_CS, "Edit", 220, 45, 35, 15
CONTROL ADD LABEL, hMainDialog, %IDC_LABEL_PIXELHEIGHT, "Max pixel height:", 5, 67, 60, 10, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT, %WS_EX_LEFT OR %WS_EX_LTRREADING
CONTROL ADD TEXTBOX, hMainDialog, %IDC_TEXTBOX_PIXELHEIGHT, "", 70, 65, 40, 15
CONTROL ADD CHECKBOX, hMainDialog, %IDC_CHECKBOX_LEAVE_MOUSE_TRAILS, "Leave mouse trails (OS dependent)", 120, 67, 130, 8
CONTROL ADD GRAPHIC, hMainDialog, %IDC_COLORPICKER, "", 250, 67, 30, 10, %WS_BORDER OR %SS_NOTIFY
CONTROL ADD LABEL, hMainDialog, %IDC_LABEL_SLOWDOWN, "Slowdown factor:", 5, 87, 60, 10, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT, %WS_EX_LEFT OR %WS_EX_LTRREADING
CONTROL ADD TEXTBOX, hMainDialog, %IDC_TEXTBOX_SLOWDOWN, "", 70, 85, 40, 15
CONTROL ADD LABEL, hMainDialog, %IDC_LABEL_ACCELERATION, "Acceleration factor:", 120, 87, 65, 8, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT, %WS_EX_LEFT OR %WS_EX_LTRREADING
CONTROL ADD TEXTBOX, hMainDialog, %IDC_TEXTBOX_ACCELERATION, "", 190, 85, 40, 15
CONTROL ADD LINE, hMainDialog, %IDC_LINE, "", 10, 109, 285, 1
CONTROL ADD BUTTON, hMainDialog, %IDC_BUTTON_RESTORE_DEFAULTS, "Reset to Defaults", 145, 115, 75, 15
CONTROL ADD BUTTON, hMainDialog, %IDC_BUTTON_QUIT_PROGRAM, "Kill Clock Ghost", 230, 115, 65, 15
'Set the font of the clock to slightly larger and bolder
FONT NEW "MS Sans Serif", 10, 1, %ANSI_CHARSET TO hFont1
CONTROL SET FONT hMainDialog, %IDC_LABEL_CLOCKSAMPLE, hFont1
SetProgramDefaults() 'Start up with settings at default
LoadSettings() 'Load the settings
wrapRegisterHotkey() 'Register the hotkey
WriteSettingsToScreen() 'Update the settings visually
'Set up the program icon
hInstance = GetModuleHandle("")
DIALOG SEND hMainDialog, %WM_SETICON, %ICON_BIG, LoadIcon(hInstance, "0L")
'And turn on interface
DIALOG SHOW MODAL hMainDialog, CALL ShowMAINDIALOGProc TO lRslt
FONT END hFont1
FUNCTION = lRslt
END FUNCTION
SUB WriteSettingsToScreen() 'Write all the settings to the dialog
LOCAL boxSize AS LONG, ctr AS LONG, temp$, tempFilename$, res AS LONG
LOCAL hNull AS LONG
LOCAL szName AS ASCIIZ * 40
LOCAL lRet1 AS LONG, lRet2 AS LONG
LOCAL dRet1 AS DWORD, dRet2 AS DWORD
LOCAL sBuff AS STRING
LOCAL ff AS LONG
'Set up the clock style combobox
COMBOBOX GET COUNT hMainDialog, %IDC_COMBOBOX_CLOCKSTYLE TO boxSize 'How many items on this list
FOR ctr = 1 TO boxSize 'Go through all the items
COMBOBOX GET TEXT hMainDialog, %IDC_COMBOBOX_CLOCKSTYLE, ctr TO temp$ 'Grab the text of this item
IF temp$ = ClockStyle$ THEN 'If it matches...
COMBOBOX SELECT hMainDialog, %IDC_COMBOBOX_CLOCKSTYLE, ctr ' ...select this item...
EXIT FOR ' ...and we're done
END IF
NEXT
'Set up the shortcut key modifier combobox
COMBOBOX GET COUNT hMainDialog, %IDC_COMBOBOX_SHORTCUTKEY_MODIFIER TO boxSize 'How many items on this list
FOR ctr = 1 TO boxSize 'Go through all the items
COMBOBOX GET TEXT hMainDialog, %IDC_COMBOBOX_SHORTCUTKEY_MODIFIER, ctr TO temp$ 'Grab the text of this item
IF temp$ = ShortcutKeyModifier$ THEN 'If it matches...
COMBOBOX SELECT hMainDialog, %IDC_COMBOBOX_SHORTCUTKEY_MODIFIER, ctr ' ...select this item...
EXIT FOR ' ...and we're done
END IF
NEXT
'Set up the shortcut key combobox
COMBOBOX GET COUNT hMainDialog, %IDC_COMBOBOX_SHORTCUT_KEY TO boxSize 'How many items on this list
FOR ctr = 1 TO boxSize 'Go through all the items
COMBOBOX GET TEXT hMainDialog, %IDC_COMBOBOX_SHORTCUT_KEY, ctr TO temp$ 'Grab the text of this item
IF temp$ = ShortcutKey$ THEN 'If it matches...
COMBOBOX SELECT hMainDialog, %IDC_COMBOBOX_SHORTCUT_KEY, ctr ' ...select this item...
EXIT FOR ' ...and we're done
END IF
NEXT
'Set color
GRAPHIC ATTACH hMainDialog, %IDC_COLORPICKER
GRAPHIC CLEAR trailsColor
GRAPHIC DETACH
'Deal with character set
IF CharacterSet$ = "Default.set" THEN 'We might need to extract the default file from the RC file
IF DIR$(EXE.PATH$ & "default.set") = "" THEN 'Default does not exist
hNull = 0 'Null = Current process
szName = "DEFAULTFONT" 'This is the name of our default font
lRet1 = FindResource(hNull, szName, BYVAL %RT_RCDATA)
IF ISFALSE lRet1 THEN 'Couldn't find the resource... Exe is damaged
MSGBOX "Could not find and create default font. Application may be damaged.", %MB_SYSTEMMODAL OR %MB_ICONERROR, "Error"
ELSE
dRet1 = SizeofResource(hNull,lRet1) 'How big is the installer file?
lRet2 = LoadResource(hNull,lRet1) 'Try and start loading it into memory
IF ISFALSE lRet2 THEN
MSGBOX "Could not load and create installer. Application may be damaged.", %MB_SYSTEMMODAL OR %MB_ICONERROR, "Error" 'This one is "load", the other is "find"
ELSE
dRet2 = LockResource(lRet2) 'Lock it down
sBuff = PEEK$(dRet2, dRet1) 'Now copy it from memory into the string
ff = FREEFILE 'Get the next available file handle
OPEN EXE.PATH$ & "default.set" FOR BINARY AS #ff 'Open the requested output file
PUT$ #ff, sBuff 'Spit the buffer out to file
CLOSE #ff 'Close the file
END IF
END IF
END IF
END IF
IF INSTR(CharacterSet$, "\") > 0 THEN 'Is it a long filename?
tempFilename$ = CharacterSet$
ELSE
tempFilename$ = EXE.PATH$ & CharacterSet$
END IF
res = LoadPoints(tempFilename$, ctr)
CONTROL SET TEXT hMainDialog, %IDC_TEXTBOX_CHARACTERSET, CharacterSet$
'Rest of settings are pretty basic
CONTROL SET TEXT hMainDialog, %IDC_TEXTBOX_PIXELHEIGHT, FORMAT$(MaxPixelHeight)
IF LeaveMouseTrails = %FALSE THEN
CONTROL SET CHECK hMainDialog, %IDC_CHECKBOX_LEAVE_MOUSE_TRAILS, %FALSE
ELSE
CONTROL SET CHECK hMainDialog, %IDC_CHECKBOX_LEAVE_MOUSE_TRAILS, %TRUE
END IF
CONTROL SET TEXT hMainDialog, %IDC_TEXTBOX_SLOWDOWN, FORMAT$(SlowdownFactor)
CONTROL SET TEXT hMainDialog, %IDC_TEXTBOX_ACCELERATION, FORMAT$(AccelerationFactor)
END SUB
SUB SetProgramDefaults() 'Set default of program
ClockStyle$ = "HH:MM (12hr)"
ShortcutKeyModifier$ = "Ctrl"
trailsColor = 0
ShortcutKey$ = "T"
CharacterSet$ = "Default.set"
MaxPixelHeight = 323
LeaveMouseTrails = %FALSE
SlowdownFactor = 5
AccelerationFactor = 2
END SUB
SUB WriteSettings() 'Write the INI file to disk
LOCAL ff AS LONG, IniFile$
IniFile$ = EXE.PATH$ & "gitm-clock.ini" 'This is the filename of the program's INI file
ff = FREEFILE
OPEN iniFile$ FOR OUTPUT AS #ff
PRINT #ff, "TrailsColor: " & FORMAT$(trailsColor)
PRINT #ff, "ClockStyle: " & ClockStyle$
PRINT #ff, "ShortcutKeyModifiers: " & ShortcutKeyModifier$
PRINT #ff, "ShortcutKey: " & ShortcutKey$
PRINT #ff, "CharacterSet: " & CharacterSet$
PRINT #ff, "MaxPixelHeight: " & FORMAT$(MaxPixelHeight)
PRINT #ff, "LeaveMouseTrails: " & FORMAT$(LeaveMouseTrails)
PRINT #ff, "SlowdownFactor: " & FORMAT$(SlowdownFactor)
PRINT #ff, "AccelerationFactor: " & FORMAT$(AccelerationFactor)
CLOSE #ff
END SUB
SUB LoadSettings() 'Load the INI file
LOCAL ff AS LONG, IniFile$, temp$, lineCmd$, lineData$
IniFile$ = EXE.PATH$ & "gitm-clock.ini" 'This is the filename of the program's INI file
IF DIR$(iniFile$) <> "" THEN 'We need the settings file to be able to load it
ff = FREEFILE
OPEN IniFile$ FOR INPUT AS #ff
DO UNTIL EOF(ff)
LINE INPUT #ff, temp$ 'This looks like "ShortCutKeyModifier: Ctrl" and so on
lineCmd$ = LCASE$(TRIM$(PARSE$(temp$, ":", 1))) 'This is the item being set
lineData$ = TRIM$(MID$(temp$, INSTR(temp$, ":") + 1)) 'This is the value it's being set to
IF lineData$ <> "" THEN
SELECT CASE lineCmd$ 'Assign the values from the ini file
CASE "trailscolor"
trailsColor = VAL(lineData$)
CASE "clockstyle"
ClockStyle$ = lineData$
CASE "shortcutkeymodifier"
ShortcutKeyModifier$ = lineData$
CASE "shortcutkey"
ShortcutKey$ = lineData$
CASE "characterset"
CharacterSet$ = lineData$
CASE "maxpixelheight"
MaxPixelHeight = VAL(lineData$)
CASE "leavemousetrails"
LeaveMouseTrails = VAL(lineData$)
CASE "slowdownfactor"
SlowdownFactor = VAL(lineData$)
CASE "accelerationfactor"
AccelerationFactor = VAL(lineData$)
END SELECT
END IF
LOOP
CLOSE #ff
END IF
END SUB
SUB UpdateShowButtons() 'Update the show buttons with bold/non-bold on the creator form to represent if they have data
LOCAL hFont1 AS DWORD, ctr AS LONG
FOR ctr = 0 TO 12 'Set fonts of show buttons to indicate whether they exist or not
IF TRIM$(pointList$(ctr)) = "" THEN 'No pointlist = regular font
hFont1 = PBFormsMakeFont("MS Sans Serif", 8, 400, %FALSE, %FALSE, %FALSE, %ANSI_CHARSET)
ELSE 'Has pointlist = bold font
hFont1 = PBFormsMakeFont("MS Sans Serif", 10, 700, %FALSE, %FALSE, %FALSE, %ANSI_CHARSET)
END IF
CONTROL SEND hCreatorDialog, %IDC_SHOW_0 + ctr, %WM_SETFONT, hFont1, 0
CONTROL REDRAW hCreatorDialog, %IDC_SHOW_0 + ctr
NEXT
END SUB
FUNCTION LoadPoints(pointFilename$, pointsLoaded AS LONG) AS LONG 'Load a point set, return the number of characers and points loaded
LOCAL ff AS LONG, ctr AS LONG, charsLoaded AS LONG
pointsLoaded = 0
IF DIR$(pointFilename$) = "" THEN 'File not found
FUNCTION = 0
ELSE
ff = FREEFILE
OPEN pointFilename$ FOR INPUT AS #ff 'Open found file
FOR ctr = 0 TO 12 'Go through all the points
IF NOT(EOF(ff)) THEN '(Avoiding damaged files)
LINE INPUT #ff, pointList$(ctr) 'And load them sequentially
ELSE
pointList$(ctr) = "" 'Should never happen
END IF
NEXT
CLOSE #ff
charsLoaded = 0
FOR ctr = 0 TO 12 'Set fonts of buttons to indicate whether they exist or not
IF TRIM$(pointList$(ctr)) <> "" THEN
pointsLoaded = pointsLoaded + (PARSECOUNT(pointList(ctr)) \ 2) 'How many points does this character have?
INCR charsLoaded 'This character has data
END IF
NEXT
FUNCTION = charsLoaded
END IF
END FUNCTION
FUNCTION GetCurrentTime$() 'Return the current time, in the current clock style
LOCAL HH AS LONG, MM AS LONG, HH12 AS LONG, AMPM$, renderTime$
'Get the basic data that we need for building all the clock types
HH = VAL(MID$(TIME$, 1, 2)) 'Get the hour (24-hour time)
MM = VAL(MID$(TIME$, 4, 2)) 'Get the minutes
HH12 = HH
IF HH12 > 12 THEN
HH12 = HH12 - 12 'Get the hour (12-hour time)
AMPM$ = "PM"
ELSE
IF HH12 = 12 THEN
AMPM$ = "PM"
ELSE
AMPM$ = "AM"
END IF
IF HH12 = 0 THEN
HH12 = 12
END IF
END IF
'Using that data, create the clock string
SELECT CASE ClockStyle$
CASE "HH:MM AM (12hr)"
renderTime$ = FORMAT$(HH12, "0") & ":" & FORMAT$(MM, "00") & " " & AMPM$
CASE "HH:MM (12hr)"
renderTime$ = FORMAT$(HH12, "0") & ":" & FORMAT$(MM, "00")
CASE "HHMM AM (12hr)"
renderTime$ = FORMAT$(HH12, "0") & FORMAT$(MM, "00") & " " & AMPM$
CASE "HHMM (12hr)"
renderTime$ = FORMAT$(HH12, "0") & FORMAT$(MM, "00")
CASE "HH:MM (24hr)"
renderTime$ = FORMAT$(HH, "0") & ":" & FORMAT$(MM, "00")
CASE "HHMM (24hr)"
renderTime$ = FORMAT$(HH, "0") & FORMAT$(MM, "00")
END SELECT
FUNCTION = renderTime$
END FUNCTION
SUB wrapRegisterHotkey() 'Register the hotkey for the program
LOCAL res AS LONG, theMod AS LONG, theKey AS LONG
'Deal with the control/alt/shift keys
theMod = 0
IF INSTR(ShortcutKeyModifier$, "Ctrl") > 0 THEN
theMod = theMod OR %HOTKEYF_CONTROL
END IF
IF INSTR(ShortcutKeyModifier$, "Alt") > 0 THEN
theMod = theMod OR %HOTKEYF_ALT
END IF
IF INSTR(ShortcutKeyModifier$, "Shift") > 0 THEN
theMod = theMod OR %HOTKEYF_SHIFT
END IF
'Now deal with the main key
SELECT CASE ShortcutKey$
CASE "0"
theKey = %VK_0
CASE "1"
theKey = %VK_1
CASE "2"
theKey = %VK_2
CASE "3"
theKey = %VK_3
CASE "4"
theKey = %VK_4
CASE "5"
theKey = %VK_5
CASE "6"
theKey = %VK_6
CASE "7"
theKey = %VK_7
CASE "8"
theKey = %VK_8
CASE "9"
theKey = %VK_9
CASE "A"
theKey = %VK_A
CASE "B"
theKey = %VK_B
CASE "C"
theKey = %VK_C
CASE "D"
theKey = %VK_D
CASE "E"
theKey = %VK_E
CASE "F"
theKey = %VK_F
CASE "G"
theKey = %VK_G
CASE "H"
theKey = %VK_H
CASE "I"
theKey = %VK_I
CASE "J"
theKey = %VK_J
CASE "K"
theKey = %VK_K
CASE "L"
theKey = %VK_L
CASE "M"
theKey = %VK_M
CASE "N"
theKey = %VK_N
CASE "O"
theKey = %VK_O
CASE "P"
theKey = %VK_P
CASE "Q"
theKey = %VK_Q
CASE "R"
theKey = %VK_R
CASE "S"
theKey = %VK_S
CASE "T"
theKey = %VK_T
CASE "U"
theKey = %VK_U
CASE "V"
theKey = %VK_V
CASE "W"
theKey = %VK_W
CASE "X"
theKey = %VK_X
CASE "Y"
theKey = %VK_Y
CASE "Z"
theKey = %VK_Z
CASE "F1"
theKey = %VK_F1
CASE "F2"
theKey = %VK_F2
CASE "F3"
theKey = %VK_F3
CASE "F4"
theKey = %VK_F4
CASE "F5"
theKey = %VK_F5
CASE "F6"
theKey = %VK_F6
CASE "F7"
theKey = %VK_F7
CASE "F8"
theKey = %VK_F8
CASE "F9"
theKey = %VK_F9
CASE "F10"
theKey = %VK_F10
CASE "F11"
theKey = %VK_F11
CASE "F12"
theKey = %VK_F12
END SELECT
'And set it
res = RegisterHotKey(hMainDialog, %IDC_HOTKEY, theMod, theKey)
IF res = 0 THEN
MSGBOX "Error: I could not register your selected hotkey " & ShortcutKeyModifier$ & "+" & ShortcutKey$ & " -- it may already be in use. Try another key combination.", %MB_SYSTEMMODAL OR %MB_ICONERROR, "Error"
END IF
END SUB
SUB wrapUnregisterHotkey() 'Free the hotkey
UnregisterHotkey hMainDialog, %IDC_HOTKEY
END SUB
SUB CalculateWorkspace(Digits AS LONG, canvasX AS LONG, canvasY AS LONG, canvasW AS LONG, canvasH AS LONG, charW AS LONG, charH AS LONG, ratio AS DOUBLE) 'Calculate the size of characters and canvas
'Digits is the number of characters long the string to be written is
'canvasX, canvasY is the screen coordinate of the top-left corner of the canvas required
'canvasW, canvasH is the size of the canvas required
'charW, charH is the size of the individual characters
'ratio is the scaling ratio from the original point size to the new one
LOCAL screenW AS LONG, screenH AS LONG
ratio = MaxPixelHeight / 323 'Sizing ratio in comparison to normal
charW = 230 * ratio 'Size of scaled character
charH = MaxPixelHeight
canvasW = charW * Digits 'Size of total canvas
canvasH = charH
DESKTOP GET CLIENT TO screenW, screenH 'Screen size, excluding toolbars
canvasX = (screenW \ 2) - (canvasW \ 2) 'Size of canvas, centered on screen
canvasY = (screenH \ 2) - (canvasH \ 2)
theCanvasX = canvasX
theCanvasY = canvasY
END SUB
FUNCTION doPlay(BYVAL dummy AS DWORD) AS DWORD 'Draw the time
LOCAL x AS INTEGER, y AS INTEGER, ctrInt AS INTEGER, res AS LONG, ctr AS LONG, timeStr$, thisDigit AS LONG
LOCAL canvasX AS LONG, canvasY AS LONG, canvasW AS LONG, canvasH AS LONG, charW AS LONG, charH AS LONG, ratio AS DOUBLE
LOCAL drawX AS LONG, drawY AS LONG, hScreenCap AS DWORD, lineIt AS LONG
LOCAL hDlg AS DWORD
LOCAL lRslt AS LONG
LOCAL Ret AS LONG
timeStr$ = REMOVE$(GetCurrentTime$(), ANY " M") 'We don't need the space at the end, nor the "M" in AM/PM
CalculateWorkspace LEN(timeStr$), canvasX, canvasY, canvasW, canvasH, charW, charH, ratio 'Where are we working, and how big?
lineIt = 0
'If we're drawing mousetrails
IF LeaveMouseTrails <> %FALSE THEN
'Take a screen cap
CLIPBOARD RESET 'Clear clipboard
keybd_event(%VK_SnapShot, 0, 0, 0) 'This copies the screen to the clipboard
SLEEP 500 'Wait for it to be copied
CLIPBOARD GET BITMAP TO hScreenCap, res 'Grab the clipboard's bitmap
IF res <> 0 THEN 'We successfully got the screencap
lineIt = 1
'Open a window with no border or caption that matches the canvas we just got info on
DIALOG NEW PIXELS, %HWND_DESKTOP, "1", canvasX, canvasY, canvasW, canvasH, %WS_POPUP OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _
%DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
CONTROL ADD GRAPHIC, hDlg, %IDC_GRAPHIC_FAKE, "", 0, 0, canvasW, canvasH
'Set the window's transparency to 50%
Ret = GetWindowLong(hDlg, %GWL_EXSTYLE)
Ret = Ret OR %WS_EX_LAYERED
SetWindowLong hDlg, %GWL_EXSTYLE, Ret
SetLayeredWindowAttributes hDlg, 0, 128, %LWA_ALPHA '0=fully transparent, 255 = opaque
'Paste the captured background into this window using GRAPHIC COPY
GRAPHIC ATTACH hDlg, %IDC_GRAPHIC_FAKE
GRAPHIC COPY hScreenCap, 0, (canvasX + 3, canvasY)-(canvasX + 3 - canvasW, canvasY + canvasH) TO (0, 0)
GRAPHIC REDRAW
GRAPHIC BITMAP END 'Throw away the bitmap we no longer need
'Turn on dialog
DIALOG SHOW MODELESS hDlg
END IF
END IF
MOUSEPTR 1 'normal cursor
'Go through the digits one by one
FOR ctr = 1 TO LEN(timeStr$)
drawX = canvasX + (charW * (ctr - 1)) 'Where are we drawing this one?
drawY = canvasY
SELECT CASE MID$(timeStr$, ctr, 1) 'What are we drawing this time?
CASE "0" TO "9"
thisDigit = VAL(MID$(timeStr$, ctr, 1))
CASE ":"
thisDigit = 10
CASE "A"
thisDigit = 11
CASE "P"
thisDigit = 12
CASE ELSE
END SELECT
res = DrawDigit(thisDigit, drawX, drawY, lineIt, ratio) 'Now draw it
SLEEP 200
NEXT
'Turn off window if we're done
IF LeaveMouseTrails <> %FALSE THEN
Ret = GetWindowLong(hDlg, %GWL_EXSTYLE)
Ret = Ret OR %WS_EX_LAYERED
SetWindowLong hDlg, %GWL_EXSTYLE, Ret
FOR ctr = 128 TO 0 STEP -8 'Fade out the window
SetLayeredWindowAttributes hDlg, 0, ctr, %LWA_ALPHA '0=fully transparent, 255 = opaque
SLEEP 50
NEXT
DIALOG END hDlg
END IF
END FUNCTION
FUNCTION DrawDigit(whatDigit AS LONG, whereX AS LONG, whereY AS LONG, lineIt AS LONG, OPTIONAL ratio AS DOUBLE) AS LONG 'Using the mouse, draw a digit
'whatDigit = 0-9, 10, 11, 12
'whereX, whereY = top left screen co-ord
'ratio = scaling variable
LOCAL thisDataset$, numPoints AS LONG, curPointX AS LONG, curPointY AS LONG, ctr AS LONG, ctr2 AS LONG
LOCAL stepX AS DOUBLE, stepY AS DOUBLE, thisPointX AS LONG, thisPointY AS LONG, numSteps2 AS LONG
IF ISMISSING(ratio) THEN 'If ratio is not set, draw at full size
ratio = 1
END IF
numPoints = PARSECOUNT(pointList$(whatDigit)) 'How many data points are there?
IF lineIt = %TRUE THEN 'Are we drawing lines (ie. mouse trails)?
thisPointX = (VAL(PARSE$(pointList$(whatDigit), 1)) * ratio) + whereX 'Figure out start point
thisPointY = (VAL(PARSE$(pointList$(whatDigit), 2)) * ratio) + whereY
GRAPHIC COLOR trailsColor 'Set the color
GRAPHIC WIDTH 5 'Draw fat lines
GRAPHIC SET PIXEL (thisPointX - thecanvasX, thisPointY - thecanvasY) 'Set start point
END IF
FOR ctr = 1 TO numPoints STEP (AccelerationFactor * 2) 'Go through all the points, at the requested speed
thisPointX = (VAL(PARSE$(pointList$(whatDigit), ctr)) * ratio) + whereX 'Where is this point?
thisPointY = (VAL(PARSE$(pointList$(whatDigit), ctr + 1)) * ratio) + whereY
IF lineIt = %TRUE THEN
GRAPHIC LINE STEP - (thisPointX - thecanvasX, thisPointY - thecanvasY) 'Draw a line to it if needed
END IF
SetCursorPos thisPointX, thisPointY 'Move mouse cursor to here
SLEEP SlowdownFactor 'Pause as needed
NEXT
FUNCTION = %TRUE
END FUNCTION
RC file:
Code:
0L ICON "GITM-CLOCK.ICO"
DEFAULTFONT RCDATA DISCARDABLE "default.set"
Note that the RC file is intended to include the default font data, so that if a user installs the program it will automatically extract that file for them so they have a character set.
--Note: updated source code to fix a typo, add "fade out" of trails, and fix mouse cursor in trail mode
Last edited by Shannon David Larratt; Dec 30th, 2009 at 03:23 PM.
|