├── .gitignore ├── GlobalAssemblyInfo.cs ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── Tomighty.Core.Test ├── FakeEventHub.cs ├── PomodoroEngineTest.cs ├── Properties │ └── AssemblyInfo.cs ├── Tomighty.Core.Test.csproj └── packages.config ├── Tomighty.Core ├── Duration.cs ├── Events │ ├── PomodoroCompleted.cs │ ├── PomodoroCountChanged.cs │ ├── TimeElapsed.cs │ ├── TimerStarted.cs │ └── TimerStopped.cs ├── ICountdownClock.cs ├── IEventHub.cs ├── IMutableUserPreferences.cs ├── IPomodoroEngine.cs ├── ITimer.cs ├── IUserPreferences.cs ├── IntervalType.cs ├── PomodoroEngine.cs ├── Properties │ └── AssemblyInfo.cs ├── SynchronousEventHub.cs ├── Timer.cs └── Tomighty.Core.csproj ├── Tomighty.Update.Swap ├── App.config ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings └── Tomighty.Update.Swap.csproj ├── Tomighty.Windows ├── About │ ├── AboutWindow.Designer.cs │ ├── AboutWindow.cs │ ├── AboutWindow.resx │ └── AboutWindowPresenter.cs ├── App.config ├── AutoUpdate.cs ├── Directories.cs ├── ErrorReportWindow.Designer.cs ├── ErrorReportWindow.cs ├── ErrorReportWindow.resx ├── Events.cs ├── Flags.cs ├── Host.cs ├── IntervalTypeExtensions.cs ├── LICENSE.txt ├── Logger.cs ├── NOTICE.txt ├── Notifications │ ├── NotificationsPresenter.cs │ ├── SoundNotificationPlayer.cs │ └── Toasts.cs ├── Preferences │ ├── UserPreferences.cs │ ├── UserPreferencesForm.Designer.cs │ ├── UserPreferencesForm.cs │ ├── UserPreferencesForm.resx │ └── UserPreferencesPresenter.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── Audio │ │ └── deskbell.wav │ ├── Toasts │ │ ├── app-updated.xml │ │ ├── first-run.xml │ │ ├── image_toast_tomato_blue.png │ │ ├── image_toast_tomato_green.png │ │ ├── image_toast_tomato_red.png │ │ └── interval-completed.xml │ ├── icon_tomato_black.ico │ ├── icon_tomato_blue.ico │ ├── icon_tomato_green.ico │ ├── icon_tomato_red.ico │ ├── icon_tomato_white.ico │ ├── image_clock.tiff │ ├── image_pinned.png │ ├── image_stop.tiff │ ├── image_tomato_black.tiff │ ├── image_tomato_blue.tiff │ ├── image_tomato_green.tiff │ ├── image_tomato_red.tiff │ ├── image_unpinned.png │ ├── image_warning.png │ └── image_x.png ├── StartupEventFlags.cs ├── StartupEvents.cs ├── Timer │ ├── Taskbar.cs │ ├── TimerWindow.Designer.cs │ ├── TimerWindow.cs │ ├── TimerWindow.resx │ └── TimerWindowPresenter.cs ├── Tomighty.Windows.csproj ├── TomightyApplication.cs ├── Tray │ ├── ITrayMenu.cs │ ├── ITrayMenuMutator.cs │ ├── TrayIconController.cs │ ├── TrayMenu.cs │ └── TrayMenuController.cs ├── URLs.cs ├── Util │ └── Hash.cs ├── Version.cs └── packages.config ├── Tomighty.sln ├── dist.bat ├── pack.ps1 └── setup.nsi /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | packages/ 3 | bin/ 4 | obj/ 5 | build/ 6 | dist/ 7 | *~ 8 | .vs/ 9 | *.user 10 | -------------------------------------------------------------------------------- /GlobalAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Reflection; 9 | using System.Resources; 10 | 11 | [assembly: AssemblyProduct("Tomighty")] 12 | [assembly: AssemblyCopyright("Copyright © 2010-2017 Celio Cidral Junior")] 13 | [assembly: NeutralResourcesLanguage("en-US")] 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | ========================================================================= 2 | == NOTICE file corresponding to section 4(d) of the Apache License, == 3 | == Version 2.0, in this case for the Tomighty distribution. == 4 | ========================================================================= 5 | 6 | Tomighty is a software developed by Célio Cidral Junior. You can find it 7 | at http://www.tomighty.org 8 | 9 | Pomodoro Technique® and Pomodoro™ are registered and filed trademarks 10 | owned by Francesco Cirillo. Tomighty is not affiliated by, associated 11 | with nor endorsed by Francesco Cirillo. 12 | 13 | Tomato icon designed by José Campos 14 | 15 | Clock icon designed by Thomas Le Bas 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tomighty for Windows 2 | ==================== 3 | 4 | Available for download at http://www.tomighty.org 5 | 6 | This repository contains the source code for the .NET version of Tomighty. 7 | If you are looking for the source code of the old, multiplatform (Java) version, see https://github.com/ccidral/tomighty 8 | 9 | Development Workflow 10 | ==================== 11 | 12 | We follow the development model presented by Vincent Driessen: 13 | 14 | http://nvie.com/posts/a-successful-git-branching-model/ 15 | 16 | Discuss 17 | ======= 18 | 19 | For development related discussions, please subscribe to: 20 | 21 | https://groups.google.com/forum/#!forum/tomighty-developers 22 | 23 | License 24 | ======= 25 | 26 | Tomighty for Windows is licensed under the terms of the Apache License 2.0. Read it here: https://www.apache.org/licenses/LICENSE-2.0.html 27 | -------------------------------------------------------------------------------- /Tomighty.Core.Test/FakeEventHub.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | 12 | namespace Tomighty.Test 13 | { 14 | public class FakeEventHub : IEventHub 15 | { 16 | private readonly List subscribers = new List(); 17 | private readonly List publishedEvents = new List(); 18 | 19 | public IEnumerable PublishedEvents() 20 | { 21 | return publishedEvents.Where(e => e.GetType() == typeof(T)).Select(e => (T) e); 22 | } 23 | 24 | public T LastEvent() 25 | { 26 | return PublishedEvents().LastOrDefault(); 27 | } 28 | 29 | public void Publish(object @event) 30 | { 31 | publishedEvents.Add(@event); 32 | 33 | foreach (var subscriber in subscribers) 34 | { 35 | subscriber.Handle(@event); 36 | } 37 | } 38 | 39 | public void Subscribe(Action eventHandler) 40 | { 41 | Action proxy = @event => eventHandler((T) @event); 42 | subscribers.Add(new Subscriber(typeof(T), proxy)); 43 | } 44 | 45 | private class Subscriber 46 | { 47 | private readonly Type eventType; 48 | private readonly Action eventHandler; 49 | 50 | public Subscriber(Type eventType, Action eventHandler) 51 | { 52 | this.eventType = eventType; 53 | this.eventHandler = eventHandler; 54 | } 55 | 56 | public void Handle(object @event) 57 | { 58 | if (eventType == @event.GetType()) 59 | { 60 | eventHandler(@event); 61 | } 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Tomighty.Core.Test/PomodoroEngineTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Linq; 9 | using NSubstitute; 10 | using NUnit.Framework; 11 | using Tomighty.Events; 12 | 13 | namespace Tomighty.Test 14 | { 15 | [TestFixture] 16 | public class PomodoroEngineTest 17 | { 18 | private IPomodoroEngine engine; 19 | private IUserPreferences userPreferences; 20 | private ITimer timer; 21 | private FakeEventHub eventHub; 22 | 23 | [SetUp] 24 | public void SetUp() 25 | { 26 | userPreferences = Substitute.For(); 27 | timer = Substitute.For(); 28 | eventHub = new FakeEventHub(); 29 | engine = new PomodoroEngine(timer, userPreferences, eventHub); 30 | } 31 | 32 | [Test] 33 | public void Initial_pomodoro_count_is_zero() 34 | { 35 | Assert.AreEqual(0, engine.PomodoroCount); 36 | } 37 | 38 | [Test] 39 | public void Start_the_timer() 40 | { 41 | var duration = Duration.InMinutes(25); 42 | var intervalType = IntervalType.Pomodoro; 43 | 44 | userPreferences.GetIntervalDuration(intervalType).Returns(duration); 45 | 46 | engine.StartTimer(intervalType); 47 | 48 | timer.Received().Start(duration, intervalType); 49 | } 50 | 51 | [Test] 52 | public void Stop_the_timer() 53 | { 54 | engine.StopTimer(); 55 | timer.Received().Stop(); 56 | } 57 | 58 | [Test] 59 | public void Publish_PomodoroCompleted_event_when_timer_stops_with_zero_remaining_time() 60 | { 61 | var remainingTime = Duration.Zero; 62 | var duration = Duration.InMinutes(25); 63 | var pomodoroCompletedEvent = new TimerStopped(IntervalType.Pomodoro, duration, remainingTime); 64 | 65 | eventHub.Publish(pomodoroCompletedEvent); 66 | 67 | var pomodoroCompleted = eventHub.PublishedEvents().Single(); 68 | Assert.AreEqual(duration, pomodoroCompleted.Duration); 69 | } 70 | 71 | [Test] 72 | public void Do_not_publish_PomodoroCompleted_event_when_timer_stops_with_remaining_time_greater_than_zero() 73 | { 74 | var remainingTime = Duration.InSeconds(1); 75 | var duration = Duration.InMinutes(25); 76 | 77 | eventHub.Publish(new TimerStopped(IntervalType.Pomodoro, duration, remainingTime)); 78 | 79 | var pomodoroCompletedEvents = eventHub.PublishedEvents(); 80 | Assert.AreEqual(0, pomodoroCompletedEvents.Count()); 81 | } 82 | 83 | [Test] 84 | public void Do_not_publish_PomodoroCompleted_event_when_timer_stops_and_interval_type_is_not_Pomodoro() 85 | { 86 | var remainingTime = Duration.Zero; 87 | var duration = Duration.InMinutes(25); 88 | 89 | eventHub.Publish(new TimerStopped(IntervalType.ShortBreak, duration, remainingTime)); 90 | 91 | var pomodoroCompletedEvents = eventHub.PublishedEvents(); 92 | Assert.AreEqual(0, pomodoroCompletedEvents.Count()); 93 | } 94 | 95 | [Test] 96 | public void Increase_pomodoro_count_when_timer_stops_with_zero_remaining_time() 97 | { 98 | userPreferences.MaxPomodoroCount.Returns(999); 99 | 100 | eventHub.Publish(PomodoroCompletedEvent()); 101 | Assert.AreEqual(1, engine.PomodoroCount); 102 | 103 | eventHub.Publish(PomodoroCompletedEvent()); 104 | Assert.AreEqual(2, engine.PomodoroCount); 105 | 106 | eventHub.Publish(PomodoroCompletedEvent()); 107 | Assert.AreEqual(3, engine.PomodoroCount); 108 | } 109 | 110 | [Test] 111 | public void Reset_pomodoro_count_when_it_is_greater_than_limit_defined_by_user_preferences() 112 | { 113 | userPreferences.MaxPomodoroCount.Returns(3); 114 | 115 | eventHub.Publish(PomodoroCompletedEvent()); 116 | Assert.AreEqual(1, engine.PomodoroCount); 117 | 118 | eventHub.Publish(PomodoroCompletedEvent()); 119 | Assert.AreEqual(2, engine.PomodoroCount); 120 | 121 | eventHub.Publish(PomodoroCompletedEvent()); 122 | Assert.AreEqual(3, engine.PomodoroCount); 123 | 124 | eventHub.Publish(PomodoroCompletedEvent()); 125 | Assert.AreEqual(1, engine.PomodoroCount); 126 | 127 | eventHub.Publish(PomodoroCompletedEvent()); 128 | Assert.AreEqual(2, engine.PomodoroCount); 129 | 130 | eventHub.Publish(PomodoroCompletedEvent()); 131 | Assert.AreEqual(3, engine.PomodoroCount); 132 | 133 | eventHub.Publish(PomodoroCompletedEvent()); 134 | Assert.AreEqual(1, engine.PomodoroCount); 135 | } 136 | 137 | [Test] 138 | public void Suggested_timer_action_is_ShortBreak_when_pomodoro_count_is_different_than_user_defined_count_limit() 139 | { 140 | userPreferences.MaxPomodoroCount.Returns(3); 141 | 142 | // pomodoro count: 0 143 | Assert.AreEqual(IntervalType.ShortBreak, engine.SuggestedBreakType); 144 | 145 | // pomodoro count: 1 146 | eventHub.Publish(PomodoroCompletedEvent()); 147 | Assert.AreEqual(IntervalType.ShortBreak, engine.SuggestedBreakType); 148 | 149 | // pomodoro count: 2 150 | eventHub.Publish(PomodoroCompletedEvent()); 151 | Assert.AreEqual(IntervalType.ShortBreak, engine.SuggestedBreakType); 152 | } 153 | 154 | [Test] 155 | public void Suggested_timer_action_is_LongBreak_when_pomodoro_count_is_equal_to_user_defined_count_limit() 156 | { 157 | userPreferences.MaxPomodoroCount.Returns(3); 158 | 159 | eventHub.Publish(PomodoroCompletedEvent()); // pomodoro count: 1 160 | eventHub.Publish(PomodoroCompletedEvent()); // pomodoro count: 2 161 | eventHub.Publish(PomodoroCompletedEvent()); // pomodoro count: 3 162 | 163 | Assert.AreEqual(IntervalType.LongBreak, engine.SuggestedBreakType); 164 | } 165 | 166 | [Test] 167 | public void Manually_reset_pomodoro_count() 168 | { 169 | userPreferences.MaxPomodoroCount.Returns(999); 170 | 171 | eventHub.Publish(PomodoroCompletedEvent()); 172 | eventHub.Publish(PomodoroCompletedEvent()); 173 | eventHub.Publish(PomodoroCompletedEvent()); 174 | 175 | engine.ResetPomodoroCount(); 176 | 177 | Assert.AreEqual(0, engine.PomodoroCount); 178 | } 179 | 180 | [Test] 181 | public void Publish_event_when_pomodoro_count_changes() 182 | { 183 | userPreferences.MaxPomodoroCount.Returns(3); 184 | 185 | eventHub.Publish(PomodoroCompletedEvent()); 186 | Assert.AreEqual(1, eventHub.PublishedEvents().Count()); 187 | Assert.AreEqual(1, eventHub.LastEvent().PomodoroCount); 188 | 189 | eventHub.Publish(PomodoroCompletedEvent()); 190 | Assert.AreEqual(2, eventHub.PublishedEvents().Count()); 191 | Assert.AreEqual(2, eventHub.LastEvent().PomodoroCount); 192 | 193 | eventHub.Publish(PomodoroCompletedEvent()); 194 | Assert.AreEqual(3, eventHub.PublishedEvents().Count()); 195 | Assert.AreEqual(3, eventHub.LastEvent().PomodoroCount); 196 | 197 | eventHub.Publish(PomodoroCompletedEvent()); 198 | Assert.AreEqual(4, eventHub.PublishedEvents().Count()); 199 | Assert.AreEqual(1, eventHub.LastEvent().PomodoroCount); 200 | 201 | engine.ResetPomodoroCount(); 202 | Assert.AreEqual(5, eventHub.PublishedEvents().Count()); 203 | Assert.AreEqual(0, eventHub.LastEvent().PomodoroCount); 204 | } 205 | 206 | private static TimerStopped PomodoroCompletedEvent() 207 | { 208 | var remainingTime = Duration.Zero; 209 | var duration = Duration.InMinutes(25); 210 | return new TimerStopped(IntervalType.Pomodoro, duration, remainingTime); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Tomighty.Core.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Reflection; 9 | using System.Runtime.InteropServices; 10 | 11 | [assembly: AssemblyTitle("Tomighty Core Tests")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("CAC6A930-C0E3-422B-A49E-7DE000DC9A40")] 14 | -------------------------------------------------------------------------------- /Tomighty.Core.Test/Tomighty.Core.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {CAC6A930-C0E3-422B-A49E-7DE000DC9A40} 8 | Library 9 | Properties 10 | Tomighty 11 | Tomighty.Core.Test 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\NSubstitute.2.0.0-rc\lib\net45\NSubstitute.dll 37 | 38 | 39 | ..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | GlobalAssemblyInfo.cs 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {C7FF3B3E-0CC1-4EC7-A7C1-39B6361B5895} 61 | Tomighty.Core 62 | 63 | 64 | 65 | 72 | -------------------------------------------------------------------------------- /Tomighty.Core.Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tomighty.Core/Duration.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | 10 | namespace Tomighty 11 | { 12 | public struct Duration 13 | { 14 | public static readonly Duration Zero = new Duration(); 15 | 16 | public static Duration InSeconds(int seconds) => new Duration(seconds); 17 | public static Duration InMinutes(int minutes) => new Duration(minutes * 60); 18 | 19 | public Duration(int seconds) 20 | { 21 | if(seconds < 0) 22 | throw new ArgumentException($"Invalid duration of {seconds} seconds"); 23 | 24 | Seconds = seconds; 25 | } 26 | 27 | public int Seconds { get; } 28 | public int Minutes => Seconds / 60; 29 | 30 | public Duration AddSeconds(int delta) => new Duration(Seconds + delta); 31 | 32 | public override string ToString() => $"{GetType().Name}({Seconds})"; 33 | 34 | public string ToTimeString() 35 | { 36 | int minutes = Seconds / 60; 37 | int seconds = Seconds - minutes * 60; 38 | return $"{minutes.ToString("0#")}:{seconds.ToString("0#")}"; 39 | } 40 | 41 | public static bool operator ==(Duration a, Duration b) => a.Seconds == b.Seconds; 42 | public static bool operator !=(Duration a, Duration b) => a.Seconds != b.Seconds; 43 | } 44 | } -------------------------------------------------------------------------------- /Tomighty.Core/Events/PomodoroCompleted.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty.Events 9 | { 10 | public class PomodoroCompleted 11 | { 12 | public PomodoroCompleted(Duration duration) 13 | { 14 | Duration = duration; 15 | } 16 | 17 | public Duration Duration { get; } 18 | } 19 | } -------------------------------------------------------------------------------- /Tomighty.Core/Events/PomodoroCountChanged.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty.Events 9 | { 10 | public class PomodoroCountChanged 11 | { 12 | public PomodoroCountChanged(int count) 13 | { 14 | PomodoroCount = count; 15 | } 16 | 17 | public int PomodoroCount { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tomighty.Core/Events/TimeElapsed.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty.Events 9 | { 10 | public class TimeElapsed 11 | { 12 | public TimeElapsed(IntervalType intervalType, Duration duration, Duration remainingTime) 13 | { 14 | IntervalType = intervalType; 15 | Duration = duration; 16 | RemainingTime = remainingTime; 17 | } 18 | 19 | public Duration Duration { get; } 20 | public IntervalType IntervalType { get; } 21 | public Duration RemainingTime { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tomighty.Core/Events/TimerStarted.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty.Events 9 | { 10 | public class TimerStarted 11 | { 12 | public IntervalType IntervalType { get; } 13 | public Duration Duration { get; } 14 | public Duration RemainingTime { get; } 15 | 16 | public TimerStarted(IntervalType intervalType, Duration duration, Duration remainingTime) 17 | { 18 | IntervalType = intervalType; 19 | Duration = duration; 20 | RemainingTime = remainingTime; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Tomighty.Core/Events/TimerStopped.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty.Events 9 | { 10 | public class TimerStopped 11 | { 12 | public IntervalType IntervalType { get; } 13 | public Duration Duration { get; } 14 | public Duration RemainingTime { get; } 15 | public bool IsIntervalCompleted => RemainingTime == Duration.Zero; 16 | 17 | public TimerStopped(IntervalType intervalType, Duration duration, Duration remainingTime) 18 | { 19 | IntervalType = intervalType; 20 | Duration = duration; 21 | RemainingTime = remainingTime; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Tomighty.Core/ICountdownClock.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty 9 | { 10 | public interface ICountdownClock 11 | { 12 | Duration RemainingTime { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tomighty.Core/IEventHub.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | 10 | namespace Tomighty 11 | { 12 | public interface IEventHub 13 | { 14 | void Publish(object @event); 15 | void Subscribe(Action eventHandler); 16 | } 17 | } -------------------------------------------------------------------------------- /Tomighty.Core/IMutableUserPreferences.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty 9 | { 10 | public interface IMutableUserPreferences : IUserPreferences 11 | { 12 | void SetIntervalDuration(IntervalType intervalType, Duration duration); 13 | new bool ShowToastNotifications { get; set; } 14 | new bool PlaySoundNotifications { get; set; } 15 | new int MaxPomodoroCount { get; set; } 16 | new bool AutoUpdate { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tomighty.Core/IPomodoroEngine.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty 9 | { 10 | public interface IPomodoroEngine 11 | { 12 | void StartTimer(IntervalType intervalType); 13 | void StopTimer(); 14 | void ResetPomodoroCount(); 15 | 16 | int PomodoroCount { get; } 17 | IntervalType SuggestedBreakType { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tomighty.Core/ITimer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty 9 | { 10 | public interface ITimer : ICountdownClock 11 | { 12 | void Start(Duration duration, IntervalType intervalType); 13 | void Stop(); 14 | } 15 | } -------------------------------------------------------------------------------- /Tomighty.Core/IUserPreferences.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | 10 | namespace Tomighty 11 | { 12 | public interface IUserPreferences 13 | { 14 | Duration GetIntervalDuration(IntervalType intervalType); 15 | int MaxPomodoroCount { get; } 16 | bool ShowToastNotifications { get; } 17 | bool PlaySoundNotifications { get; } 18 | bool AutoUpdate { get; } 19 | 20 | void Update(Action action); 21 | } 22 | } -------------------------------------------------------------------------------- /Tomighty.Core/IntervalType.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty 9 | { 10 | public enum IntervalType 11 | { 12 | Pomodoro, 13 | ShortBreak, 14 | LongBreak 15 | } 16 | } -------------------------------------------------------------------------------- /Tomighty.Core/PomodoroEngine.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using Tomighty.Events; 9 | 10 | namespace Tomighty 11 | { 12 | public class PomodoroEngine : IPomodoroEngine 13 | { 14 | private readonly ITimer timer; 15 | private readonly IUserPreferences userPreferences; 16 | private readonly IEventHub eventHub; 17 | private int _pomodoroCount; 18 | 19 | public PomodoroEngine(ITimer timer, IUserPreferences userPreferences, IEventHub eventHub) 20 | { 21 | this.timer = timer; 22 | this.userPreferences = userPreferences; 23 | this.eventHub = eventHub; 24 | 25 | eventHub.Subscribe(OnTimerStopped); 26 | } 27 | 28 | public int PomodoroCount 29 | { 30 | get 31 | { 32 | return _pomodoroCount; 33 | } 34 | private set 35 | { 36 | if (value != _pomodoroCount) 37 | { 38 | _pomodoroCount = value; 39 | eventHub.Publish(new PomodoroCountChanged(value)); 40 | } 41 | } 42 | } 43 | 44 | public IntervalType SuggestedBreakType => PomodoroCount != userPreferences.MaxPomodoroCount ? IntervalType.ShortBreak : IntervalType.LongBreak; 45 | 46 | public void StartTimer(IntervalType intervalType) 47 | { 48 | var duration = userPreferences.GetIntervalDuration(intervalType); 49 | timer.Start(duration, intervalType); 50 | } 51 | 52 | public void StopTimer() 53 | { 54 | timer.Stop(); 55 | } 56 | 57 | private void OnTimerStopped(TimerStopped timerStopped) 58 | { 59 | if (timerStopped.IntervalType == IntervalType.Pomodoro && timerStopped.IsIntervalCompleted) 60 | { 61 | PomodoroCompleted(timerStopped.Duration); 62 | } 63 | } 64 | 65 | private void PomodoroCompleted(Duration duration) 66 | { 67 | IncreasePomodoroCount(); 68 | eventHub.Publish(new PomodoroCompleted(duration)); 69 | } 70 | 71 | private void IncreasePomodoroCount() 72 | { 73 | var newCount = PomodoroCount + 1; 74 | PomodoroCount = newCount > userPreferences.MaxPomodoroCount ? 1 : newCount; 75 | } 76 | 77 | public void ResetPomodoroCount() 78 | { 79 | PomodoroCount = 0; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Tomighty.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Reflection; 9 | using System.Runtime.InteropServices; 10 | 11 | [assembly: AssemblyTitle("Tomighty Core")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("C7FF3B3E-0CC1-4EC7-A7C1-39B6361B5895")] 14 | -------------------------------------------------------------------------------- /Tomighty.Core/SynchronousEventHub.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | namespace Tomighty 12 | { 13 | public class SynchronousEventHub : IEventHub 14 | { 15 | private readonly IDictionary>> allSubscribers = new Dictionary>>(); 16 | 17 | public void Publish(object @event) 18 | { 19 | var eventType = @event.GetType(); 20 | 21 | if (!allSubscribers.ContainsKey(eventType)) 22 | return; 23 | 24 | foreach (var subscriber in allSubscribers[eventType]) 25 | { 26 | subscriber(@event); 27 | } 28 | } 29 | 30 | public void Subscribe(Action eventHandler) 31 | { 32 | List> subscribers; 33 | var eventType = typeof(T); 34 | 35 | if (allSubscribers.ContainsKey(eventType)) 36 | { 37 | subscribers = allSubscribers[eventType]; 38 | } 39 | else 40 | { 41 | subscribers = new List>(); 42 | allSubscribers[eventType] = subscribers; 43 | } 44 | 45 | subscribers.Add(e => eventHandler((T) e)); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Tomighty.Core/Timer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Timers; 9 | using Tomighty.Events; 10 | using SystemTimer = System.Timers.Timer; 11 | 12 | namespace Tomighty 13 | { 14 | public class Timer : ITimer 15 | { 16 | private const int IntervalInSeconds = 1; 17 | 18 | private readonly SystemTimer systemTimer = new SystemTimer(); 19 | private readonly IEventHub eventHub; 20 | 21 | // Mutable fields 22 | private Duration remainingTime = Duration.Zero; 23 | private IntervalType intervalType; 24 | private Duration duration; 25 | 26 | public Timer(IEventHub eventHub) 27 | { 28 | this.eventHub = eventHub; 29 | 30 | systemTimer.Interval = IntervalInSeconds * 1000; 31 | systemTimer.AutoReset = true; 32 | systemTimer.Elapsed += SystemTimerOnElapsed; 33 | } 34 | 35 | public Duration RemainingTime => remainingTime; 36 | 37 | public void Start(Duration duration, IntervalType intervalType) 38 | { 39 | this.duration = duration; 40 | this.intervalType = intervalType; 41 | 42 | remainingTime = duration; 43 | 44 | systemTimer.Start(); 45 | 46 | eventHub.Publish(new TimerStarted(intervalType, duration, remainingTime)); 47 | } 48 | 49 | public void Stop() 50 | { 51 | systemTimer.Stop(); 52 | 53 | eventHub.Publish(new TimerStopped(intervalType, duration, remainingTime)); 54 | 55 | remainingTime = Duration.Zero; 56 | } 57 | 58 | private void DecreaseRemainingTime(int seconds) 59 | { 60 | remainingTime = remainingTime.AddSeconds(-seconds); 61 | 62 | eventHub.Publish(new TimeElapsed(intervalType, duration, remainingTime)); 63 | 64 | if (remainingTime.Seconds == 0) 65 | { 66 | Stop(); 67 | } 68 | } 69 | 70 | private void SystemTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs) 71 | { 72 | DecreaseRemainingTime(IntervalInSeconds); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Tomighty.Core/Tomighty.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C7FF3B3E-0CC1-4EC7-A7C1-39B6361B5895} 8 | Library 9 | Properties 10 | Tomighty 11 | Tomighty.Core 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | GlobalAssemblyInfo.cs 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /Tomighty.Update.Swap/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Tomighty.Update.Swap/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Text.RegularExpressions; 6 | using System.Threading; 7 | using Tomighty.Windows; 8 | 9 | namespace Tomighty.Update.Swap 10 | { 11 | static class Program 12 | { 13 | private static readonly Logger logger = new Logger("swap"); 14 | 15 | [STAThread] 16 | static void Main() 17 | { 18 | string[] args = Environment.GetCommandLineArgs(); 19 | 20 | if (args.Length != 5) 21 | { 22 | logger.Error($"Wrong number of arguments: {args.Length}, exiting now"); 23 | return; 24 | } 25 | 26 | var processId = args[1]; 27 | var exePath = args[2]; 28 | var sourcePackage = args[3]; 29 | var restart = args[4]; 30 | 31 | if (!IsInteger(processId)) 32 | { 33 | logger.Error($"Invalid process id: {processId}"); 34 | return; 35 | } 36 | 37 | if (!File.Exists(exePath)) 38 | { 39 | logger.Error($"File not found: {exePath}"); 40 | return; 41 | } 42 | 43 | if (!File.Exists(sourcePackage)) 44 | { 45 | logger.Error($"File not found: {sourcePackage}"); 46 | return; 47 | } 48 | 49 | var targetDir = Path.GetDirectoryName(exePath); 50 | 51 | try 52 | { 53 | Swap(int.Parse(processId), exePath, sourcePackage, targetDir, restart == "true"); 54 | } 55 | catch (Exception e) 56 | { 57 | logger.Error(e); 58 | } 59 | } 60 | 61 | private static void Swap(int processId, string exePath, string sourcePackage, string targetDir, bool restart) 62 | { 63 | try 64 | { 65 | logger.Info($"Waiting for process {processId} to exit"); 66 | Process.GetProcessById(processId).WaitForExit(); 67 | 68 | logger.Info($"Process {processId} has exited"); 69 | } 70 | catch (ArgumentException) 71 | { 72 | logger.Info($"Process {processId} doesn't exist, probably exited"); 73 | } 74 | 75 | Thread.Sleep(500); 76 | 77 | logger.Info($"Deleting directory contents: {targetDir}"); 78 | DeleteDirectoryContents(targetDir); 79 | 80 | logger.Info($"Extracting files from `{sourcePackage}` into `{targetDir}`"); 81 | ZipFile.ExtractToDirectory(sourcePackage, targetDir); 82 | 83 | if (restart) 84 | { 85 | logger.Info($"Starting process {exePath}"); 86 | Process.Start(exePath); 87 | } 88 | 89 | StartupEventFlags.Flags.TurnOn(StartupEventFlags.AppUpdatedFlag); 90 | 91 | logger.Info($"Done"); 92 | } 93 | 94 | private static void DeleteDirectoryContents(string targetDir) 95 | { 96 | foreach (var file in Directory.GetFiles(targetDir)) 97 | { 98 | File.Delete(file); 99 | } 100 | 101 | foreach (var dir in Directory.GetDirectories(targetDir)) 102 | { 103 | new DirectoryInfo(dir).Delete(true); 104 | } 105 | } 106 | 107 | private static bool IsInteger(string s) 108 | { 109 | return s != null && Regex.IsMatch(s, @"^\d+$"); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Tomighty.Update.Swap/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Tomighty.Update.Swap")] 6 | [assembly: ComVisible(false)] 7 | [assembly: Guid("3fc0dc20-6120-4cd7-8f54-a1ca6f8886bf")] 8 | -------------------------------------------------------------------------------- /Tomighty.Update.Swap/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Tomighty.Update.Swap.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tomighty.Update.Swap/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tomighty.Update.Swap/Tomighty.Update.Swap.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {3FC0DC20-6120-4CD7-8F54-A1CA6F8886BF} 8 | WinExe 9 | Properties 10 | Tomighty.Update.Swap 11 | Tomighty.Update.Swap 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Directories.cs 53 | 54 | 55 | Flags.cs 56 | 57 | 58 | Logger.cs 59 | 60 | 61 | StartupEventFlags.cs 62 | 63 | 64 | 65 | 66 | SettingsSingleFileGenerator 67 | Settings.Designer.cs 68 | 69 | 70 | True 71 | Settings.settings 72 | True 73 | 74 | 75 | 76 | 77 | 78 | 79 | 86 | -------------------------------------------------------------------------------- /Tomighty.Windows/About/AboutWindow.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty.Windows.About 9 | { 10 | partial class AboutWindow 11 | { 12 | /// 13 | /// Required designer variable. 14 | /// 15 | private System.ComponentModel.IContainer components = null; 16 | 17 | /// 18 | /// Clean up any resources being used. 19 | /// 20 | /// true if managed resources should be disposed; otherwise, false. 21 | protected override void Dispose(bool disposing) 22 | { 23 | if (disposing && (components != null)) 24 | { 25 | components.Dispose(); 26 | } 27 | base.Dispose(disposing); 28 | } 29 | 30 | #region Windows Form Designer generated code 31 | 32 | /// 33 | /// Required method for Designer support - do not modify 34 | /// the contents of this method with the code editor. 35 | /// 36 | private void InitializeComponent() 37 | { 38 | this.titleLabel = new System.Windows.Forms.Label(); 39 | this.urlLabel = new System.Windows.Forms.Label(); 40 | this.licenseTextBox = new System.Windows.Forms.TextBox(); 41 | this.closeButton = new System.Windows.Forms.Button(); 42 | this.productVersionTextBox = new System.Windows.Forms.TextBox(); 43 | this.SuspendLayout(); 44 | // 45 | // titleLabel 46 | // 47 | this.titleLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 48 | | System.Windows.Forms.AnchorStyles.Right))); 49 | this.titleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 50 | this.titleLabel.Location = new System.Drawing.Point(12, 9); 51 | this.titleLabel.Name = "titleLabel"; 52 | this.titleLabel.Size = new System.Drawing.Size(612, 40); 53 | this.titleLabel.TabIndex = 1; 54 | this.titleLabel.Text = "Tomighty"; 55 | this.titleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 56 | // 57 | // urlLabel 58 | // 59 | this.urlLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 60 | | System.Windows.Forms.AnchorStyles.Right))); 61 | this.urlLabel.Location = new System.Drawing.Point(12, 49); 62 | this.urlLabel.Name = "urlLabel"; 63 | this.urlLabel.Size = new System.Drawing.Size(612, 20); 64 | this.urlLabel.TabIndex = 2; 65 | this.urlLabel.Text = "http://www.tomighty.org"; 66 | this.urlLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 67 | // 68 | // licenseTextBox 69 | // 70 | this.licenseTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 71 | | System.Windows.Forms.AnchorStyles.Left) 72 | | System.Windows.Forms.AnchorStyles.Right))); 73 | this.licenseTextBox.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 74 | this.licenseTextBox.Location = new System.Drawing.Point(18, 91); 75 | this.licenseTextBox.Multiline = true; 76 | this.licenseTextBox.Name = "licenseTextBox"; 77 | this.licenseTextBox.ReadOnly = true; 78 | this.licenseTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; 79 | this.licenseTextBox.Size = new System.Drawing.Size(606, 282); 80 | this.licenseTextBox.TabIndex = 3; 81 | this.licenseTextBox.WordWrap = false; 82 | // 83 | // closeButton 84 | // 85 | this.closeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 86 | this.closeButton.Location = new System.Drawing.Point(549, 379); 87 | this.closeButton.Name = "closeButton"; 88 | this.closeButton.Size = new System.Drawing.Size(75, 23); 89 | this.closeButton.TabIndex = 0; 90 | this.closeButton.Text = "Close"; 91 | this.closeButton.UseVisualStyleBackColor = true; 92 | this.closeButton.Click += new System.EventHandler(this.closeButton_Click); 93 | // 94 | // productVersionTextBox 95 | // 96 | this.productVersionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 97 | | System.Windows.Forms.AnchorStyles.Right))); 98 | this.productVersionTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; 99 | this.productVersionTextBox.Location = new System.Drawing.Point(18, 72); 100 | this.productVersionTextBox.Name = "productVersionTextBox"; 101 | this.productVersionTextBox.ReadOnly = true; 102 | this.productVersionTextBox.Size = new System.Drawing.Size(606, 13); 103 | this.productVersionTextBox.TabIndex = 4; 104 | this.productVersionTextBox.Text = ""; 105 | this.productVersionTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; 106 | // 107 | // AboutWindow 108 | // 109 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 110 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 111 | this.ClientSize = new System.Drawing.Size(636, 414); 112 | this.Controls.Add(this.productVersionTextBox); 113 | this.Controls.Add(this.closeButton); 114 | this.Controls.Add(this.licenseTextBox); 115 | this.Controls.Add(this.urlLabel); 116 | this.Controls.Add(this.titleLabel); 117 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 118 | this.MaximizeBox = false; 119 | this.MinimizeBox = false; 120 | this.Name = "AboutWindow"; 121 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 122 | this.Text = "About Tomighty"; 123 | this.Load += new System.EventHandler(this.AboutForm_Load); 124 | this.ResumeLayout(false); 125 | this.PerformLayout(); 126 | 127 | } 128 | 129 | #endregion 130 | 131 | private System.Windows.Forms.Label titleLabel; 132 | private System.Windows.Forms.Label urlLabel; 133 | private System.Windows.Forms.TextBox licenseTextBox; 134 | private System.Windows.Forms.Button closeButton; 135 | private System.Windows.Forms.TextBox productVersionTextBox; 136 | } 137 | } -------------------------------------------------------------------------------- /Tomighty.Windows/About/AboutWindow.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.IO; 10 | using System.Reflection; 11 | using System.Windows.Forms; 12 | 13 | namespace Tomighty.Windows.About 14 | { 15 | public partial class AboutWindow : Form 16 | { 17 | public AboutWindow() 18 | { 19 | InitializeComponent(); 20 | } 21 | 22 | private void AboutForm_Load(object sender, EventArgs e) 23 | { 24 | var version = Assembly.GetExecutingAssembly().GetName().Version; 25 | 26 | titleLabel.Text = $"Tomighty {version.Major}.{version.Minor}.{version.Build}"; 27 | licenseTextBox.Text = File.ReadAllText(@"LICENSE.txt"); 28 | productVersionTextBox.Text = Version.Product; 29 | } 30 | 31 | private void closeButton_Click(object sender, EventArgs e) 32 | { 33 | Close(); 34 | Dispose(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tomighty.Windows/About/AboutWindow.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Tomighty.Windows/About/AboutWindowPresenter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Windows.Forms; 9 | 10 | namespace Tomighty.Windows.About 11 | { 12 | internal class AboutWindowPresenter 13 | { 14 | private AboutWindow window; 15 | 16 | public void Show() 17 | { 18 | if (window == null) 19 | { 20 | window = new AboutWindow(); 21 | window.FormClosed += OnWindowClosed; 22 | } 23 | 24 | if (window.Visible) 25 | { 26 | window.Focus(); 27 | } 28 | else 29 | { 30 | window.ShowDialog(); 31 | } 32 | } 33 | 34 | private void OnWindowClosed(object sender, FormClosedEventArgs e) 35 | { 36 | window.Dispose(); 37 | window = null; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tomighty.Windows/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Tomighty.Windows/AutoUpdate.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Diagnostics; 10 | using System.IO; 11 | using System.Net.Http; 12 | using System.Reflection; 13 | using System.Threading.Tasks; 14 | using System.Windows.Forms; 15 | using Tomighty.Windows.Preferences; 16 | using Tomighty.Windows.Util; 17 | 18 | namespace Tomighty.Windows 19 | { 20 | internal class AutoUpdate 21 | { 22 | private const int CheckIntervalInHours = 72; 23 | 24 | private static readonly Logger logger = new Logger("update"); 25 | 26 | private readonly UserPreferences userPreferences; 27 | 28 | public AutoUpdate(UserPreferences userPreferences) 29 | { 30 | this.userPreferences = userPreferences; 31 | Application.ApplicationExit += Update; 32 | } 33 | 34 | private static string LatestPackageFile => Path.Combine(Directories.AppData, "latest.zip"); 35 | 36 | private static string LastDownloadedVersionFile => Path.Combine(Directories.AppData, "latest.version"); 37 | 38 | private static string SwapProgramFile => Path.Combine(Directories.ProgramLocation, "Tomighty.Update.Swap.exe"); 39 | 40 | public void Start() 41 | { 42 | if (userPreferences.AutoUpdate) 43 | { 44 | Task.Run(async () => 45 | { 46 | try 47 | { 48 | await CheckIfNecessary(); 49 | } 50 | catch(Exception e) 51 | { 52 | logger.Error(e); 53 | } 54 | }); 55 | } 56 | } 57 | 58 | private async Task CheckIfNecessary() 59 | { 60 | if (IsTimeToCheck) 61 | { 62 | await Check(); 63 | } 64 | } 65 | 66 | public bool IsTimeToCheck => 67 | !File.Exists(LastDownloadedVersionFile) || 68 | (DateTime.Now - LastCheckTime).TotalHours > CheckIntervalInHours; 69 | 70 | private static DateTime LastCheckTime => File.GetLastWriteTime(LastDownloadedVersionFile); 71 | 72 | private static void ResetLastCheckTime() 73 | { 74 | if (File.Exists(LastDownloadedVersionFile)) 75 | { 76 | File.SetLastWriteTime(LastDownloadedVersionFile, DateTime.Now); 77 | } 78 | else 79 | { 80 | File.Create(LastDownloadedVersionFile).Close(); 81 | } 82 | } 83 | 84 | private async Task Check() 85 | { 86 | logger.Info("Checking"); 87 | 88 | var client = new HttpClient(); 89 | var result = await client.GetAsync(URLs.UpdateFeed); 90 | 91 | if (result.StatusCode != System.Net.HttpStatusCode.OK) 92 | { 93 | logger.Error($"Received {result.StatusCode} from {URLs.UpdateFeed}"); 94 | return; 95 | } 96 | 97 | var content = await result.Content.ReadAsStringAsync(); 98 | var lines = content.Split('\t'); 99 | 100 | if (lines.Length != 3) 101 | { 102 | logger.Error($"Invalid feed file"); 103 | return; 104 | } 105 | 106 | var latestVersion = lines[0]; 107 | 108 | if (latestVersion == GetLastDownloadedVersion()) 109 | { 110 | logger.Info($"Latest version has already been downloaded: {latestVersion}"); 111 | ResetLastCheckTime(); 112 | return; 113 | } 114 | 115 | if (Version.Product == latestVersion) 116 | { 117 | logger.Info($"Already running the latest version: {latestVersion}"); 118 | ResetLastCheckTime(); 119 | return; 120 | } 121 | 122 | var url = lines[1]; 123 | var sha256 = lines[2]; 124 | 125 | if (await Download(url, sha256)) 126 | { 127 | File.WriteAllText(LastDownloadedVersionFile, latestVersion); 128 | } 129 | } 130 | 131 | private async Task Download(string url, string expectedSha256) 132 | { 133 | logger.Info($"Downloading {url}"); 134 | 135 | var client = new HttpClient(); 136 | var result = await client.GetAsync(url); 137 | 138 | if (result.StatusCode != System.Net.HttpStatusCode.OK) 139 | { 140 | logger.Error($"Received {result.StatusCode} from {url}"); 141 | return false; 142 | } 143 | 144 | var tempFile = LatestPackageFile + ".tmp"; 145 | 146 | using (var input = await result.Content.ReadAsStreamAsync()) 147 | { 148 | using (var output = new FileStream(tempFile, FileMode.Create)) 149 | { 150 | await input.CopyToAsync(output); 151 | } 152 | } 153 | 154 | logger.Info($"Download completed"); 155 | 156 | using (var stream = new FileStream(tempFile, FileMode.Open)) 157 | { 158 | var sha256 = Hash.Sha256(stream); 159 | if (sha256 != expectedSha256) 160 | { 161 | logger.Error($"Integrity error: expected hash {expectedSha256} instead of {sha256}"); 162 | return false; 163 | } 164 | } 165 | 166 | if (File.Exists(LatestPackageFile)) 167 | { 168 | File.Delete(LatestPackageFile); 169 | } 170 | 171 | File.Move(tempFile, LatestPackageFile); 172 | 173 | logger.Info($"New version available at {LatestPackageFile}"); 174 | 175 | return true; 176 | } 177 | 178 | private void Update(object sender, EventArgs e) 179 | { 180 | try 181 | { 182 | if (ShouldUpdate) 183 | { 184 | logger.Info($"Updating from version {Version.Product} to {GetLastDownloadedVersion()}"); 185 | 186 | var processId = Process.GetCurrentProcess().Id; 187 | var tomighty = Assembly.GetEntryAssembly().Location; 188 | var restart = false; 189 | var tempSwapProgramFile = CopySwapProgramFileToTempDir(); 190 | Process.Start(tempSwapProgramFile, $"{processId} \"{tomighty}\" \"{LatestPackageFile}\" {restart}"); 191 | } 192 | } 193 | catch(Exception ex) 194 | { 195 | logger.Error(ex); 196 | } 197 | } 198 | 199 | private static string CopySwapProgramFileToTempDir() 200 | { 201 | var file = Path.Combine(Path.GetTempPath(), Path.GetFileName(SwapProgramFile)); 202 | File.Copy(SwapProgramFile, file, true); 203 | return file; 204 | } 205 | 206 | public bool ShouldUpdate => 207 | userPreferences.AutoUpdate && 208 | IsOutdated && 209 | File.Exists(LatestPackageFile); 210 | 211 | private static bool IsOutdated 212 | { 213 | get 214 | { 215 | string latest = GetLastDownloadedVersion(); 216 | return !string.IsNullOrEmpty(latest) && Version.Product != latest; 217 | } 218 | } 219 | 220 | private static string GetLastDownloadedVersion() 221 | { 222 | var file = LastDownloadedVersionFile; 223 | return File.Exists(file) ? File.ReadAllText(file) : null; 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /Tomighty.Windows/Directories.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.IO; 10 | using System.Reflection; 11 | 12 | namespace Tomighty.Windows 13 | { 14 | internal class Directories 15 | { 16 | public static string ProgramLocation => Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); 17 | 18 | public static string AppData 19 | { 20 | get 21 | { 22 | var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Tomighty"); 23 | if (!Directory.Exists(path)) 24 | Directory.CreateDirectory(path); 25 | return path; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tomighty.Windows/ErrorReportWindow.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace Tomighty.Windows 2 | { 3 | partial class ErrorReportWindow 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ErrorReportWindow)); 32 | this.label1 = new System.Windows.Forms.Label(); 33 | this.errorDescription = new System.Windows.Forms.TextBox(); 34 | this.reportButton = new System.Windows.Forms.Button(); 35 | this.pictureBox1 = new System.Windows.Forms.PictureBox(); 36 | this.label2 = new System.Windows.Forms.Label(); 37 | this.progressLabel = new System.Windows.Forms.Label(); 38 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); 39 | this.SuspendLayout(); 40 | // 41 | // label1 42 | // 43 | this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 44 | | System.Windows.Forms.AnchorStyles.Right))); 45 | this.label1.Location = new System.Drawing.Point(55, 19); 46 | this.label1.Name = "label1"; 47 | this.label1.Size = new System.Drawing.Size(326, 46); 48 | this.label1.TabIndex = 1; 49 | this.label1.Text = "Oops! Looks like you found a bug in Tomighty. Below you can see the ugly details " + 50 | "of the bug. There\'s nothing to worry about, it\'s totally safe to close Tomighty." + 51 | " We apologize for the inconvenience."; 52 | // 53 | // errorDescription 54 | // 55 | this.errorDescription.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 56 | | System.Windows.Forms.AnchorStyles.Left) 57 | | System.Windows.Forms.AnchorStyles.Right))); 58 | this.errorDescription.BackColor = System.Drawing.Color.White; 59 | this.errorDescription.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 60 | this.errorDescription.Location = new System.Drawing.Point(17, 68); 61 | this.errorDescription.Multiline = true; 62 | this.errorDescription.Name = "errorDescription"; 63 | this.errorDescription.ReadOnly = true; 64 | this.errorDescription.ScrollBars = System.Windows.Forms.ScrollBars.Both; 65 | this.errorDescription.Size = new System.Drawing.Size(364, 147); 66 | this.errorDescription.TabIndex = 2; 67 | this.errorDescription.WordWrap = false; 68 | // 69 | // reportButton 70 | // 71 | this.reportButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 72 | this.reportButton.Location = new System.Drawing.Point(271, 274); 73 | this.reportButton.Name = "reportButton"; 74 | this.reportButton.Size = new System.Drawing.Size(110, 23); 75 | this.reportButton.TabIndex = 0; 76 | this.reportButton.Text = "Send Report Error"; 77 | this.reportButton.UseVisualStyleBackColor = true; 78 | this.reportButton.Click += new System.EventHandler(this.reportButton_Click); 79 | // 80 | // pictureBox1 81 | // 82 | this.pictureBox1.Image = global::Tomighty.Windows.Properties.Resources.image_warning; 83 | this.pictureBox1.Location = new System.Drawing.Point(17, 19); 84 | this.pictureBox1.Name = "pictureBox1"; 85 | this.pictureBox1.Size = new System.Drawing.Size(32, 32); 86 | this.pictureBox1.TabIndex = 3; 87 | this.pictureBox1.TabStop = false; 88 | // 89 | // label2 90 | // 91 | this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 92 | | System.Windows.Forms.AnchorStyles.Right))); 93 | this.label2.Location = new System.Drawing.Point(17, 230); 94 | this.label2.Name = "label2"; 95 | this.label2.Size = new System.Drawing.Size(364, 41); 96 | this.label2.TabIndex = 4; 97 | this.label2.Text = "Anyway, it would be great if you send us an error report by clicking the button b" + 98 | "elow. Don\'t worry, no personal information will be sent, we only send the techni" + 99 | "cal tidbits about this error."; 100 | // 101 | // progressLabel 102 | // 103 | this.progressLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 104 | this.progressLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(192))))); 105 | this.progressLabel.Location = new System.Drawing.Point(17, 274); 106 | this.progressLabel.Name = "progressLabel"; 107 | this.progressLabel.Size = new System.Drawing.Size(248, 23); 108 | this.progressLabel.TabIndex = 5; 109 | this.progressLabel.Text = "Progress message"; 110 | this.progressLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; 111 | this.progressLabel.Visible = false; 112 | // 113 | // ErrorReportWindow 114 | // 115 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 116 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 117 | this.ClientSize = new System.Drawing.Size(397, 313); 118 | this.Controls.Add(this.progressLabel); 119 | this.Controls.Add(this.label2); 120 | this.Controls.Add(this.pictureBox1); 121 | this.Controls.Add(this.reportButton); 122 | this.Controls.Add(this.errorDescription); 123 | this.Controls.Add(this.label1); 124 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 125 | this.MaximizeBox = false; 126 | this.MinimizeBox = false; 127 | this.Name = "ErrorReportWindow"; 128 | this.Padding = new System.Windows.Forms.Padding(4); 129 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 130 | this.Text = "Program Error - Tomighty"; 131 | this.TopMost = true; 132 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ErrorReportWindow_FormClosed); 133 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); 134 | this.ResumeLayout(false); 135 | this.PerformLayout(); 136 | 137 | } 138 | 139 | #endregion 140 | 141 | private System.Windows.Forms.Label label1; 142 | private System.Windows.Forms.TextBox errorDescription; 143 | private System.Windows.Forms.Button reportButton; 144 | private System.Windows.Forms.PictureBox pictureBox1; 145 | private System.Windows.Forms.Label label2; 146 | private System.Windows.Forms.Label progressLabel; 147 | } 148 | } -------------------------------------------------------------------------------- /Tomighty.Windows/ErrorReportWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Net.Http; 6 | using System.Runtime.Serialization; 7 | using System.Runtime.Serialization.Json; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | 11 | namespace Tomighty.Windows 12 | { 13 | public partial class ErrorReportWindow : Form 14 | { 15 | public ErrorReportWindow(Exception exception) 16 | { 17 | InitializeComponent(); 18 | errorDescription.Text = exception.ToString(); 19 | } 20 | 21 | private void ErrorReportWindow_FormClosed(object sender, FormClosedEventArgs e) 22 | { 23 | Application.Exit(); 24 | } 25 | 26 | private void reportButton_Click(object sender, EventArgs e) 27 | { 28 | reportButton.Enabled = false; 29 | progressLabel.Text = "Sending error report, please wait..."; 30 | progressLabel.ForeColor = Color.Blue; 31 | progressLabel.Visible = true; 32 | 33 | var worker = new BackgroundWorker(); 34 | worker.DoWork += SendErrorReport; 35 | worker.RunWorkerCompleted += OnErrorReportSent; 36 | worker.RunWorkerAsync(); 37 | } 38 | 39 | private void SendErrorReport(object sender, EventArgs e) 40 | { 41 | var data = GetReportData(); 42 | var client = new HttpClient(); 43 | var json = ToJson(data); 44 | var content = new StringContent(json, Encoding.UTF8, "application/json"); 45 | var task = client.PostAsync(URLs.ErrorReport, content); 46 | 47 | task.Wait(); 48 | 49 | if (task.Exception != null) 50 | throw new Exception("Failed to send error report", task.Exception); 51 | } 52 | 53 | private static string ToJson(ReportData data) 54 | { 55 | var serializer = new DataContractJsonSerializer(typeof(ReportData)); 56 | var stream = new MemoryStream(); 57 | serializer.WriteObject(stream, data); 58 | return Encoding.UTF8.GetString(stream.ToArray()); 59 | } 60 | 61 | private ReportData GetReportData() 62 | { 63 | return new ReportData 64 | { 65 | version = Version.Product, 66 | host_id = Host.Id, 67 | error = errorDescription.Text 68 | }; 69 | } 70 | 71 | private void OnErrorReportSent(object sender, RunWorkerCompletedEventArgs e) 72 | { 73 | var success = e.Error == null; 74 | if (success) 75 | { 76 | progressLabel.Text = "Done"; 77 | progressLabel.ForeColor = Color.Green; 78 | MessageBox.Show(this, "Thanks for sending the error report. Bye!", "Error Report Sent", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); 79 | Application.Exit(); 80 | } 81 | else 82 | { 83 | reportButton.Enabled = true; 84 | progressLabel.Text = "Failed to send report. Try again later."; 85 | progressLabel.ForeColor = Color.Red; 86 | } 87 | } 88 | 89 | [DataContract] 90 | private class ReportData 91 | { 92 | [DataMember] public string version { get; set; } 93 | [DataMember] public string host_id { get; set; } 94 | [DataMember] public string error { get; set; } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Tomighty.Windows/Events.cs: -------------------------------------------------------------------------------- 1 | namespace Tomighty.Windows.Events 2 | { 3 | public class FirstRun { } 4 | public class AppUpdated { } 5 | } 6 | -------------------------------------------------------------------------------- /Tomighty.Windows/Flags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Tomighty.Windows 5 | { 6 | public class Flags 7 | { 8 | private readonly string dir; 9 | 10 | public Flags(string name) 11 | { 12 | dir = Path.Combine(Directories.AppData, name); 13 | } 14 | 15 | public bool IsOn(string name, bool defaultValue) 16 | { 17 | return ReadFile(name, defaultValue); 18 | } 19 | 20 | public void TurnOn(string name) 21 | { 22 | WriteFile(name, true); 23 | } 24 | 25 | public void TurnOff(string name) 26 | { 27 | WriteFile(name, false); 28 | } 29 | 30 | private string GetFile(string name) => Path.Combine(dir, name); 31 | 32 | private void EnsureDirectoryExists() 33 | { 34 | if (!Directory.Exists(dir)) 35 | Directory.CreateDirectory(dir); 36 | } 37 | 38 | private void WriteFile(string name, bool value) 39 | { 40 | EnsureDirectoryExists(); 41 | File.WriteAllText(GetFile(name), value ? "1" : "0"); 42 | } 43 | 44 | private bool ReadFile(string name, bool defaultValue) 45 | { 46 | EnsureDirectoryExists(); 47 | 48 | var file = GetFile(name); 49 | 50 | if (File.Exists(file)) 51 | return File.ReadAllText(file) == "1" ? true : false; 52 | 53 | return defaultValue; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tomighty.Windows/Host.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.IO; 10 | using System.Management; 11 | using Tomighty.Windows.Util; 12 | 13 | namespace Tomighty.Windows 14 | { 15 | internal class Host 16 | { 17 | private static string id; 18 | 19 | public static string Id 20 | { 21 | get 22 | { 23 | if (id == null) id = GetId(); 24 | return id; 25 | } 26 | } 27 | 28 | private static string GetId() 29 | { 30 | try 31 | { 32 | return ComputeId(); 33 | } 34 | catch 35 | { 36 | try 37 | { 38 | return GenerateId(); 39 | } 40 | catch 41 | { 42 | return "undefined"; 43 | } 44 | } 45 | } 46 | 47 | private static string GenerateId() 48 | { 49 | string uuid = null; 50 | var path = Path.Combine(Directories.AppData, "machine_id"); 51 | 52 | if (File.Exists(path)) 53 | uuid = File.ReadAllText(path); 54 | 55 | if (uuid == null || uuid.Length != Guid.Empty.ToString().Length) 56 | { 57 | uuid = Guid.NewGuid().ToString(); 58 | File.WriteAllText(path, uuid); 59 | } 60 | 61 | return uuid; 62 | } 63 | 64 | private static string ComputeId() 65 | { 66 | var mgmt = new ManagementClass("win32_processor"); 67 | var data = ""; 68 | foreach (ManagementObject mo in mgmt.GetInstances()) 69 | data += GetProcessorInfo(mo); 70 | return Hash.Sha1(data); 71 | } 72 | 73 | private static string GetProcessorInfo(ManagementObject mo) 74 | { 75 | return "[" + 76 | mo.Properties["processorID"].Value + "|" + 77 | mo.Properties["Architecture"].Value + "|" + 78 | mo.Properties["Family"].Value + "|" + 79 | mo.Properties["Caption"].Value + "]"; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Tomighty.Windows/IntervalTypeExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using Tomighty.Windows.Properties; 10 | 11 | namespace Tomighty.Windows 12 | { 13 | internal static class IntervalTypeExtensions 14 | { 15 | public static string GetName(this IntervalType intervalType) 16 | { 17 | switch (intervalType) 18 | { 19 | case IntervalType.Pomodoro: return Resources.String_Pomodoro; 20 | case IntervalType.ShortBreak: return Resources.String_ShortBreak; 21 | case IntervalType.LongBreak: return Resources.String_LongBreak; 22 | default: throw new ArgumentException($"Unknown interval type: {intervalType}"); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tomighty.Windows/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /Tomighty.Windows/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Tomighty.Windows 5 | { 6 | class Logger : IDisposable 7 | { 8 | private const long MaxFileSize = 1024 * 512; //512KB 9 | 10 | private readonly StreamWriter writer; 11 | 12 | public Logger(string name) 13 | { 14 | writer = new StreamWriter(GetFile(name + ".log")); 15 | } 16 | 17 | private FileStream GetFile(string name) 18 | { 19 | var path = Path.Combine(Directories.AppData, name); 20 | 21 | if (HasReachedSizeLimit(path)) 22 | { 23 | try 24 | { 25 | File.Delete(path); 26 | } 27 | catch 28 | { 29 | //That's ok, let's not break the program just because 30 | //we can't delete the log file 31 | } 32 | } 33 | return new FileStream(path, FileMode.Append); 34 | } 35 | 36 | private bool HasReachedSizeLimit(string filepath) 37 | { 38 | return File.Exists(filepath) 39 | && new FileInfo(filepath).Length > MaxFileSize; 40 | } 41 | 42 | private void Log(string level, string msg) 43 | { 44 | writer.WriteLine($"{DateTimeOffset.Now.ToString()} [{level}] {msg}"); 45 | writer.Flush(); 46 | } 47 | 48 | public void Info(string msg) 49 | { 50 | Log("INFO", msg); 51 | } 52 | 53 | public void Error(string msg) 54 | { 55 | Log("ERROR", msg); 56 | } 57 | 58 | public void Error(Exception e) 59 | { 60 | Error(e.ToString()); 61 | } 62 | 63 | public void Dispose() 64 | { 65 | if (writer != null) 66 | writer.Close(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Tomighty.Windows/NOTICE.txt: -------------------------------------------------------------------------------- 1 | ========================================================================= 2 | == NOTICE file corresponding to section 4(d) of the Apache License, == 3 | == Version 2.0, in this case for the Tomighty distribution. == 4 | ========================================================================= 5 | 6 | Tomighty is a software developed by Célio Cidral Junior. You can find it 7 | at http://www.tomighty.org 8 | 9 | Pomodoro Technique® and Pomodoro™ are registered and filed trademarks 10 | owned by Francesco Cirillo. Tomighty is not affiliated by, associated 11 | with nor endorsed by Francesco Cirillo. 12 | 13 | Tomato icon designed by José Campos 14 | 15 | Clock icon designed by Thomas Le Bas 16 | -------------------------------------------------------------------------------- /Tomighty.Windows/Notifications/NotificationsPresenter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using Tomighty.Events; 9 | using Tomighty.Windows.Events; 10 | using Windows.UI.Notifications; 11 | 12 | namespace Tomighty.Windows.Notifications 13 | { 14 | internal class NotificationsPresenter 15 | { 16 | private readonly IPomodoroEngine pomodoroEngine; 17 | private readonly IUserPreferences userPreferences; 18 | private readonly ToastNotifier toastNotifier = ToastNotificationManager.CreateToastNotifier("Tomighty"); 19 | 20 | public NotificationsPresenter(IPomodoroEngine pomodoroEngine, IUserPreferences userPreferences, IEventHub eventHub) 21 | { 22 | this.pomodoroEngine = pomodoroEngine; 23 | this.userPreferences = userPreferences; 24 | eventHub.Subscribe(OnTimerStopped); 25 | eventHub.Subscribe(OnAppUpdated); 26 | eventHub.Subscribe(OnFirstRun); 27 | } 28 | 29 | private void OnFirstRun(FirstRun @event) 30 | { 31 | var toast = Toasts.FirstRun(); 32 | toastNotifier.Show(toast); 33 | } 34 | 35 | private void OnAppUpdated(AppUpdated @event) 36 | { 37 | toastNotifier.Show(Toasts.AppUpdated()); 38 | } 39 | 40 | private void OnTimerStopped(TimerStopped @event) 41 | { 42 | if (@event.IsIntervalCompleted && userPreferences.ShowToastNotifications) 43 | { 44 | var toast = Toasts.IntervalCompleted(@event.IntervalType, pomodoroEngine.SuggestedBreakType); 45 | toast.Activated += OnToastActivated; 46 | toastNotifier.Show(toast); 47 | } 48 | } 49 | 50 | private void OnToastActivated(ToastNotification sender, object args) 51 | { 52 | if (args is ToastActivatedEventArgs) 53 | { 54 | var activation = args as ToastActivatedEventArgs; 55 | 56 | if (Toasts.TimerAction.WithArgs.ContainsKey(activation.Arguments)) 57 | { 58 | var timerAction = Toasts.TimerAction.WithArgs[activation.Arguments]; 59 | 60 | pomodoroEngine.StartTimer(timerAction.IntervalType); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tomighty.Windows/Notifications/SoundNotificationPlayer.cs: -------------------------------------------------------------------------------- 1 | using System.Media; 2 | using Tomighty.Events; 3 | 4 | namespace Tomighty.Windows.Notifications 5 | { 6 | public class SoundNotificationPlayer 7 | { 8 | private readonly IUserPreferences userPreferences; 9 | private readonly SoundPlayer intervalCompletedNotification = new SoundPlayer(Properties.Resources.audio_deskbell); 10 | 11 | public SoundNotificationPlayer(IUserPreferences userPreferences, IEventHub eventHub) 12 | { 13 | this.userPreferences = userPreferences; 14 | eventHub.Subscribe(OnTimerStopped); 15 | } 16 | 17 | private void OnTimerStopped(TimerStopped @event) 18 | { 19 | if (@event.IsIntervalCompleted && userPreferences.PlaySoundNotifications) 20 | { 21 | intervalCompletedNotification.Play(); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tomighty.Windows/Notifications/Toasts.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.IO; 11 | using Windows.Data.Xml.Dom; 12 | using Windows.UI.Notifications; 13 | 14 | namespace Tomighty.Windows.Notifications 15 | { 16 | internal static class Toasts 17 | { 18 | private static readonly string RedTomatoImage = new Uri(Path.GetFullPath(@"Resources\Toasts\image_toast_tomato_red.png")).AbsoluteUri; 19 | private static readonly string GreenTomatoImage = new Uri(Path.GetFullPath(@"Resources\Toasts\image_toast_tomato_green.png")).AbsoluteUri; 20 | private static readonly string BlueTomatoImage = new Uri(Path.GetFullPath(@"Resources\Toasts\image_toast_tomato_blue.png")).AbsoluteUri; 21 | private static readonly XmlDocument PomodoroCompletedTakeShortBreakTemplate = FillIntervalCompletedTemplate("Pomodoro", RedTomatoImage, TimerAction.StartShortBreak); 22 | private static readonly XmlDocument PomodoroCompletedTakeLongBreakTemplate = FillIntervalCompletedTemplate("Pomodoro", RedTomatoImage, TimerAction.StartLongBreak); 23 | private static readonly XmlDocument ShortBreakCompletedTemplate = FillIntervalCompletedTemplate("Short break", GreenTomatoImage, TimerAction.StartPomodoro); 24 | private static readonly XmlDocument LongBreakCompletedTemplate = FillIntervalCompletedTemplate("Long break", BlueTomatoImage, TimerAction.StartPomodoro); 25 | private static readonly XmlDocument FirstRunTemplate = ToXmlDocument(Properties.Resources.toast_template_first_run.Replace("{image_src}", RedTomatoImage)); 26 | private static readonly XmlDocument AppUpdatedTemplate = ToXmlDocument(Properties.Resources.toast_template_app_updated.Replace("{image_src}", RedTomatoImage)); 27 | 28 | public static ToastNotification FirstRun() 29 | { 30 | return new ToastNotification(FirstRunTemplate); 31 | } 32 | 33 | public static ToastNotification AppUpdated() 34 | { 35 | return new ToastNotification(AppUpdatedTemplate); 36 | } 37 | 38 | public static ToastNotification IntervalCompleted(IntervalType completedIntervalType, IntervalType suggestedBreakType) 39 | { 40 | if (completedIntervalType == IntervalType.Pomodoro && suggestedBreakType == IntervalType.ShortBreak) 41 | return new ToastNotification(PomodoroCompletedTakeShortBreakTemplate); 42 | 43 | if (completedIntervalType == IntervalType.Pomodoro && suggestedBreakType == IntervalType.LongBreak) 44 | return new ToastNotification(PomodoroCompletedTakeLongBreakTemplate); 45 | 46 | if (completedIntervalType == IntervalType.ShortBreak) 47 | return new ToastNotification(ShortBreakCompletedTemplate); 48 | 49 | if (completedIntervalType == IntervalType.LongBreak) 50 | return new ToastNotification(LongBreakCompletedTemplate); 51 | 52 | throw new ArgumentException($"Invalid arguments: {nameof(completedIntervalType)}={completedIntervalType} {nameof(suggestedBreakType)}={suggestedBreakType}"); 53 | } 54 | 55 | private static XmlDocument FillIntervalCompletedTemplate(string intervalType, string imageSrc, TimerAction action) 56 | { 57 | return ToXmlDocument(Properties.Resources.toast_template_interval_completed 58 | .Replace("{interval_type}", intervalType) 59 | .Replace("{image_src}", imageSrc) 60 | .Replace("{action_content}", action.Content) 61 | .Replace("{action_args}", action.Args)); 62 | } 63 | 64 | private static XmlDocument ToXmlDocument(string xml) 65 | { 66 | var doc = new XmlDocument(); 67 | doc.LoadXml(xml); 68 | return doc; 69 | } 70 | 71 | public class TimerAction 72 | { 73 | public static readonly TimerAction StartPomodoro = new TimerAction("start_pomodoro", "Start pomodoro", IntervalType.Pomodoro); 74 | public static readonly TimerAction StartShortBreak = new TimerAction("start_short_break", "Take a short break", IntervalType.ShortBreak); 75 | public static readonly TimerAction StartLongBreak = new TimerAction("start_long_break", "Take a long break", IntervalType.LongBreak); 76 | 77 | public static readonly IDictionary WithArgs = new Dictionary 78 | { 79 | { StartPomodoro.Args, StartPomodoro }, 80 | { StartShortBreak.Args, StartShortBreak }, 81 | { StartLongBreak.Args, StartLongBreak } 82 | }; 83 | 84 | private TimerAction(string args, string content, IntervalType intervalType) 85 | { 86 | Args = args; 87 | Content = content; 88 | IntervalType = intervalType; 89 | } 90 | 91 | public string Args { get; } 92 | public string Content { get; } 93 | public IntervalType IntervalType { get; } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Tomighty.Windows/Preferences/UserPreferences.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.IO; 10 | using System.Runtime.Serialization; 11 | using System.Runtime.Serialization.Json; 12 | 13 | namespace Tomighty.Windows.Preferences 14 | { 15 | public class UserPreferences : IMutableUserPreferences 16 | { 17 | private static readonly string FilePath = Path.Combine(Directories.AppData, "preferences.json"); 18 | private static readonly DataContractJsonSerializer Json = new DataContractJsonSerializer(typeof(Values)); 19 | 20 | private readonly Values values; 21 | 22 | public UserPreferences() 23 | { 24 | values = ReadFromFile() ?? DefaultValues; 25 | } 26 | 27 | private static Values ReadFromFile() 28 | { 29 | if (!File.Exists(FilePath)) 30 | return null; 31 | 32 | using (var file = File.OpenRead(FilePath)) 33 | { 34 | try 35 | { 36 | return ReplaceInvalidSettingsWithDefaultValues((Values)Json.ReadObject(file)); 37 | } 38 | catch(SerializationException) 39 | { 40 | return DefaultValues; 41 | } 42 | } 43 | } 44 | 45 | private static Values ReplaceInvalidSettingsWithDefaultValues(Values values) 46 | { 47 | if (values.PomodoroDuration <= 0) values.PomodoroDuration = DefaultValues.PomodoroDuration; 48 | if (values.ShortBreakDuration <= 0) values.ShortBreakDuration = DefaultValues.ShortBreakDuration; 49 | if (values.LongBreakDuration <= 0) values.LongBreakDuration = DefaultValues.LongBreakDuration; 50 | if (values.MaxPomodoroCount <= 0) values.MaxPomodoroCount = DefaultValues.MaxPomodoroCount; 51 | return values; 52 | } 53 | 54 | private static Values DefaultValues => new Values 55 | { 56 | PomodoroDuration = Duration.InMinutes(25).Seconds, 57 | ShortBreakDuration = Duration.InMinutes(5).Seconds, 58 | LongBreakDuration = Duration.InMinutes(15).Seconds, 59 | MaxPomodoroCount = 4, 60 | ShowToastNotifications = true, 61 | PlaySoundNotifications = true, 62 | AutoUpdate = true 63 | }; 64 | 65 | public Duration GetIntervalDuration(IntervalType intervalType) 66 | { 67 | if (intervalType == IntervalType.Pomodoro) return new Duration(values.PomodoroDuration); 68 | if (intervalType == IntervalType.ShortBreak) return new Duration(values.ShortBreakDuration); 69 | if (intervalType == IntervalType.LongBreak) return new Duration(values.LongBreakDuration); 70 | throw new ArgumentException($"Unsupported interval type: {intervalType}"); 71 | } 72 | 73 | public void SetIntervalDuration(IntervalType intervalType, Duration duration) 74 | { 75 | if (intervalType == IntervalType.Pomodoro) values.PomodoroDuration = duration.Seconds; 76 | else if (intervalType == IntervalType.ShortBreak) values.ShortBreakDuration = duration.Seconds; 77 | else if (intervalType == IntervalType.LongBreak) values.LongBreakDuration = duration.Seconds; 78 | else throw new ArgumentException($"Unsupported interval type: {intervalType}"); 79 | } 80 | 81 | public int MaxPomodoroCount 82 | { 83 | get { return values.MaxPomodoroCount; } 84 | set { values.MaxPomodoroCount = value; } 85 | } 86 | 87 | public bool ShowToastNotifications 88 | { 89 | get { return values.ShowToastNotifications; } 90 | set { values.ShowToastNotifications = value; } 91 | } 92 | 93 | public bool PlaySoundNotifications 94 | { 95 | get { return values.PlaySoundNotifications; } 96 | set { values.PlaySoundNotifications = value; } 97 | } 98 | 99 | public bool AutoUpdate 100 | { 101 | get { return values.AutoUpdate; } 102 | set { values.AutoUpdate = value; } 103 | } 104 | 105 | public void Update(Action action) 106 | { 107 | action(this); 108 | using (var file = new FileStream(FilePath, FileMode.Create)) 109 | { 110 | Json.WriteObject(file, values); 111 | } 112 | } 113 | 114 | [DataContract] 115 | private class Values 116 | { 117 | [DataMember] public int PomodoroDuration { get; set; } 118 | [DataMember] public int ShortBreakDuration { get; set; } 119 | [DataMember] public int LongBreakDuration { get; set; } 120 | [DataMember] public int MaxPomodoroCount { get; set; } 121 | [DataMember] public bool ShowToastNotifications { get; set; } 122 | [DataMember] public bool PlaySoundNotifications { get; set; } 123 | [DataMember] public bool AutoUpdate { get; set; } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Tomighty.Windows/Preferences/UserPreferencesForm.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Windows.Forms; 9 | 10 | namespace Tomighty.Windows.Preferences 11 | { 12 | public partial class UserPreferencesForm : Form 13 | { 14 | private IUserPreferences userPreferences; 15 | 16 | public UserPreferencesForm(IUserPreferences userPreferences) 17 | { 18 | this.userPreferences = userPreferences; 19 | 20 | InitializeComponent(); 21 | 22 | pomodoroDurationTextBox.Value = userPreferences.GetIntervalDuration(IntervalType.Pomodoro).Minutes; 23 | shortBreakDurationTextBox.Value = userPreferences.GetIntervalDuration(IntervalType.ShortBreak).Minutes; 24 | longBreakDurationTextBox.Value = userPreferences.GetIntervalDuration(IntervalType.LongBreak).Minutes; 25 | maxPomodoroCountTextBox.Value = userPreferences.MaxPomodoroCount; 26 | toastNotification.Checked = userPreferences.ShowToastNotifications; 27 | soundNotifications.Checked = userPreferences.PlaySoundNotifications; 28 | autoupdate.Checked = userPreferences.AutoUpdate; 29 | } 30 | 31 | private void OnCancelButtonClick(object sender, System.EventArgs e) 32 | { 33 | Close(); 34 | } 35 | 36 | private void OnOkButtonClick(object sender, System.EventArgs e) 37 | { 38 | userPreferences.Update(newPreferences => 39 | { 40 | newPreferences.SetIntervalDuration(IntervalType.Pomodoro, Duration.InMinutes((int)pomodoroDurationTextBox.Value)); 41 | newPreferences.SetIntervalDuration(IntervalType.ShortBreak, Duration.InMinutes((int)shortBreakDurationTextBox.Value)); 42 | newPreferences.SetIntervalDuration(IntervalType.LongBreak, Duration.InMinutes((int)longBreakDurationTextBox.Value)); 43 | newPreferences.MaxPomodoroCount = (int)maxPomodoroCountTextBox.Value; 44 | newPreferences.ShowToastNotifications = toastNotification.Checked; 45 | newPreferences.PlaySoundNotifications = soundNotifications.Checked; 46 | newPreferences.AutoUpdate = autoupdate.Checked; 47 | }); 48 | 49 | Close(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tomighty.Windows/Preferences/UserPreferencesForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Tomighty.Windows/Preferences/UserPreferencesPresenter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Windows.Forms; 9 | 10 | namespace Tomighty.Windows.Preferences 11 | { 12 | internal class UserPreferencesPresenter 13 | { 14 | private readonly IUserPreferences userPreferences; 15 | private UserPreferencesForm window; 16 | 17 | public UserPreferencesPresenter(IUserPreferences userPreferences) 18 | { 19 | this.userPreferences = userPreferences; 20 | } 21 | 22 | public void Show() 23 | { 24 | if (window == null) 25 | { 26 | window = new UserPreferencesForm(userPreferences); 27 | window.FormClosed += OnWindowClosed; 28 | } 29 | 30 | if (window.Visible) 31 | { 32 | window.Focus(); 33 | } 34 | else 35 | { 36 | window.ShowDialog(); 37 | } 38 | } 39 | 40 | private void OnWindowClosed(object sender, FormClosedEventArgs e) 41 | { 42 | window.Dispose(); 43 | window = null; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tomighty.Windows/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Windows.Forms; 10 | 11 | namespace Tomighty.Windows 12 | { 13 | static class Program 14 | { 15 | [STAThread] 16 | static void Main() 17 | { 18 | Application.EnableVisualStyles(); 19 | Application.SetCompatibleTextRenderingDefault(false); 20 | AppDomain.CurrentDomain.UnhandledException += (sender, args) => HandleUnhandledException(args.ExceptionObject as Exception); 21 | Application.ThreadException += (sender, args) => HandleUnhandledException(args.Exception); 22 | 23 | try 24 | { 25 | Application.Run(new TomightyApplication()); 26 | } 27 | catch(Exception e) 28 | { 29 | Application.Run(new ErrorReportWindow(e)); 30 | } 31 | } 32 | 33 | private static void HandleUnhandledException(Exception exception) 34 | { 35 | new ErrorReportWindow(exception).Show(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tomighty.Windows/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.Reflection; 9 | using System.Runtime.InteropServices; 10 | 11 | [assembly: AssemblyTitle("Tomighty")] 12 | [assembly: AssemblyDescription("Tomighty for Windows")] 13 | [assembly: ComVisible(false)] 14 | [assembly: Guid("f45108d8-e103-4db9-a43f-82f864fd9217")] 15 | -------------------------------------------------------------------------------- /Tomighty.Windows/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\resources\icon_tomato_black.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\resources\icon_tomato_blue.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\resources\icon_tomato_green.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\resources\icon_tomato_red.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | 134 | ..\resources\icon_tomato_white.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 135 | 136 | 137 | ..\resources\image_clock.tiff;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 138 | 139 | 140 | ..\resources\image_pinned.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 141 | 142 | 143 | ..\resources\image_stop.tiff;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 144 | 145 | 146 | ..\resources\image_tomato_black.tiff;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 147 | 148 | 149 | ..\resources\image_tomato_blue.tiff;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 150 | 151 | 152 | ..\resources\image_tomato_green.tiff;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 153 | 154 | 155 | ..\resources\image_tomato_red.tiff;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 156 | 157 | 158 | ..\resources\image_unpinned.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 159 | 160 | 161 | ..\resources\image_x.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 162 | 163 | 164 | Long break 165 | 166 | 167 | Pomodoro 168 | 169 | 170 | Short break 171 | 172 | 173 | ..\Resources\Toasts\interval-completed.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 174 | 175 | 176 | ..\Resources\Audio\deskbell.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 177 | 178 | 179 | ..\Resources\image_warning.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 180 | 181 | 182 | ..\Resources\Toasts\first-run.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 183 | 184 | 185 | ..\Resources\Toasts\app-updated.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 186 | 187 | -------------------------------------------------------------------------------- /Tomighty.Windows/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Tomighty.Windows.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tomighty.Windows/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/Audio/deskbell.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/Audio/deskbell.wav -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/Toasts/app-updated.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Tomighty Updated 6 | A new version of Tomighty has been automatically installed 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/Toasts/first-run.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | First time using Tomighty? 6 | There should be a tomato-shaped icon somewhere in your task bar. 7 | Start by clicking on it. The left button shows the menu. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/Toasts/image_toast_tomato_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/Toasts/image_toast_tomato_blue.png -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/Toasts/image_toast_tomato_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/Toasts/image_toast_tomato_green.png -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/Toasts/image_toast_tomato_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/Toasts/image_toast_tomato_red.png -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/Toasts/interval-completed.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {interval_type} completed 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/icon_tomato_black.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/icon_tomato_black.ico -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/icon_tomato_blue.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/icon_tomato_blue.ico -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/icon_tomato_green.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/icon_tomato_green.ico -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/icon_tomato_red.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/icon_tomato_red.ico -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/icon_tomato_white.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/icon_tomato_white.ico -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_clock.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_clock.tiff -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_pinned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_pinned.png -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_stop.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_stop.tiff -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_tomato_black.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_tomato_black.tiff -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_tomato_blue.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_tomato_blue.tiff -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_tomato_green.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_tomato_green.tiff -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_tomato_red.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_tomato_red.tiff -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_unpinned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_unpinned.png -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_warning.png -------------------------------------------------------------------------------- /Tomighty.Windows/Resources/image_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-windows/d0558daa418209a922e973d18bb5b440586424bb/Tomighty.Windows/Resources/image_x.png -------------------------------------------------------------------------------- /Tomighty.Windows/StartupEventFlags.cs: -------------------------------------------------------------------------------- 1 | namespace Tomighty.Windows 2 | { 3 | public class StartupEventFlags 4 | { 5 | public static readonly Flags Flags = new Flags("startup"); 6 | public static readonly string FirstRunFlag = "firstrun"; 7 | public static readonly string AppUpdatedFlag = "app_updated"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tomighty.Windows/StartupEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Tomighty.Windows.Events; 4 | 5 | namespace Tomighty.Windows 6 | { 7 | public class StartupEvents : StartupEventFlags 8 | { 9 | public StartupEvents(IEventHub eventHub) 10 | { 11 | Task.Run(() => 12 | { 13 | if (Flags.IsOn(FirstRunFlag, true)) 14 | { 15 | Flags.TurnOff(FirstRunFlag); 16 | eventHub.Publish(new FirstRun()); 17 | } 18 | 19 | if (Flags.IsOn(AppUpdatedFlag, false)) 20 | { 21 | Flags.TurnOff(AppUpdatedFlag); 22 | eventHub.Publish(new AppUpdated()); 23 | } 24 | }); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tomighty.Windows/Timer/Taskbar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Runtime.InteropServices; 4 | 5 | // Shamelessly copied from https://winsharp93.wordpress.com/2009/06/29/find-out-size-and-position-of-the-taskbar/ 6 | 7 | namespace Tomighty.Windows.Timer 8 | { 9 | public enum TaskbarPosition 10 | { 11 | Unknown = -1, 12 | Left, 13 | Top, 14 | Right, 15 | Bottom, 16 | } 17 | 18 | public sealed class Taskbar 19 | { 20 | private const string ClassName = "Shell_TrayWnd"; 21 | 22 | public Taskbar() 23 | { 24 | IntPtr taskbarHandle = User32.FindWindow(Taskbar.ClassName, null); 25 | 26 | APPBARDATA data = new APPBARDATA(); 27 | data.cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA)); 28 | data.hWnd = taskbarHandle; 29 | IntPtr result = Shell32.SHAppBarMessage(ABM.GetTaskbarPos, ref data); 30 | if (result == IntPtr.Zero) 31 | throw new InvalidOperationException(); 32 | 33 | this.Position = (TaskbarPosition)data.uEdge; 34 | this.Bounds = Rectangle.FromLTRB(data.rc.left, data.rc.top, data.rc.right, data.rc.bottom); 35 | 36 | data.cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA)); 37 | result = Shell32.SHAppBarMessage(ABM.GetState, ref data); 38 | int state = result.ToInt32(); 39 | this.AlwaysOnTop = (state & ABS.AlwaysOnTop) == ABS.AlwaysOnTop; 40 | this.AutoHide = (state & ABS.Autohide) == ABS.Autohide; 41 | } 42 | 43 | public Rectangle Bounds 44 | { 45 | get; 46 | private set; 47 | } 48 | 49 | public TaskbarPosition Position 50 | { 51 | get; 52 | private set; 53 | } 54 | 55 | public Point Location 56 | { 57 | get 58 | { 59 | return this.Bounds.Location; 60 | } 61 | } 62 | 63 | public Size Size 64 | { 65 | get 66 | { 67 | return this.Bounds.Size; 68 | } 69 | } 70 | 71 | //Always returns false under Windows 7 72 | public bool AlwaysOnTop 73 | { 74 | get; 75 | private set; 76 | } 77 | 78 | public bool AutoHide 79 | { 80 | get; 81 | private set; 82 | } 83 | } 84 | 85 | public enum ABM : uint 86 | { 87 | New = 0x00000000, 88 | Remove = 0x00000001, 89 | QueryPos = 0x00000002, 90 | SetPos = 0x00000003, 91 | GetState = 0x00000004, 92 | GetTaskbarPos = 0x00000005, 93 | Activate = 0x00000006, 94 | GetAutoHideBar = 0x00000007, 95 | SetAutoHideBar = 0x00000008, 96 | WindowPosChanged = 0x00000009, 97 | SetState = 0x0000000A, 98 | } 99 | 100 | public enum ABE : uint 101 | { 102 | Left = 0, 103 | Top = 1, 104 | Right = 2, 105 | Bottom = 3 106 | } 107 | 108 | public static class ABS 109 | { 110 | public const int Autohide = 0x0000001; 111 | public const int AlwaysOnTop = 0x0000002; 112 | } 113 | 114 | public static class Shell32 115 | { 116 | [DllImport("shell32.dll", SetLastError = true)] 117 | public static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData); 118 | } 119 | 120 | public static class User32 121 | { 122 | [DllImport("user32.dll", SetLastError = true)] 123 | public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 124 | } 125 | 126 | [StructLayout(LayoutKind.Sequential)] 127 | public struct APPBARDATA 128 | { 129 | public uint cbSize; 130 | public IntPtr hWnd; 131 | public uint uCallbackMessage; 132 | public ABE uEdge; 133 | public RECT rc; 134 | public int lParam; 135 | } 136 | 137 | [StructLayout(LayoutKind.Sequential)] 138 | public struct RECT 139 | { 140 | public int left; 141 | public int top; 142 | public int right; 143 | public int bottom; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Tomighty.Windows/Timer/TimerWindow.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty.Windows.Timer 9 | { 10 | partial class TimerWindow 11 | { 12 | /// 13 | /// Required designer variable. 14 | /// 15 | private System.ComponentModel.IContainer components = null; 16 | 17 | /// 18 | /// Clean up any resources being used. 19 | /// 20 | /// true if managed resources should be disposed; otherwise, false. 21 | protected override void Dispose(bool disposing) 22 | { 23 | if (disposing && (components != null)) 24 | { 25 | components.Dispose(); 26 | } 27 | base.Dispose(disposing); 28 | } 29 | 30 | #region Windows Form Designer generated code 31 | 32 | /// 33 | /// Required method for Designer support - do not modify 34 | /// the contents of this method with the code editor. 35 | /// 36 | private void InitializeComponent() 37 | { 38 | this.timeLabel = new Tomighty.Windows.Timer.TimerWindow.TransparentLabel(); 39 | this.pinButton = new System.Windows.Forms.Button(); 40 | this.closeButton = new System.Windows.Forms.Button(); 41 | this.titleLabel = new Tomighty.Windows.Timer.TimerWindow.TransparentLabel(); 42 | this.timerButton = new System.Windows.Forms.Button(); 43 | this.SuspendLayout(); 44 | // 45 | // timeLabel 46 | // 47 | this.timeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 48 | | System.Windows.Forms.AnchorStyles.Left) 49 | | System.Windows.Forms.AnchorStyles.Right))); 50 | this.timeLabel.BackColor = System.Drawing.Color.Transparent; 51 | this.timeLabel.ForeColor = System.Drawing.Color.White; 52 | this.timeLabel.Location = new System.Drawing.Point(12, 28); 53 | this.timeLabel.Name = "timeLabel"; 54 | this.timeLabel.Size = new System.Drawing.Size(153, 67); 55 | this.timeLabel.TabIndex = 0; 56 | this.timeLabel.Text = "--:--"; 57 | this.timeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 58 | // 59 | // pinButton 60 | // 61 | this.pinButton.CausesValidation = false; 62 | this.pinButton.FlatAppearance.BorderSize = 0; 63 | this.pinButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 64 | this.pinButton.Image = global::Tomighty.Windows.Properties.Resources.image_unpinned; 65 | this.pinButton.Location = new System.Drawing.Point(2, 2); 66 | this.pinButton.Name = "pinButton"; 67 | this.pinButton.Size = new System.Drawing.Size(22, 23); 68 | this.pinButton.TabIndex = 1; 69 | this.pinButton.TabStop = false; 70 | this.pinButton.UseVisualStyleBackColor = true; 71 | // 72 | // closeButton 73 | // 74 | this.closeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 75 | this.closeButton.CausesValidation = false; 76 | this.closeButton.FlatAppearance.BorderSize = 0; 77 | this.closeButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 78 | this.closeButton.Image = global::Tomighty.Windows.Properties.Resources.image_x; 79 | this.closeButton.Location = new System.Drawing.Point(152, 2); 80 | this.closeButton.Name = "closeButton"; 81 | this.closeButton.Size = new System.Drawing.Size(22, 23); 82 | this.closeButton.TabIndex = 2; 83 | this.closeButton.TabStop = false; 84 | this.closeButton.UseVisualStyleBackColor = true; 85 | // 86 | // titleLabel 87 | // 88 | this.titleLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 89 | | System.Windows.Forms.AnchorStyles.Right))); 90 | this.titleLabel.BackColor = System.Drawing.Color.Transparent; 91 | this.titleLabel.ForeColor = System.Drawing.Color.White; 92 | this.titleLabel.Location = new System.Drawing.Point(31, 8); 93 | this.titleLabel.Name = "titleLabel"; 94 | this.titleLabel.Size = new System.Drawing.Size(115, 30); 95 | this.titleLabel.TabIndex = 3; 96 | this.titleLabel.Text = "Title"; 97 | this.titleLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 98 | // 99 | // timerButton 100 | // 101 | this.timerButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 102 | | System.Windows.Forms.AnchorStyles.Right))); 103 | this.timerButton.BackColor = System.Drawing.Color.Silver; 104 | this.timerButton.FlatAppearance.BorderColor = System.Drawing.Color.DarkGray; 105 | this.timerButton.FlatAppearance.MouseDownBackColor = System.Drawing.Color.Silver; 106 | this.timerButton.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Silver; 107 | this.timerButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 108 | this.timerButton.Location = new System.Drawing.Point(12, 98); 109 | this.timerButton.Name = "timerButton"; 110 | this.timerButton.Size = new System.Drawing.Size(153, 28); 111 | this.timerButton.TabIndex = 4; 112 | this.timerButton.Text = "Timer Action"; 113 | this.timerButton.UseVisualStyleBackColor = false; 114 | this.timerButton.Click += new System.EventHandler(this.OnTimerButtonClick); 115 | // 116 | // TimerWindow 117 | // 118 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 119 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 120 | this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); 121 | this.ClientSize = new System.Drawing.Size(177, 138); 122 | this.ControlBox = false; 123 | this.Controls.Add(this.timerButton); 124 | this.Controls.Add(this.titleLabel); 125 | this.Controls.Add(this.closeButton); 126 | this.Controls.Add(this.pinButton); 127 | this.Controls.Add(this.timeLabel); 128 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; 129 | this.MaximizeBox = false; 130 | this.MinimizeBox = false; 131 | this.Name = "TimerWindow"; 132 | this.ShowIcon = false; 133 | this.ShowInTaskbar = false; 134 | this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; 135 | this.Text = "Tomighty"; 136 | this.TopMost = true; 137 | this.ResumeLayout(false); 138 | 139 | } 140 | 141 | #endregion 142 | 143 | private TransparentLabel timeLabel; 144 | private System.Windows.Forms.Button pinButton; 145 | private System.Windows.Forms.Button closeButton; 146 | private TransparentLabel titleLabel; 147 | private System.Windows.Forms.Button timerButton; 148 | } 149 | } -------------------------------------------------------------------------------- /Tomighty.Windows/Timer/TimerWindow.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Drawing; 10 | using System.Drawing.Drawing2D; 11 | using System.Windows.Forms; 12 | 13 | namespace Tomighty.Windows.Timer 14 | { 15 | public partial class TimerWindow : Form 16 | { 17 | public static readonly Color DarkGray = Color.FromArgb(41, 41, 41); 18 | public static readonly Color Red = Color.FromArgb(168, 32, 41); 19 | public static readonly Color Green = Color.FromArgb(26, 102, 41); 20 | public static readonly Color Blue = Color.FromArgb(22, 57, 101); 21 | 22 | private ColorScheme currentColorScheme; 23 | private Action currentTimerAction; 24 | 25 | private readonly Action UpdateTimeDisplayDelegate; 26 | private readonly Action UpdateTitleDelegate; 27 | private readonly Action UpdateTimerButtonTextDelegate; 28 | 29 | public TimerWindow() 30 | { 31 | InitializeComponent(); 32 | 33 | currentColorScheme = CreateColorScheme(DarkGray); 34 | 35 | SetTransparentBackground(pinButton); 36 | SetTransparentBackground(closeButton); 37 | Icon = Properties.Resources.icon_tomato_red; 38 | 39 | timeLabel.Font = new Font(SystemFonts.DefaultFont.FontFamily, 32f); 40 | 41 | UpdateTimeDisplayDelegate = (text) => timeLabel.Text = text; 42 | UpdateTitleDelegate = (text) => titleLabel.Text = text; 43 | UpdateTimerButtonTextDelegate = (text, action) => timerButton.Text = text; 44 | } 45 | 46 | public Button PinButton => pinButton; 47 | public Button CloseButton => closeButton; 48 | 49 | public void UpdateTitle(string text) 50 | { 51 | if (IsHandleCreated) 52 | timeLabel.BeginInvoke(UpdateTitleDelegate, text); 53 | else 54 | UpdateTitleDelegate(text); 55 | } 56 | 57 | public void SetTimerAction(string text, Action action) 58 | { 59 | currentTimerAction = action; 60 | 61 | if (IsHandleCreated) 62 | timerButton.BeginInvoke(UpdateTimerButtonTextDelegate, text, action); 63 | else 64 | UpdateTimerButtonTextDelegate(text, action); 65 | } 66 | 67 | public void UpdateTimeDisplay(string text) 68 | { 69 | if (IsHandleCreated) 70 | timeLabel.BeginInvoke(UpdateTimeDisplayDelegate, text); 71 | else 72 | UpdateTimeDisplayDelegate(text); 73 | } 74 | 75 | public void UpdateColorScheme(Color color) 76 | { 77 | currentColorScheme = CreateColorScheme(color); 78 | Invalidate(); 79 | } 80 | 81 | public void UpdatePinButtonState(bool pinned) 82 | { 83 | pinButton.Image = pinned ? Properties.Resources.image_pinned : Properties.Resources.image_unpinned; 84 | } 85 | 86 | public void Show(Point location) 87 | { 88 | Location = location; 89 | Show(); 90 | } 91 | 92 | private static void SetTransparentBackground(Button button) 93 | { 94 | button.BackColor = Color.Transparent; 95 | button.FlatAppearance.CheckedBackColor = Color.Transparent; 96 | button.FlatAppearance.MouseDownBackColor = Color.Transparent; 97 | button.FlatAppearance.MouseOverBackColor = Color.Transparent; 98 | } 99 | 100 | protected override void OnPaintBackground(PaintEventArgs e) 101 | { 102 | var background = new Rectangle(0, 0, Width, Height); 103 | var border = new Rectangle(0, 0, Width - 1, Height - 1); 104 | e.Graphics.FillRectangle(currentColorScheme.Fill, background); 105 | e.Graphics.DrawRectangle(currentColorScheme.Border, border); 106 | } 107 | 108 | private class TransparentLabel : Label 109 | { 110 | private const int WM_NCHITTEST = 0x0084; 111 | private const int HTTRANSPARENT = (-1); 112 | 113 | public TransparentLabel() 114 | { 115 | BackColor = Color.Transparent; 116 | } 117 | 118 | protected override void WndProc(ref Message m) 119 | { 120 | // Bubble up mouse events to the parent component so as to 121 | // allow the timer window to be dragged around even when 122 | // the user clicks on and moves the mouse over this label 123 | if (m.Msg == WM_NCHITTEST) 124 | { 125 | m.Result = (IntPtr)HTTRANSPARENT; 126 | } 127 | else 128 | { 129 | base.WndProc(ref m); 130 | } 131 | } 132 | } 133 | 134 | private void OnTimerButtonClick(object sender, EventArgs e) 135 | { 136 | currentTimerAction?.Invoke(); 137 | } 138 | 139 | public ColorScheme CreateColorScheme(Color color) 140 | { 141 | return new ColorScheme 142 | ( 143 | fill: CreateGradientBrush(color), 144 | border: new Pen(Darker(color), 1f) 145 | ); 146 | } 147 | 148 | private Brush CreateGradientBrush(Color color) 149 | { 150 | return new LinearGradientBrush( 151 | new Point(), 152 | new Point(0, Height), 153 | Brighter(color), 154 | Darker(color) 155 | ); 156 | } 157 | 158 | private Color Darker(Color color) 159 | { 160 | return AdjustBrightness(color, -40); 161 | } 162 | 163 | private Color Brighter(Color color) 164 | { 165 | return AdjustBrightness(color, 20); 166 | } 167 | 168 | private static Color AdjustBrightness(Color color, int offset) 169 | { 170 | var r = Math.Max(0, color.R + offset); 171 | var g = Math.Max(0, color.G + offset); 172 | var b = Math.Max(0, color.B + offset); 173 | return Color.FromArgb(r, g, b); 174 | } 175 | } 176 | 177 | public class ColorScheme 178 | { 179 | public ColorScheme(Brush fill, Pen border) 180 | { 181 | Fill = fill; 182 | Border = border; 183 | } 184 | 185 | public Brush Fill { get; } 186 | public Pen Border { get; } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Tomighty.Windows/Timer/TimerWindow.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Tomighty.Windows/TomightyApplication.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System.ComponentModel; 9 | using System.Windows.Forms; 10 | using Tomighty.Windows.About; 11 | using Tomighty.Windows.Notifications; 12 | using Tomighty.Windows.Preferences; 13 | using Tomighty.Windows.Timer; 14 | using Tomighty.Windows.Tray; 15 | 16 | namespace Tomighty.Windows 17 | { 18 | internal class TomightyApplication : ApplicationContext 19 | { 20 | public TomightyApplication() 21 | { 22 | var eventHub = new SynchronousEventHub(); 23 | var timer = new Tomighty.Timer(eventHub); 24 | var userPreferences = new UserPreferences(); 25 | var pomodoroEngine = new PomodoroEngine(timer, userPreferences, eventHub); 26 | 27 | var trayMenu = new TrayMenu() as ITrayMenu; 28 | var trayIcon = CreateTrayIcon(trayMenu); 29 | var timerWindowPresenter = new TimerWindowPresenter(pomodoroEngine, timer, eventHub); 30 | 31 | new TrayIconController(trayIcon, timerWindowPresenter, eventHub); 32 | new TrayMenuController(trayMenu, this, pomodoroEngine, eventHub); 33 | new NotificationsPresenter(pomodoroEngine, userPreferences, eventHub); 34 | new SoundNotificationPlayer(userPreferences, eventHub); 35 | new AutoUpdate(userPreferences).Start(); 36 | 37 | var aboutWindowPresenter = new AboutWindowPresenter(); 38 | var userPreferencesPresenter = new UserPreferencesPresenter(userPreferences); 39 | 40 | trayMenu.OnAboutClick((sender, e) => aboutWindowPresenter.Show()); 41 | trayMenu.OnPreferencesClick((sender, e) => userPreferencesPresenter.Show()); 42 | 43 | ThreadExit += (sender, e) => trayIcon.Dispose(); 44 | 45 | new StartupEvents(eventHub); 46 | } 47 | 48 | private static NotifyIcon CreateTrayIcon(ITrayMenu trayMenu) 49 | { 50 | var trayIcon = new NotifyIcon(new Container()); 51 | 52 | trayIcon.Text = "Tomighty"; 53 | trayIcon.Icon = Properties.Resources.icon_tomato_white; 54 | trayIcon.ContextMenuStrip = trayMenu.Component; 55 | trayIcon.Visible = true; 56 | 57 | return trayIcon; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Tomighty.Windows/Tray/ITrayMenu.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Windows.Forms; 10 | 11 | namespace Tomighty.Windows.Tray 12 | { 13 | internal interface ITrayMenu 14 | { 15 | ContextMenuStrip Component { get; } 16 | 17 | void OnAboutClick(EventHandler handler); 18 | void OnPreferencesClick(EventHandler handler); 19 | void OnStartPomodoroClick(EventHandler handler); 20 | void OnStartLongBreakClick(EventHandler handler); 21 | void OnStartShortBreakClick(EventHandler handler); 22 | void OnStopTimerClick(EventHandler handler); 23 | void OnResetPomodoroCountClick(EventHandler handler); 24 | void OnExitClick(EventHandler handler); 25 | void Update(Action action); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tomighty.Windows/Tray/ITrayMenuMutator.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | namespace Tomighty.Windows.Tray 9 | { 10 | internal interface ITrayMenuMutator 11 | { 12 | void UpdateRemainingTime(string text); 13 | void EnableStartPomodoroItem(bool enable); 14 | void EnableStartShortBreakItem(bool enable); 15 | void EnableStartLongBreakItem(bool enable); 16 | void EnableStopTimerItem(bool enable); 17 | void EnableResetPomodoroCountItem(bool enable); 18 | void UpdatePomodoroCount(int count); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tomighty.Windows/Tray/TrayIconController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Drawing; 10 | using System.Windows.Forms; 11 | using Tomighty.Events; 12 | using Tomighty.Windows.Timer; 13 | 14 | namespace Tomighty.Windows.Tray 15 | { 16 | internal class TrayIconController : IDisposable 17 | { 18 | private readonly NotifyIcon notifyIcon; 19 | private readonly TimerWindowPresenter timerWindowPresenter; 20 | 21 | public TrayIconController(NotifyIcon notifyIcon, TimerWindowPresenter timerWindowPresenter, IEventHub eventHub) 22 | { 23 | this.notifyIcon = notifyIcon; 24 | this.timerWindowPresenter = timerWindowPresenter; 25 | 26 | notifyIcon.Click += OnNotifyIconClick; 27 | 28 | eventHub.Subscribe(OnTimerStarted); 29 | eventHub.Subscribe(OnTimerStopped); 30 | } 31 | 32 | public void Dispose() 33 | { 34 | notifyIcon.Container.Dispose(); 35 | } 36 | 37 | private void OnNotifyIconClick(object sender, EventArgs @event) 38 | { 39 | if (((MouseEventArgs)@event).Button == MouseButtons.Left) 40 | { 41 | timerWindowPresenter.Toggle(Cursor.Position); 42 | } 43 | } 44 | 45 | private void OnTimerStarted(TimerStarted @event) 46 | { 47 | notifyIcon.Icon = GetIcon(@event.IntervalType); 48 | } 49 | 50 | private void OnTimerStopped(TimerStopped @event) 51 | { 52 | notifyIcon.Icon = Properties.Resources.icon_tomato_white; 53 | } 54 | 55 | private Icon GetIcon(IntervalType intervalType) 56 | { 57 | if (intervalType == IntervalType.Pomodoro) return Properties.Resources.icon_tomato_red; 58 | if (intervalType == IntervalType.ShortBreak) return Properties.Resources.icon_tomato_green; 59 | if (intervalType == IntervalType.LongBreak) return Properties.Resources.icon_tomato_blue; 60 | throw new ArgumentException($"Invalid interval type: {intervalType}"); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tomighty.Windows/Tray/TrayMenu.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Drawing; 10 | using System.Windows.Forms; 11 | 12 | namespace Tomighty.Windows.Tray 13 | { 14 | internal class TrayMenu : ITrayMenu, ITrayMenuMutator 15 | { 16 | private readonly ContextMenuStrip contextMenu; 17 | private readonly ToolStripMenuItem remainingTimeItem; 18 | private readonly ToolStripMenuItem stopTimerItem; 19 | private readonly ToolStripMenuItem pomodoroCountItem; 20 | private readonly ToolStripMenuItem resetPomodoroCountItem; 21 | private readonly ToolStripMenuItem startPomodoroItem; 22 | private readonly ToolStripMenuItem startShortBreakItem; 23 | private readonly ToolStripMenuItem startLongBreakItem; 24 | private readonly ToolStripMenuItem aboutItem; 25 | private readonly ToolStripMenuItem exitItem; 26 | private readonly ToolStripMenuItem preferencesItem; 27 | 28 | public TrayMenu() 29 | { 30 | remainingTimeItem = new ToolStripMenuItem("00:00", Properties.Resources.image_clock); 31 | remainingTimeItem.Enabled = false; 32 | remainingTimeItem.Font = new Font(remainingTimeItem.Font, FontStyle.Bold); 33 | 34 | stopTimerItem = new ToolStripMenuItem("Stop", Properties.Resources.image_stop); 35 | 36 | pomodoroCountItem = new ToolStripMenuItem(); 37 | pomodoroCountItem.Enabled = false; 38 | 39 | resetPomodoroCountItem = new ToolStripMenuItem("Reset count"); 40 | resetPomodoroCountItem.Enabled = false; 41 | 42 | startPomodoroItem = new ToolStripMenuItem("Pomodoro", Properties.Resources.image_tomato_red); 43 | startShortBreakItem = new ToolStripMenuItem("Short break", Properties.Resources.image_tomato_green); 44 | startLongBreakItem = new ToolStripMenuItem("Long break", Properties.Resources.image_tomato_blue); 45 | 46 | aboutItem = new ToolStripMenuItem("About Tomighty"); 47 | preferencesItem = new ToolStripMenuItem("Preferences..."); 48 | exitItem = new ToolStripMenuItem("Exit"); 49 | 50 | contextMenu = new ContextMenuStrip(); 51 | contextMenu.Items.Add(remainingTimeItem); 52 | contextMenu.Items.Add(stopTimerItem); 53 | contextMenu.Items.Add(new ToolStripSeparator()); 54 | contextMenu.Items.Add(pomodoroCountItem); 55 | contextMenu.Items.Add(resetPomodoroCountItem); 56 | contextMenu.Items.Add(new ToolStripSeparator()); 57 | contextMenu.Items.Add(startPomodoroItem); 58 | contextMenu.Items.Add(startShortBreakItem); 59 | contextMenu.Items.Add(startLongBreakItem); 60 | contextMenu.Items.Add(new ToolStripSeparator()); 61 | contextMenu.Items.Add(aboutItem); 62 | contextMenu.Items.Add(preferencesItem); 63 | contextMenu.Items.Add(new ToolStripSeparator()); 64 | contextMenu.Items.Add(exitItem); 65 | } 66 | 67 | public ContextMenuStrip Component => contextMenu; 68 | 69 | public void OnStartPomodoroClick(EventHandler handler) => startPomodoroItem.Click += handler; 70 | public void OnStartLongBreakClick(EventHandler handler) => startLongBreakItem.Click += handler; 71 | public void OnStartShortBreakClick(EventHandler handler) => startShortBreakItem.Click += handler; 72 | public void OnStopTimerClick(EventHandler handler) => stopTimerItem.Click += handler; 73 | public void OnResetPomodoroCountClick(EventHandler handler) => resetPomodoroCountItem.Click += handler; 74 | public void OnAboutClick(EventHandler handler) => aboutItem.Click += handler; 75 | public void OnPreferencesClick(EventHandler handler) => preferencesItem.Click += handler; 76 | public void OnExitClick(EventHandler handler) => exitItem.Click += handler; 77 | 78 | public void UpdateRemainingTime(string text) => remainingTimeItem.Text = text; 79 | public void UpdatePomodoroCount(int count) => pomodoroCountItem.Text = $"Completed pomodoros: {count}"; 80 | public void EnableStartPomodoroItem(bool enable) => startPomodoroItem.Enabled = enable; 81 | public void EnableStartShortBreakItem(bool enable) => startShortBreakItem.Enabled = enable; 82 | public void EnableStartLongBreakItem(bool enable) => startLongBreakItem.Enabled = enable; 83 | public void EnableStopTimerItem(bool enable) => stopTimerItem.Enabled = enable; 84 | public void EnableResetPomodoroCountItem(bool enable) => resetPomodoroCountItem.Enabled = enable; 85 | 86 | public void Update(Action action) 87 | { 88 | if (contextMenu.IsHandleCreated) 89 | { 90 | contextMenu.BeginInvoke(action, this); 91 | } 92 | else 93 | { 94 | action(this); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Tomighty.Windows/Tray/TrayMenuController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | using Tomighty.Events; 12 | 13 | namespace Tomighty.Windows.Tray 14 | { 15 | internal class TrayMenuController 16 | { 17 | private readonly ITrayMenu menu; 18 | private readonly ApplicationContext app; 19 | private readonly IPomodoroEngine pomodoroEngine; 20 | 21 | public TrayMenuController(ITrayMenu menu, ApplicationContext app, IPomodoroEngine pomodoroEngine, IEventHub eventHub) 22 | { 23 | this.menu = menu; 24 | this.app = app; 25 | this.pomodoroEngine = pomodoroEngine; 26 | 27 | menu.OnStartPomodoroClick(OnStartPomodoroClick); 28 | menu.OnStartLongBreakClick(OnStartLongBreakClick); 29 | menu.OnStartShortBreakClick(OnStartShortBreakClick); 30 | menu.OnStopTimerClick(OnStopTimerClick); 31 | menu.OnResetPomodoroCountClick(OnResetPomodoroCountClick); 32 | menu.OnExitClick(OnExitClick); 33 | 34 | eventHub.Subscribe(OnTimerStarted); 35 | eventHub.Subscribe(OnTimerStopped); 36 | eventHub.Subscribe(OnTimeElasped); 37 | eventHub.Subscribe(OnPomodoroCountChanged); 38 | 39 | menu.Update(mutator => 40 | { 41 | mutator.UpdateRemainingTime(Duration.Zero.ToTimeString()); 42 | mutator.UpdatePomodoroCount(0); 43 | mutator.EnableStopTimerItem(false); 44 | }); 45 | } 46 | 47 | private void OnStartPomodoroClick(object sender, EventArgs e) => StartTimer(IntervalType.Pomodoro); 48 | private void OnStartLongBreakClick(object sender, EventArgs e) => StartTimer(IntervalType.LongBreak); 49 | private void OnStartShortBreakClick(object sender, EventArgs e) => StartTimer(IntervalType.ShortBreak); 50 | private void OnStopTimerClick(object sender, EventArgs e) => Task.Run(() => pomodoroEngine.StopTimer()); 51 | private void OnResetPomodoroCountClick(object sender, EventArgs e) => Task.Run(() => pomodoroEngine.ResetPomodoroCount()); 52 | 53 | private void OnExitClick(object sender, EventArgs e) 54 | { 55 | app.ExitThread(); 56 | } 57 | 58 | private void OnTimerStarted(TimerStarted @event) 59 | { 60 | menu.Update(mutator => 61 | { 62 | mutator.UpdateRemainingTime(@event.RemainingTime.ToTimeString()); 63 | mutator.EnableStartPomodoroItem(@event.IntervalType != IntervalType.Pomodoro); 64 | mutator.EnableStartShortBreakItem(@event.IntervalType != IntervalType.ShortBreak); 65 | mutator.EnableStartLongBreakItem(@event.IntervalType != IntervalType.LongBreak); 66 | mutator.EnableStopTimerItem(true); 67 | }); 68 | } 69 | 70 | private void OnTimerStopped(TimerStopped timerStopped) 71 | { 72 | menu.Update(mutator => 73 | { 74 | mutator.UpdateRemainingTime(Duration.Zero.ToTimeString()); 75 | mutator.EnableStartPomodoroItem(true); 76 | mutator.EnableStartShortBreakItem(true); 77 | mutator.EnableStartLongBreakItem(true); 78 | mutator.EnableStopTimerItem(false); 79 | }); 80 | } 81 | 82 | private void OnTimeElasped(TimeElapsed @event) 83 | { 84 | menu.Update(mutator => 85 | { 86 | mutator.UpdateRemainingTime(@event.RemainingTime.ToTimeString()); 87 | }); 88 | } 89 | 90 | private void OnPomodoroCountChanged(PomodoroCountChanged @event) 91 | { 92 | menu.Update(mutator => 93 | { 94 | var count = @event.PomodoroCount; 95 | mutator.UpdatePomodoroCount(count); 96 | mutator.EnableResetPomodoroCountItem(count > 0); 97 | }); 98 | } 99 | 100 | private void StartTimer(IntervalType intervalType) 101 | { 102 | Task.Run(() => pomodoroEngine.StartTimer(intervalType)); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Tomighty.Windows/URLs.cs: -------------------------------------------------------------------------------- 1 | namespace Tomighty.Windows 2 | { 3 | internal class URLs 4 | { 5 | public static readonly string UpdateFeed = "https://raw.githubusercontent.com/tomighty/tomighty-update-feed/master/windows"; 6 | public static readonly string ErrorReport = "https://tomighty-errors.herokuapp.com"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tomighty.Windows/Util/Hash.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Tomighty - http://www.tomighty.org 3 | // 4 | // This software is licensed under the Apache License Version 2.0: 5 | // http://www.apache.org/licenses/LICENSE-2.0.txt 6 | // 7 | 8 | using System; 9 | using System.IO; 10 | using System.Security.Cryptography; 11 | using System.Text; 12 | 13 | namespace Tomighty.Windows.Util 14 | { 15 | public class Hash 16 | { 17 | public static string Sha1(string s) 18 | { 19 | var sha = new SHA1CryptoServiceProvider(); 20 | var data = Encoding.UTF8.GetBytes(s); 21 | var hash = sha.ComputeHash(data); 22 | return BitConverter.ToString(hash).Replace("-", string.Empty).ToLower(); 23 | } 24 | 25 | public static string Sha256(Stream stream) 26 | { 27 | var sha = new SHA256CryptoServiceProvider(); 28 | var hash = sha.ComputeHash(stream); 29 | return BitConverter.ToString(hash).Replace("-", string.Empty).ToLower(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tomighty.Windows/Version.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Reflection; 3 | 4 | namespace Tomighty.Windows 5 | { 6 | internal class Version 7 | { 8 | private static string product; 9 | 10 | public static string Product 11 | { 12 | get 13 | { 14 | if (product == null) 15 | product = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion; 16 | return product; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tomighty.Windows/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Tomighty.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomighty.Core", "Tomighty.Core\Tomighty.Core.csproj", "{C7FF3B3E-0CC1-4EC7-A7C1-39B6361B5895}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomighty.Core.Test", "Tomighty.Core.Test\Tomighty.Core.Test.csproj", "{CAC6A930-C0E3-422B-A49E-7DE000DC9A40}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomighty.Windows", "Tomighty.Windows\Tomighty.Windows.csproj", "{F45108D8-E103-4DB9-A43F-82F864FD9217}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{619DA42E-31A3-4B97-8451-0F8F1E16CA6A}" 13 | ProjectSection(SolutionItems) = preProject 14 | .gitignore = .gitignore 15 | dist.bat = dist.bat 16 | GlobalAssemblyInfo.cs = GlobalAssemblyInfo.cs 17 | LICENSE.txt = LICENSE.txt 18 | NOTICE.txt = NOTICE.txt 19 | pack.ps1 = pack.ps1 20 | README.md = README.md 21 | EndProjectSection 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomighty.Update.Swap", "Tomighty.Update.Swap\Tomighty.Update.Swap.csproj", "{3FC0DC20-6120-4CD7-8F54-A1CA6F8886BF}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {C7FF3B3E-0CC1-4EC7-A7C1-39B6361B5895}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {C7FF3B3E-0CC1-4EC7-A7C1-39B6361B5895}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {C7FF3B3E-0CC1-4EC7-A7C1-39B6361B5895}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {C7FF3B3E-0CC1-4EC7-A7C1-39B6361B5895}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {CAC6A930-C0E3-422B-A49E-7DE000DC9A40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {CAC6A930-C0E3-422B-A49E-7DE000DC9A40}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {CAC6A930-C0E3-422B-A49E-7DE000DC9A40}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {CAC6A930-C0E3-422B-A49E-7DE000DC9A40}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {F45108D8-E103-4DB9-A43F-82F864FD9217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {F45108D8-E103-4DB9-A43F-82F864FD9217}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {F45108D8-E103-4DB9-A43F-82F864FD9217}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {F45108D8-E103-4DB9-A43F-82F864FD9217}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {3FC0DC20-6120-4CD7-8F54-A1CA6F8886BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {3FC0DC20-6120-4CD7-8F54-A1CA6F8886BF}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {3FC0DC20-6120-4CD7-8F54-A1CA6F8886BF}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {3FC0DC20-6120-4CD7-8F54-A1CA6F8886BF}.Release|Any CPU.Build.0 = Release|Any CPU 47 | EndGlobalSection 48 | GlobalSection(SolutionProperties) = preSolution 49 | HideSolutionNode = FALSE 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /dist.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo ========================================================================== 4 | echo Building Tomighty 5 | echo ========================================================================== 6 | echo. 7 | 8 | msbuild Tomighty.sln /t:rebuild /p:Configuration=Release 9 | 10 | for /f %%i in ('powershell -noprofile -executionpolicy bypass "(Get-Item Tomighty.Windows\bin\Release\Tomighty.Windows.exe).VersionInfo.FileVersion"') do set version=%%i 11 | 12 | set tag=%1 13 | if "%tag%"=="commit" (for /f %%i in ('git rev-parse --short HEAD') do set tag=%%i) 14 | 15 | set artifact=tomighty-windows-%version% 16 | if not "%tag%"=="" (set artifact=%artifact%-%tag%) 17 | 18 | set zipfile=dist\%artifact%.zip 19 | set src=Tomighty.Windows\bin\Release 20 | set dest=build\%artifact% 21 | 22 | IF NOT EXIST build ( 23 | mkdir build 24 | ) 25 | 26 | IF NOT EXIST dist ( 27 | mkdir dist 28 | ) 29 | 30 | IF EXIST %dest% ( 31 | rmdir %dest% /S /Q 32 | ) 33 | 34 | if exist %zipfile% ( 35 | del %zipfile% 36 | ) 37 | 38 | mkdir %dest%\Resources 39 | 40 | xcopy /f LICENSE.txt %dest% 41 | xcopy /f NOTICE.txt %dest% 42 | xcopy /f %src%\Tomighty.Windows.exe %dest% 43 | xcopy /f %src%\Tomighty.Core.dll %dest% 44 | xcopy /f %src%\Microsoft.Toolkit.Uwp.Notifications.dll %dest% 45 | xcopy /f /s %src%\Resources %dest%\Resources 46 | xcopy /f Tomighty.Update.Swap\bin\Release\Tomighty.Update.Swap.exe %dest% 47 | 48 | powershell -executionpolicy bypass -file pack.ps1 "%dest%" "%zipfile%" 49 | 50 | makensis -DPRODUCT_NAME=Tomighty -DPRODUCT_VERSION=%version% -DPRODUCT_FILE_VERSION=%version% -DARTIFACT_NAME=%artifact% -DBUILD_DIR=%dest% setup.nsi 51 | 52 | echo. 53 | echo -------------------------------------------------------------------------- 54 | echo Package: %zipfile% 55 | echo Build finished 56 | echo -------------------------------------------------------------------------- 57 | -------------------------------------------------------------------------------- /pack.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string]$directory, 3 | [string]$name 4 | ) 5 | 6 | Add-Type -Assembly System.IO.Compression.FileSystem 7 | [System.IO.Compression.ZipFile]::CreateFromDirectory($directory, $name, [System.IO.Compression.CompressionLevel]::Optimal, $false) -------------------------------------------------------------------------------- /setup.nsi: -------------------------------------------------------------------------------- 1 | ;====================================================== 2 | ; Includes 3 | 4 | !include MUI.nsh 5 | !include Sections.nsh 6 | !include FileFunc.nsh 7 | 8 | ;====================================================== 9 | ; Installer Information 10 | 11 | Name "Tomighty" 12 | OutFile "dist\${ARTIFACT_NAME}-install.exe" 13 | SetCompressor /SOLID lzma 14 | XPStyle on 15 | CRCCheck on 16 | InstallDir "C:\Program Files\${PRODUCT_NAME}\" 17 | AutoCloseWindow false 18 | ShowInstDetails show 19 | Icon "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" 20 | 21 | ;====================================================== 22 | ; Version Tab information for Setup.exe properties 23 | 24 | VIProductVersion 2008.3.22.0 25 | VIAddVersionKey ProductName "${PRODUCT_NAME}" 26 | VIAddVersionKey ProductVersion "${PRODUCT_VERSION}" 27 | VIAddVersionKey FileVersion "${PRODUCT_FILE_VERSION}" 28 | 29 | ;====================================================== 30 | ; Variables 31 | 32 | 33 | ;====================================================== 34 | ; Modern Interface Configuration 35 | 36 | !define MUI_HEADERIMAGE 37 | !define MUI_ABORTWARNING 38 | !define MUI_COMPONENTSPAGE_SMALLDESC 39 | !define MUI_HEADERIMAGE_BITMAP_NOSTRETCH 40 | !define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" 41 | !define MUI_FINISHPAGE_RUN 42 | !define MUI_FINISHPAGE_RUN_TEXT "Run Tomighty" 43 | !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink" 44 | !define REG_UNINSTALL "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomighty" 45 | 46 | ;====================================================== 47 | ; Modern Interface Pages 48 | 49 | !define MUI_DIRECTORYPAGE_VERIFYONLEAVE 50 | !insertmacro MUI_PAGE_LICENSE .\LICENSE.txt 51 | !insertmacro MUI_PAGE_DIRECTORY 52 | !insertmacro MUI_PAGE_COMPONENTS 53 | !insertmacro MUI_PAGE_INSTFILES 54 | !insertmacro MUI_PAGE_FINISH 55 | 56 | ;====================================================== 57 | ; Languages 58 | 59 | !insertmacro MUI_LANGUAGE "English" 60 | 61 | ;====================================================== 62 | ; Installer Sections 63 | 64 | Section "Tomighty" 65 | SectionIn RO 66 | SetOutPath $INSTDIR 67 | SetOverwrite on 68 | File "${BUILD_DIR}\Tomighty.Windows.exe" 69 | File "${BUILD_DIR}\Tomighty.Update.Swap.exe" 70 | File "${BUILD_DIR}\Tomighty.Core.dll" 71 | File "${BUILD_DIR}\Microsoft.Toolkit.Uwp.Notifications.dll" 72 | File "${BUILD_DIR}\NOTICE.txt" 73 | File "${BUILD_DIR}\LICENSE.txt" 74 | File /r "${BUILD_DIR}\Resources" 75 | WriteUninstaller $INSTDIR\uninstall.exe 76 | SectionEnd 77 | 78 | Section "Start menu shortcuts" 79 | SetShellVarContext all 80 | CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}" 81 | CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Tomighty.lnk" "$INSTDIR\Tomighty.Windows.exe" "" "$INSTDIR\Tomighty.Windows.exe" 0 82 | CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 83 | SectionEnd 84 | 85 | Section "Desktop shortcut" 86 | SetShellVarContext all 87 | CreateShortCut "$DESKTOP\Tomighty.lnk" "$INSTDIR\Tomighty.Windows.exe" "" "$INSTDIR\Tomighty.Windows.exe" 0 88 | SectionEnd 89 | 90 | ; Installer functions 91 | Function .onInstSuccess 92 | WriteRegStr HKLM "${REG_UNINSTALL}" "DisplayName" "Tomighty - Desktop Pomodoro Timer" 93 | WriteRegStr HKLM "${REG_UNINSTALL}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" 94 | WriteRegStr HKLM "${REG_UNINSTALL}" "Publisher" "Open Source" 95 | WriteRegStr HKLM "${REG_UNINSTALL}" "DisplayIcon" "$\"$INSTDIR\Tomighty.Windows.exe$\"" 96 | 97 | ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 98 | IntFmt $0 "0x%08X" $0 99 | WriteRegDWORD HKLM "${REG_UNINSTALL}" "EstimatedSize" "$0" 100 | FunctionEnd 101 | 102 | Function LaunchLink 103 | ExecShell "" "$SMPROGRAMS\${PRODUCT_NAME}\Tomighty.lnk" 104 | FunctionEnd 105 | 106 | Section "uninstall" 107 | SetShellVarContext all 108 | 109 | delete "$DESKTOP\Tomighty.lnk" 110 | RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}" 111 | RMDir /r "$INSTDIR" 112 | DeleteRegKey HKLM ${REG_UNINSTALL} 113 | SectionEnd 114 | 115 | Function .onInit 116 | InitPluginsDir 117 | FunctionEnd --------------------------------------------------------------------------------