diff --git a/README.md b/README.md index 5c16a6c..ddbd8d6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # excel-pomodoro-timer -Excel Pomodoro Timer for Windows +Excel Pomodoro Timer for Windows and Mac This Project offers a simple Excel Timer for the Pomodoro Technique. More details: https://en.wikipedia.org/wiki/Pomodoro_Technique diff --git a/src/Pomodoro_Timer.xlsb/API_AlwaysOnTop.bas b/src/Pomodoro_Timer.xlsb/API_AlwaysOnTop.bas index 9c15291..421d63a 100644 --- a/src/Pomodoro_Timer.xlsb/API_AlwaysOnTop.bas +++ b/src/Pomodoro_Timer.xlsb/API_AlwaysOnTop.bas @@ -1,6 +1,6 @@ Attribute VB_Name = "API_AlwaysOnTop" -'PURPOSE: This module includes the functions used to make sure that the Timer stays on top of all windows. 'REFERENCE: https://www.mrexcel.com/forum/excel-questions/386643-userform-always-top-2.html +'PURPOSE: This module includes the functions used to make sure that the Timer stays on top of all windows. Option Explicit @@ -17,8 +17,11 @@ End Enum 'https://msdn.microsoft.com/en-us/library/office/gg264421.aspx '64-Bit Visual Basic for Applications Overview +'See also: https://sysmod.wordpress.com/2016/09/03/conditional-compilation-vba-excel-macwin3264/ +'For Mac declarations -#If VBA7 Then + +#If VBA7 Then ' Excel 2010 or later for Windows 'VBA version 7 compiler, therefore >= Office 2010 'PtrSafe means function works in 32-bit and 64-bit Office @@ -27,7 +30,7 @@ End Enum Public Declare PtrSafe Function SetWindowPos Lib "user32" _ (ByVal hWnd As LongPtr, _ ByVal hWndInsertAfter As LongPtr, _ - ByVal x As Long, _ + ByVal X As Long, _ ByVal Y As Long, _ ByVal cx As Long, _ ByVal cy As Long, _ @@ -37,8 +40,7 @@ End Enum (ByVal lpClassName As String, _ ByVal lpWindowName As String) As LongPtr -#Else - +#Else ' pre Excel 2010 for Windows 'VBA version 6 or earlier compiler, therefore <= Office 2007 Public Declare Function SetWindowPos Lib "user32" _ @@ -55,3 +57,6 @@ End Enum ByVal lpWindowName As String) As Long #End If + + + diff --git a/src/Pomodoro_Timer.xlsb/API_Sleep.bas b/src/Pomodoro_Timer.xlsb/API_Sleep.bas index ddb43aa..1f4806f 100644 --- a/src/Pomodoro_Timer.xlsb/API_Sleep.bas +++ b/src/Pomodoro_Timer.xlsb/API_Sleep.bas @@ -3,12 +3,17 @@ Attribute VB_Name = "API_Sleep" Option Explicit -#If VBA7 Then +#If VBA7 Then ' Excel 2010 or later for Windows + Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For 64 Bit Systems -#Else + +#Else ' pre Excel 2010 for Windows + Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For 32 Bit Systems + #End If + Sub SleepTest() 'MsgBox "Execution is started" Sleep 10000 'delay in milliseconds diff --git a/src/Pomodoro_Timer.xlsb/CountDown_mac.bas b/src/Pomodoro_Timer.xlsb/CountDown_mac.bas new file mode 100644 index 0000000..5ed66d3 --- /dev/null +++ b/src/Pomodoro_Timer.xlsb/CountDown_mac.bas @@ -0,0 +1,205 @@ +Attribute VB_Name = "CountDown_mac" +Option Explicit + +Const FREQ = 1 + +Sub Launch_timer_mac() + 'Stop the code if the form is not visible + If UFIsVisible = False Then: Debug.Print "Form is not visible. The code will now stop.": End + + Dim frm As UserForm + Set frm = PomodoroTimer + Call Optimize_VBA_Performance(True) + + OngoingTimer = True + StopTimer = False + CloseTimer = False + frm.CommandButton2.caption = "Cancel" + + 'Reset the colors + PomodoroTimer.BackColor = -2147483633 + frm.TextBox2.BackColor = -2147483633 + frm.tBx1.BackColor = -2147483633 + + StartTime = Now() + TodaysDate = Date + + + Dim M As Double, S As Double + Dim TotalTime + Dim EndTime As Double + Dim RemaingTime As Double + + TotalTime = 60 * AllowedTime + AllowedTimeSec + EndTime = DateAdd("s", TotalTime, Now()) + RemaingTime = DateDiff("s", Now(), EndTime) + + RemaingTime = DateDiff("s", Now(), EndTime) + M = Int(RemaingTime / 60) + S = RemaingTime - 60 * M + + With frm.tBx1 + .Value = Format(CStr(M), "00") & ":" & Format(CStr(S), "00") + End With + + 'Released the control to the OS + 'DoEvents + + 'Now "sleep" + Application.OnTime Now + TimeValue("00:00:01") * FREQ, "Launch_timer_mac2" + +End Sub + +Sub Launch_timer_mac2() + Dim frm As UserForm + Set frm = PomodoroTimer + + Dim M As Double, S As Double + Dim TotalTime + Dim EllapsedtTime + Dim StartTime As Double + Dim EndTime As Double + Dim RemaingTime As Double + + TotalTime = 60 * AllowedTime + AllowedTimeSec +' EllapsedtTime = TotalTime - (60 * Split(frm.tBx1.Value, ":")(0) + 1 * Split(frm.tBx1.Value, ":")(1)) +' M = Int(EllapsedtTime / 60) +' S = EllapsedtTime - 60 * M +' EndTime = DateAdd("s", TotalTime, Now()) +' StartTime = Now() - TimeValue("00:" & Format(CStr(M), "00") & ":" & Format(CStr(S), "00")) + RemaingTime = 60 * Split(frm.tBx1.Value, ":")(0) + 1 * Split(frm.tBx1.Value, ":")(1) + + If RemaingTime > 0 And Not StopTimer Then + RemaingTime = RemaingTime - FREQ + M = Int(RemaingTime / 60) + S = RemaingTime - 60 * M + + With frm.tBx1 + .Value = Format(CStr(M), "00") & ":" & Format(CStr(S), "00") + End With + + 'Released the control to the OS + 'DoEvents + + 'Now "sleep" + Application.OnTime Now + TimeValue("00:00:01") * FREQ, "Launch_timer_mac2" + + Else + + 'Since we are using the "Application.ontime" technique, it is possible that some public variables will have lost their values + If TodaysDate = 0 Then TodaysDate = Date + If StartTime = 0 Then + EllapsedtTime = TotalTime - RemaingTime + M = Int(EllapsedtTime / 60) + S = EllapsedtTime - 60 * M + StartTime = Now() - TimeValue("00:" & Format(CStr(M), "00") & ":" & Format(CStr(S), "00")) + End If + + 'Recording session + If StopTimer = False Or ThisWorkbook.Sheets("Settings").Range("Record_unfinished").Value2 = True Then + If (TotalTime - RemaingTime) / 60 > ThisWorkbook.Sheets("Settings").Range("No_Recording_limit") Then + Call Add_new_record(TodaysDate, StartTime, Now, Not (StopTimer), Range("TaskNameRng")) + End If + End If + + Call Optimize_VBA_Performance(False, xlAutomatic) + + If StopTimer = False Then 'If the timer was stopped by the user + 'Proceed with the Break + If ThisWorkbook.Sheets("Settings").Range("Sound_end_Pomodoro") = True Then Beep + frm.TextBox2.Value = "Break" + Call TakeBreak_mac + Else + 'Do nothing + frm.CommandButton2.caption = "Start" + OngoingTimer = False + End If + + If CloseTimer Then Unload frm + End If +End Sub + +Private Sub TakeBreak_mac() + Dim frm As UserForm + Set frm = PomodoroTimer + 'Reset StopTimer: + StopTimer = False + + Call Optimize_VBA_Performance(True) + + Dim M As Double, S As Double + M = BreakTime + S = BreakTimeSec + + With frm.tBx1 + .Value = Format(CStr(M), "00") & ":" & Format(CStr(S), "00") + End With + + Call TakeBreak_mac2 + +End Sub + +Private Sub TakeBreak_mac2() + Dim frm As UserForm + Set frm = PomodoroTimer + Dim M As Long, S As Long + Dim EndTime As Double + Dim RemaingTime As Double + Dim TotalTime As Long + + TotalTime = 60 * BreakTime + BreakTimeSec + RemaingTime = 60 * Split(frm.tBx1.Value, ":")(0) + 1 * Split(frm.tBx1.Value, ":")(1) + + If RemaingTime > 0 And Not StopTimer Then + RemaingTime = RemaingTime - FREQ + M = Int(RemaingTime / 60) + S = RemaingTime - 60 * M + + 'Flashing + If TotalTime - RemaingTime < 9 Then + If S Mod 2 = 1 Then + frm.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color + frm.TextBox2.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color + frm.tBx1.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color + Else + frm.BackColor = -2147483633 'Normal color + frm.TextBox2.BackColor = -2147483633 'Normal color + frm.tBx1.BackColor = -2147483633 'Normal color + End If + End If + + With frm.tBx1 + .Value = Format(CStr(M), "00") & ":" & Format(CStr(S), "00") + End With + 'Released the control to the OS + 'DoEvents + 'Now "sleep" + Application.OnTime Now + TimeValue("00:00:01") * FREQ, "TakeBreak_mac2" + + Else + + If StopTimer = False Then + If ThisWorkbook.Sheets("Settings").Range("Sound_end_Break") = True Then Beep + 'Remain in color to get the user's attention + frm.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color + frm.TextBox2.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color + frm.tBx1.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color + Else + frm.BackColor = -2147483633 'Normal color + frm.TextBox2.BackColor = -2147483633 'Normal color + frm.tBx1.BackColor = -2147483633 'Normal color + End If + frm.TextBox2.Value = "" + frm.CommandButton2.caption = "Start" + OngoingTimer = False + + 'Redo basic calculations form the initialize macro + M = Int(AllowedTime) + S = (AllowedTime - Int(AllowedTime)) * 60 + With frm.tBx1 + .Value = Format(CStr(M), "00") & ":" & Format(CStr(S), "00") + End With + + Call Optimize_VBA_Performance(False, xlAutomatic) + End If +End Sub diff --git a/src/Pomodoro_Timer.xlsb/Main.bas b/src/Pomodoro_Timer.xlsb/Main.bas index 2896516..9c50576 100644 --- a/src/Pomodoro_Timer.xlsb/Main.bas +++ b/src/Pomodoro_Timer.xlsb/Main.bas @@ -13,6 +13,7 @@ Public CloseTimer As Boolean 'User clicked the X Public OngoingTimer As Boolean 'Take the value true after the timer has started (was initialized) Public StartTime As Variant Public TodaysDate As Variant +Public UFIsVisible As Boolean Sub PomodoroSession() AllowedTime = Range("Pomodoro") @@ -20,10 +21,12 @@ Sub PomodoroSession() BreakTime = Range("Break") BreakTimeSec = Range("Break_sec") AutoLaunch = True - If Range("Run_in_seperate_instance").Value = True And Reopen_decision = True Then - MsgBox "To let you work with Excel while the timer is running, this file will now be reopened in a second instance of Excel." & vbNewLine & _ - "Once it was has been reopened, you will need to relaunch the timer." - Call OpenItSelfInAnotherInstance + If Not IsMac Then + If Range("Run_in_seperate_instance").Value = True And Reopen_decision = True Then + MsgBox "To let you work with Excel while the timer is Reopen_decisionrunning, this file will now be reopen in a second instance of Excel." & vbNewLine & _ + "Once, the file has been reopened, you will need to relaunch the timer." + Call OpenItSelfInAnotherInstance + End If End If ThisWorkbook.Application.WindowState = xlMinimized PomodoroTimer.Show vbModeless diff --git a/src/Pomodoro_Timer.xlsb/PomodoroTimer.frm b/src/Pomodoro_Timer.xlsb/PomodoroTimer.frm index ba6482f..0683293 100644 --- a/src/Pomodoro_Timer.xlsb/PomodoroTimer.frm +++ b/src/Pomodoro_Timer.xlsb/PomodoroTimer.frm @@ -21,13 +21,15 @@ Option Explicit Const sleeptime = 10 'Miliseconds Private Sub UserForm_Initialize() - + UFIsVisible = True 'Position of the Userform - Me.StartUpPosition = 0 - If Range("Custom_position") = True Then - Me.Top = Range("Top_pos").Value2 * (PointPerPixelY() * GETWORKAREA_HEIGHT - Me.Height) - Me.Left = Range("Left_pos").Value2 * (PointPerPixelX() * GETWORKAREA_WIDTH - Me.Width) - Else + If ThisWorkbook.Sheets("Settings").Range("Custom_position") = True And Not IsMac Then + Me.StartUpPosition = 0 + Me.Top = ThisWorkbook.Sheets("Settings").Range("Top_pos").Value2 * (PointPerPixelY() * GETWORKAREA_HEIGHT - Me.Height) + Me.Left = ThisWorkbook.Sheets("Settings").Range("Left_pos").Value2 * (PointPerPixelX() * GETWORKAREA_WIDTH - Me.Width) + ElseIf Not IsMac Then + 'Reposition the window + Me.StartUpPosition = 0 Me.Top = PointPerPixelY() * GETWORKAREA_HEIGHT - Me.Height Me.Left = PointPerPixelX() * GETWORKAREA_WIDTH - Me.Width End If @@ -44,16 +46,29 @@ Private Sub UserForm_Initialize() 'The code below makes sure that the userform stays on top of all windows. 'Source: https://www.mrexcel.com/forum/excel-questions/386643-userform-always-top.html - - AlwaysOnTop Me.caption - + If Not IsMac Then + AlwaysOnTop Me.caption + End If + + If AutoLaunch Then + If IsMac Then + Call Launch_timer_mac + End If + End If End Sub Private Sub UserForm_Activate() - If AutoLaunch Then Call Launch_timer + If AutoLaunch Then + If Not IsMac Then + Call Launch_timer + End If + End If End Sub Private Sub Launch_timer() + 'Stop the code if the form is not visible + If UFIsVisible = False Then: Debug.Print "Form is not visible. The code will now stop.": End + Dim calc_iniset As Variant: calc_iniset = Application.Calculation Call Optimize_VBA_Performance(True) @@ -101,7 +116,7 @@ Private Sub Launch_timer() 'Recording session If StopTimer = False Or ThisWorkbook.Sheets("Settings").Range("Record_unfinished").Value2 = True Then If (TotalTime - RemaingTime) / 60 > ThisWorkbook.Sheets("Settings").Range("No_Recording_limit") Then - Call Add_new_record(TodaysDate, StartTime, Now, Not (StopTimer), Range("TaskNameRng")) + Call Add_new_record(TodaysDate, StartTime, Now, Not (StopTimer), ThisWorkbook.Sheets("Pomodoro").Range("TaskNameRng")) End If End If @@ -162,9 +177,9 @@ Private Sub TakeBreak2() 'Flashing If TotalTime - RemaingTime < 9 Then If S Mod 2 = 1 Then - PomodoroTimer.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color - TextBox2.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color - tBx1.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color + PomodoroTimer.BackColor = GetRGBColor_Fill(ThisWorkbook.Sheets("Settings").Range("Flashing_color")) 'Flashing color + TextBox2.BackColor = GetRGBColor_Fill(ThisWorkbook.Sheets("Settings").Range("Flashing_color")) 'Flashing color + tBx1.BackColor = GetRGBColor_Fill(ThisWorkbook.Sheets("Settings").Range("Flashing_color")) 'Flashing color Else PomodoroTimer.BackColor = -2147483633 'Normal color TextBox2.BackColor = -2147483633 'Normal color @@ -184,9 +199,9 @@ Private Sub TakeBreak2() If StopTimer = False Then If ThisWorkbook.Sheets("Settings").Range("Sound_end_Break") = True Then Beep 'Remain in color to get the user's attention - PomodoroTimer.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color - TextBox2.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color - tBx1.BackColor = GetRGBColor_Fill(Range("Flashing_color")) 'Flashing color + PomodoroTimer.BackColor = GetRGBColor_Fill(ThisWorkbook.Sheets("Settings").Range("Flashing_color")) 'Flashing color + TextBox2.BackColor = GetRGBColor_Fill(ThisWorkbook.Sheets("Settings").Range("Flashing_color")) 'Flashing color + tBx1.BackColor = GetRGBColor_Fill(ThisWorkbook.Sheets("Settings").Range("Flashing_color")) 'Flashing color Else PomodoroTimer.BackColor = -2147483633 'Normal color TextBox2.BackColor = -2147483633 'Normal color @@ -209,8 +224,14 @@ End Sub Private Sub CommandButton2_Click() If OngoingTimer = False Then 'Start the timer + UFIsVisible = True 'The form must be visible + ThisWorkbook.Application.WindowState = xlMinimized CommandButton2.caption = "Cancel" - Call Launch_timer + If Not IsMac Then + Call Launch_timer + Else + Call Launch_timer_mac + End If Else 'Stop the timer StopTimer = True OngoingTimer = False @@ -232,7 +253,7 @@ Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) 'At this point, since the user clicked on the userform to close it. Excel is the active window, but it might not be on top. 'Make Excel the active window (optional) On Error Resume Next - If ThisWorkbook.Sheets("Settings").Range("Reopen_Excel_after_x").Value2 = True Then + If ThisWorkbook.Sheets("Settings").Range("Reopen_Excel_after_x").Value2 = True And Not IsMac Then Call AppActivate(Wkb.Application.caption, True) ShowWindow GetForegroundWindow, SW_SHOWMAXIMIZED End If @@ -269,3 +290,7 @@ Private Sub AlwaysOnTop(caption As String) End If End Sub + +Private Sub UserForm_Terminate() + UFIsVisible = False +End Sub diff --git a/src/Pomodoro_Timer.xlsb/PomodoroTimer.frx b/src/Pomodoro_Timer.xlsb/PomodoroTimer.frx index ae7dcfe..d6455b7 100644 Binary files a/src/Pomodoro_Timer.xlsb/PomodoroTimer.frx and b/src/Pomodoro_Timer.xlsb/PomodoroTimer.frx differ diff --git a/src/Pomodoro_Timer.xlsb/Records.bas b/src/Pomodoro_Timer.xlsb/Records.bas index 0f8d830..2614cd7 100644 --- a/src/Pomodoro_Timer.xlsb/Records.bas +++ b/src/Pomodoro_Timer.xlsb/Records.bas @@ -14,7 +14,7 @@ End Sub Sub new_record_test() -Call Add_new_record(Date, Now, Now, True, "TaskName") + Call Add_new_record(Date, Now, Now, True, "TaskName") End Sub @@ -52,12 +52,12 @@ End Sub Sub Add_task(ByVal TaskName As String) - Dim x As Variant + Dim X As Variant On Error Resume Next - x = Application.Match(TaskName, Range("Recent_Tasks").Value2, 0) + X = Application.Match(TaskName, Range("Recent_Tasks").Value2, 0) On Error GoTo 0 - If IsError(x) Then + If IsError(X) Then Sheets("Recent").Cells(LastCell_row(Sheets("Recent")) + 1, 1).Value2 = TaskName End If diff --git a/src/Pomodoro_Timer.xlsb/Sheet1.sheet.cls b/src/Pomodoro_Timer.xlsb/Sheet1.sheet.cls new file mode 100644 index 0000000..e69de29 diff --git a/src/Pomodoro_Timer.xlsb/Sheet3.sheet.cls b/src/Pomodoro_Timer.xlsb/Sheet3.sheet.cls new file mode 100644 index 0000000..4f97aa8 --- /dev/null +++ b/src/Pomodoro_Timer.xlsb/Sheet3.sheet.cls @@ -0,0 +1 @@ +Option Explicit diff --git a/src/Pomodoro_Timer.xlsb/Sheet5.sheet.cls b/src/Pomodoro_Timer.xlsb/Sheet5.sheet.cls new file mode 100644 index 0000000..4f97aa8 --- /dev/null +++ b/src/Pomodoro_Timer.xlsb/Sheet5.sheet.cls @@ -0,0 +1 @@ +Option Explicit diff --git a/src/Pomodoro_Timer.xlsb/ThisWorkbook.sheet.cls b/src/Pomodoro_Timer.xlsb/ThisWorkbook.sheet.cls index b56ad7b..15c68c6 100644 --- a/src/Pomodoro_Timer.xlsb/ThisWorkbook.sheet.cls +++ b/src/Pomodoro_Timer.xlsb/ThisWorkbook.sheet.cls @@ -1,12 +1,31 @@ Private Sub Workbook_Open() - If Sheets("Settings").Range("Run_in_seperate_instance").Value = True Then - If Reopen_decision = True Then Call OpenItSelfInAnotherInstance - End If - If Sheets("Settings").Range("Shortcut").Value = True Then + If Sheets("Settings").Range("Shortcut").Value = True And Not IsMac Then Call Shortcuts End If + Call MacOptions End Sub Sub Shortcuts() Application.OnKey "{F10}", "PomodoroSession" +End Sub + +Sub MacOptions() +'PURPOSE: Hide options that are not available for Mac + +If IsMac Then + Range("Reopen_Excel_after_x").EntireRow.Hidden = True + Range("Run_in_seperate_instance").EntireRow.Hidden = True + Range("Custom_position").EntireRow.Hidden = True + Range("Left_pos").EntireRow.Hidden = True + Range("Top_pos").EntireRow.Hidden = True + Range("Shortcut").EntireRow.Hidden = True +Else + Range("Reopen_Excel_after_x").EntireRow.Hidden = False + Range("Run_in_seperate_instance").EntireRow.Hidden = False + Range("Custom_position").EntireRow.Hidden = False + Range("Left_pos").EntireRow.Hidden = False + Range("Top_pos").EntireRow.Hidden = False + Range("Shortcut").EntireRow.Hidden = False +End If + End Sub \ No newline at end of file diff --git a/src/Pomodoro_Timer.xlsb/Version.bas b/src/Pomodoro_Timer.xlsb/Version.bas new file mode 100644 index 0000000..c5a05f6 --- /dev/null +++ b/src/Pomodoro_Timer.xlsb/Version.bas @@ -0,0 +1,23 @@ +Attribute VB_Name = "Version" +Option Explicit +'REFERENCE: https://www.rondebruin.nl/mac/mac001.htm + +Public Function IsMac() As Boolean +#If Mac Then + IsMac = True +#Else + IsMac = False +#End If +End Function + +Public Function Is64BitOffice() As Boolean +#If Win64 Then + Is64BitOffice = True +#End If +End Function + +Public Function Excelversion() As Double +'Win Excel versions are always a whole number (15) +'Mac Excel versions show also the number of the update (15.29) + Excelversion = Val(Application.Version) +End Function