├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── build ├── .gitignore ├── README.md ├── build.sh └── package.sh ├── docs ├── tomighty_architecture.gliffy └── tomighty_architecture.png ├── src ├── Icons │ ├── icon-clock.tiff │ ├── icon-start-long-break.tiff │ ├── icon-start-pomodoro.tiff │ ├── icon-start-short-break.tiff │ ├── icon-status-alternate.tiff │ ├── icon-status-idle.tiff │ ├── icon-status-long-break.tiff │ ├── icon-status-pomodoro.tiff │ ├── icon-status-short-break.tiff │ └── icon-stop-timer.tiff ├── Podfile ├── Sound │ ├── timer_goes_off.wav │ ├── timer_start.wav │ └── timer_tick.wav ├── Tomighty.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Tomighty.xcworkspace │ └── contents.xcworkspacedata ├── Tomighty │ ├── Base.lproj │ │ ├── MainMenu.xib │ │ └── PreferencesWindow.xib │ ├── Core │ │ ├── Agent │ │ │ ├── TYSoundAgent.h │ │ │ ├── TYSoundAgent.m │ │ │ ├── TYUserInterfaceAgent.h │ │ │ └── TYUserInterfaceAgent.m │ │ ├── Event │ │ │ ├── TYDefaultEventBus.h │ │ │ ├── TYDefaultEventBus.m │ │ │ ├── TYEventBus.h │ │ │ ├── TYSyntheticEventPublisher.h │ │ │ └── TYSyntheticEventPublisher.m │ │ ├── Hotkey │ │ │ ├── TYHotkey.h │ │ │ ├── TYHotkey.m │ │ │ ├── TYHotkeyCell.h │ │ │ ├── TYHotkeyCell.m │ │ │ ├── TYHotkeyControl.h │ │ │ └── TYHotkeyControl.m │ │ ├── Preferences │ │ │ ├── TYPreferences.h │ │ │ ├── TYUserDefaultsPreferences.h │ │ │ └── TYUserDefaultsPreferences.m │ │ ├── Sound │ │ │ ├── TYDefaultSoundPlayer.h │ │ │ ├── TYDefaultSoundPlayer.m │ │ │ └── TYSoundPlayer.h │ │ ├── TYDefaultTomighty.h │ │ ├── TYDefaultTomighty.m │ │ ├── TYTomighty.h │ │ ├── Timer │ │ │ ├── TYDefaultSystemTimer.h │ │ │ ├── TYDefaultSystemTimer.m │ │ │ ├── TYDefaultTimer.h │ │ │ ├── TYDefaultTimer.m │ │ │ ├── TYDefaultTimerContext.h │ │ │ ├── TYDefaultTimerContext.m │ │ │ ├── TYSystemTimer.h │ │ │ ├── TYTimer.h │ │ │ └── TYTimerContext.h │ │ └── UI │ │ │ ├── TYAppUI.h │ │ │ ├── TYDefaultAppUI.h │ │ │ ├── TYDefaultAppUI.m │ │ │ ├── TYDefaultStatusIcon.h │ │ │ ├── TYDefaultStatusIcon.m │ │ │ ├── TYImageLoader.h │ │ │ ├── TYImageLoader.m │ │ │ ├── TYPreferencesWindowController.h │ │ │ ├── TYPreferencesWindowController.m │ │ │ ├── TYStatusIcon.h │ │ │ └── TYStatusMenu.h │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── TYAppDelegate.h │ ├── TYAppDelegate.m │ ├── Tomighty-Info.plist │ ├── Tomighty-Prefix.pch │ ├── en.lproj │ │ ├── Credits.rtf │ │ └── InfoPlist.strings │ └── main.m ├── TomightyTests │ ├── Core │ │ ├── Agent │ │ │ ├── TYSoundAgentTests.m │ │ │ └── TYUserInterfaceAgentTests.m │ │ ├── Event │ │ │ ├── TYEventBusTests.m │ │ │ └── TYSyntheticEventPublisherTests.m │ │ ├── Hotkey │ │ │ └── TYHotkeyTests.m │ │ ├── Preferences │ │ │ └── TYPreferencesTests.m │ │ ├── TYTomightyTests.m │ │ ├── Timer │ │ │ ├── TYTimerContextTests.m │ │ │ └── TYTimerTests.m │ │ └── UI │ │ │ └── TYAppUITests.m │ ├── Mock │ │ ├── TYMockEventBus.h │ │ ├── TYMockEventBus.m │ │ ├── TYMockSystemTimer.h │ │ ├── TYMockSystemTimer.m │ │ ├── TYMockTimerContext.h │ │ ├── TYMockTimerContext.m │ │ ├── TYPublishedEvent.h │ │ └── TYPublishedEvent.m │ ├── TomightyTests-Info.plist │ └── en.lproj │ │ └── InfoPlist.strings └── tomighty.icns └── svg ├── clock.svg ├── export-svg-to-png.sh ├── start-long-break.svg ├── start-pomodoro.svg ├── start-short-break.svg ├── status-long-break.svg ├── status-pomodoro.svg ├── status-short-break.svg ├── stop.svg └── tomighty-logo.svg /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.xcodeproj/project.xcworkspace/xcshareddata/*.xccheckout 2 | **/*.xcodeproj/project.xcworkspace/xcuserdata/ 3 | **/*.xcodeproj/xcuserdata/ 4 | 5 | **/*.xcworkspace/project.xcworkspace/xcuserdata/ 6 | **/*.xcworkspace/xcuserdata/ 7 | 8 | *.xccheckout 9 | 10 | src/Pods 11 | src/Podfile.lock 12 | 13 | src/.idea 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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: -------------------------------------------------------------------------------- 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 for Mac is a software developed by: 7 | 8 | * Célio Cidral Junior https://github.com/ccidral 9 | * Jindřich Doležy https://github.com/dzindra 10 | * Luke Wong (@hausofwong) 11 | * Matthew Andersen (@mattandersen) 12 | * Devon Tucker (@dvntucker) 13 | 14 | 15 | More information about Tomighty at http://www.tomighty.org 16 | 17 | Pomodoro Technique® and Pomodoro™ are registered and filed trademarks 18 | owned by Francesco Cirillo. Tomighty is not affiliated by, associated 19 | with nor endorsed by Francesco Cirillo. 20 | 21 | Tomato icon designed by José Campos 22 | 23 | Clock icon designed by Thomas Le Bas 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tomighty for Mac 2 | ================ 3 | 4 | Available for download at http://www.tomighty.org 5 | 6 | This repository contains the source code for the native OSX version of Tomighty. 7 | If you are looking for the source code of the multiplatform (Java) version, go to https://github.com/ccidral/tomighty 8 | 9 | Architecture 10 | ============ 11 | 12 | ![](/docs/tomighty_architecture.png) 13 | 14 | Development Workflow 15 | ==================== 16 | 17 | We follow the development model presented by Vincent Driessen: 18 | 19 | http://nvie.com/posts/a-successful-git-branching-model/ 20 | 21 | Discuss 22 | ======= 23 | 24 | For development related discussions, please subscribe to: 25 | 26 | https://groups.google.com/forum/#!forum/tomighty-developers 27 | 28 | License 29 | ======= 30 | 31 | Tomighty for Mac is licensed under the terms of the Apache License 2.0. Read it here: https://www.apache.org/licenses/LICENSE-2.0.html 32 | 33 | Contributors 34 | ============ 35 | Thank you to all the contributors to Tomighty OSX: 36 | 37 | * Célio Cidral Jr. (@ccidral) 38 | * Jindřich Doležy (@dzindra) 39 | * Luke Wong (@hausofwong) 40 | * Matthew Andersen (@mattandersen) 41 | * Devon Tucker (@dvntucker) 42 | * Misha Tavkhelidze (@mtavkhelidze) 43 | 44 | Please let us know if you feel your name is missing from this list (and feel free to update it with your PRs). 45 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | yoursway-create-dmg 3 | -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | This is a simple and dirty (very dirty) build script to produce the Tomighty app package 4 | from the command line. 5 | 6 | ## Running 7 | 8 | Simply run build.sh from the build directory. Build artifacts should appear in the `target` 9 | directory. Existing build artifacts must be deleted before running again. 10 | 11 | ## Packaging 12 | * Update package.sh to change the VERSION variable to the correct version. 13 | * Run package.sh. Tomighty-VERSION.dmg will be created in the folder `target` 14 | 15 | ## Other Notes 16 | 17 | Right now this doesn't update any application properties before running. Properties (such as version) 18 | need to updated manually before running 19 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | mkdir -p target 6 | xcodebuild -project ../src/Tomighty.xcodeproj -archivePath target/Tomighty -scheme Tomighty archive 7 | xcodebuild -exportArchive -exportFormat APP -archivePath target/Tomighty.xcarchive -exportPath target/Tomighty 8 | -------------------------------------------------------------------------------- /build/package.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #Use the create-dmg project to create the DMG 4 | #git clone https://github.com/andreyvit/yoursway-create-dmg.git 5 | 6 | VERSION="1.3" 7 | 8 | mkdir -p target/package 9 | cp -rf target/Tomighty.app target/package/ 10 | 11 | yoursway-create-dmg/create-dmg \ 12 | --volname "Tomighty-$VERSION" \ 13 | --icon-size 75 \ 14 | --app-drop-link 10 10 \ 15 | --icon target/package/Tomighty.app 95 10 \ 16 | "target/Tomighty-$VERSION.dmg" \ 17 | target/package 18 | -------------------------------------------------------------------------------- /docs/tomighty_architecture.gliffy: -------------------------------------------------------------------------------- 1 | {"contentType":"application/gliffy+json","version":"1.1","stage":{"background":"#FFFFFF","width":601,"height":475,"nodeIndex":60,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":true,"drawingGuidesOn":true,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"maxWidth":5000,"maxHeight":5000,"themeData":null,"objects":[{"x":149.0,"y":371.0,"rotation":0.0,"id":55,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":55,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":42,"py":1.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":47,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[0.9999999999999716,-1.0],[0.9999999999999716,26.5],[71.07692307692304,26.5],[71.07692307692304,54.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":150.0,"y":373.0,"rotation":0.0,"id":54,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":54,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":42,"py":1.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":45,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[-2.8421709430404007E-14,-3.0],[-2.8421709430404007E-14,24.5],[-70.0,24.5],[-70.0,52.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":160.07692307692304,"y":425.0,"rotation":0.0,"id":47,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":31,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":48,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Status Icon

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":20.0,"y":425.0,"rotation":0.0,"id":45,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":28,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":46,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Status Menu

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":248.0,"y":347.0,"rotation":0.0,"id":44,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":27,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":33,"py":0.5,"px":0.0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":42,"py":0.5,"px":1.0}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[2.0,-2.0],[-11.333333333333343,-2.0],[-24.666666666666686,-2.0],[-38.00000000000003,-2.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":89.99999999999997,"y":320.0,"rotation":0.0,"id":42,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":25,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":43,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

App UI

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":446.0,"y":367.0,"rotation":0.0,"id":41,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":24,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":35,"py":0.5,"px":1.0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":39,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[64.0,-22.0],[94.0,-22.0],[94.0,58.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":480.0,"y":425.0,"rotation":0.0,"id":39,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":22,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":40,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Sound Player

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":379.0,"y":271.0,"rotation":0.0,"id":38,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":21,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":19,"py":1.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":35,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[1.0,-1.0],[1.0,24.0],[71.0,24.0],[71.0,49.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":380.0,"y":270.0,"rotation":0.0,"id":37,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":20,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":19,"py":1.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":33,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[0.0,0.0],[0.0,25.0],[-70.0,25.0],[-70.0,50.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":390.0,"y":320.0,"rotation":0.0,"id":35,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":18,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":36,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Sound Agent

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":250.0,"y":320.0,"rotation":0.0,"id":33,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":16,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":34,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

UI Agent

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":316.0,"y":45.0,"rotation":0.0,"id":27,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":15,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"py":0.5,"px":0.0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":19,"py":0.5,"px":0.0}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[4.0,0.0],[-99.0,0.0],[-99.0,200.0],[4.0,200.0]],"lockSegments":{"1":true},"ortho":true}},"linkMap":[],"children":[]},{"x":498.0,"y":246.0,"rotation":0.0,"id":25,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":14,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":21,"py":0.5,"px":0.0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":19,"py":0.5,"px":1.0}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[-18.0,-1.0],[-31.333333333333314,-1.0],[-44.666666666666686,-1.0],[-58.0,-1.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":446.0,"y":201.0,"rotation":0.0,"id":24,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":13,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":11,"py":1.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":19,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[4.0,-31.0],[4.0,-6.0],[-66.0,-6.0],[-66.0,19.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":306.0,"y":201.0,"rotation":0.0,"id":23,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":12,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":9,"py":1.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":19,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[4.0,-31.0],[4.0,-6.0],[74.0,-6.0],[74.0,19.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":480.0,"y":220.0,"rotation":0.0,"id":21,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":10,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":22,"width":116.30769230769229,"height":33.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Synthetic Event Publisher

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":320.0,"y":220.0,"rotation":0.0,"id":19,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":8,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":20,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Event Bus

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":382.0,"y":68.0,"rotation":0.0,"id":16,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":7,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"py":1.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":11,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[-2.0,2.0],[-2.0,27.0],[68.0,27.0],[68.0,52.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":380.0,"y":71.0,"rotation":0.0,"id":15,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":6,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"py":1.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":9,"py":0.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[0.0,-1.0],[0.0,24.0],[-70.0,24.0],[-70.0,49.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[]},{"x":390.0,"y":120.0,"rotation":0.0,"id":11,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":4,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":12,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Preferences

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":250.0,"y":120.0,"rotation":0.0,"id":9,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":2,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":10,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Timer

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]},{"x":320.0,"y":20.0,"rotation":0.0,"id":0,"width":120.0,"height":50.0,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":0,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":1.8461538461538456,"y":0.0,"rotation":0.0,"id":8,"width":116.30769230769229,"height":17.0,"uid":null,"order":"auto","lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Tomighty

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"children":[]}]}],"shapeStyles":{"com.gliffy.shape.basic.basic_v1.default":{"fill":"#FFFFFF","stroke":"#333333","strokeWidth":2,"shadow":true}},"lineStyles":{"global":{"endArrow":2,"strokeWidth":2}},"textStyles":{"global":{"size":"14px"}}},"metadata":{"title":"untitled","revision":0,"exportBorder":false,"libraries":["com.gliffy.libraries.basic.basic_v1.default","com.gliffy.libraries.flowchart.flowchart_v1.default","com.gliffy.libraries.swimlanes.swimlanes_v1.default","com.gliffy.libraries.uml.uml_v2.class","com.gliffy.libraries.uml.uml_v2.sequence","com.gliffy.libraries.uml.uml_v2.activity","com.gliffy.libraries.erd.erd_v1.default","com.gliffy.libraries.ui.ui_v3.containers_content","com.gliffy.libraries.ui.ui_v3.forms_controls","com.gliffy.libraries.images"],"autosaveDisabled":false},"embeddedResources":{"index":0,"resources":[]}} -------------------------------------------------------------------------------- /docs/tomighty_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/docs/tomighty_architecture.png -------------------------------------------------------------------------------- /src/Icons/icon-clock.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-clock.tiff -------------------------------------------------------------------------------- /src/Icons/icon-start-long-break.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-start-long-break.tiff -------------------------------------------------------------------------------- /src/Icons/icon-start-pomodoro.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-start-pomodoro.tiff -------------------------------------------------------------------------------- /src/Icons/icon-start-short-break.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-start-short-break.tiff -------------------------------------------------------------------------------- /src/Icons/icon-status-alternate.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-status-alternate.tiff -------------------------------------------------------------------------------- /src/Icons/icon-status-idle.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-status-idle.tiff -------------------------------------------------------------------------------- /src/Icons/icon-status-long-break.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-status-long-break.tiff -------------------------------------------------------------------------------- /src/Icons/icon-status-pomodoro.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-status-pomodoro.tiff -------------------------------------------------------------------------------- /src/Icons/icon-status-short-break.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-status-short-break.tiff -------------------------------------------------------------------------------- /src/Icons/icon-stop-timer.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Icons/icon-stop-timer.tiff -------------------------------------------------------------------------------- /src/Podfile: -------------------------------------------------------------------------------- 1 | # no pods for main target yet 2 | 3 | target :TomightyTests do 4 | pod 'OCHamcrest', '~> 3.0' 5 | pod 'OCMockito', '~> 1.0' 6 | end 7 | -------------------------------------------------------------------------------- /src/Sound/timer_goes_off.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Sound/timer_goes_off.wav -------------------------------------------------------------------------------- /src/Sound/timer_start.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Sound/timer_start.wav -------------------------------------------------------------------------------- /src/Sound/timer_tick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/Sound/timer_tick.wav -------------------------------------------------------------------------------- /src/Tomighty.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Tomighty.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Tomighty/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 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 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Agent/TYSoundAgent.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYEventBus.h" 10 | #import "TYPreferences.h" 11 | #import "TYSoundPlayer.h" 12 | 13 | @interface TYSoundAgent : NSObject 14 | 15 | - (id)initWith:(id )soundPlayer preferences:(id )preferences; 16 | - (void)playSoundsInResponseToEventsFrom:(id )eventBus; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Agent/TYSoundAgent.m: -------------------------------------------------------------------------------- 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 | #import "TYSoundAgent.h" 9 | #import "TYTimerContext.h" 10 | 11 | @implementation TYSoundAgent 12 | { 13 | id soundPlayer; 14 | id preferences; 15 | } 16 | 17 | - (id)initWith:(id )aSoundPlayer preferences:(id )aPreferences 18 | { 19 | self = [super self]; 20 | if(self) 21 | { 22 | soundPlayer = aSoundPlayer; 23 | preferences = aPreferences; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)playSoundsInResponseToEventsFrom:(id )eventBus 29 | { 30 | [eventBus subscribeTo:TIMER_START subscriber:^(id timerContext) 31 | { 32 | if([preferences getInt:PREF_PLAY_SOUND_WHEN_TIMER_STARTS]) 33 | { 34 | [soundPlayer play:SOUND_TIMER_START]; 35 | } 36 | }]; 37 | 38 | [eventBus subscribeTo:TIMER_STOP subscriber:^(id eventData) { 39 | [soundPlayer stopCurrentLoop]; 40 | }]; 41 | 42 | [eventBus subscribeTo:POMODORO_START subscriber:^(id timerContext) 43 | { 44 | [soundPlayer stopCurrentLoop]; 45 | if([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO]) 46 | { 47 | [soundPlayer loop:SOUND_TIMER_TICK]; 48 | } 49 | }]; 50 | 51 | [eventBus subscribeTo:BREAK_START subscriber:^(id timerContext) 52 | { 53 | [soundPlayer stopCurrentLoop]; 54 | if([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK]) 55 | { 56 | [soundPlayer loop:SOUND_TIMER_TICK]; 57 | } 58 | }]; 59 | 60 | [eventBus subscribeTo:TIMER_GOES_OFF subscriber:^(id timerContext) 61 | { 62 | if([preferences getInt:PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF]) 63 | { 64 | [soundPlayer play:SOUND_TIMER_GOES_OFF]; 65 | } 66 | }]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Agent/TYUserInterfaceAgent.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | #import "TYAppUI.h" 11 | #import "TYEventBus.h" 12 | #import "TYPreferences.h" 13 | 14 | @interface TYUserInterfaceAgent : NSObject 15 | 16 | - (id)initWith:(id )ui preferences:(id )aPreferences; 17 | - (void)updateAppUiInResponseToEventsFrom:(id )eventBus; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Agent/TYUserInterfaceAgent.m: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYUserInterfaceAgent.h" 10 | #import "TYTimerContext.h" 11 | 12 | @implementation TYUserInterfaceAgent 13 | { 14 | id ui; 15 | id preferences; 16 | } 17 | 18 | - (id)initWith:(id )theAppUI preferences:(id )aPreferences 19 | { 20 | self = [super init]; 21 | if(self) 22 | { 23 | ui = theAppUI; 24 | preferences = aPreferences; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)dispatchNewNotification: (NSString*) text 30 | { 31 | if ([preferences getInt:PREF_ENABLE_NOTIFICATIONS]) { 32 | NSUserNotification *notification = [[NSUserNotification alloc] init]; 33 | notification.title = text; 34 | notification.soundName = NSUserNotificationDefaultSoundName; 35 | 36 | [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; 37 | } 38 | } 39 | 40 | - (void)updateAppUiInResponseToEventsFrom:(id )eventBus 41 | { 42 | [eventBus subscribeTo:APP_INIT subscriber:^(id eventData) { 43 | [ui switchToIdleState]; 44 | [ui updateRemainingTime:0 withMode:TYAppUIRemainingTimeModeDefault]; 45 | [ui setStatusIconTextFormat:(TYAppUIStatusIconTextFormat) [preferences getInt:PREF_STATUS_ICON_TIME_FORMAT]]; 46 | [ui updatePomodoroCount:0]; 47 | }]; 48 | 49 | [eventBus subscribeTo:POMODORO_START subscriber:^(id eventData) { 50 | [ui switchToPomodoroState]; 51 | [self dispatchNewNotification:@"Pomodoro started"]; 52 | }]; 53 | 54 | [eventBus subscribeTo:TIMER_STOP subscriber:^(id eventData) { 55 | [ui switchToIdleState]; 56 | }]; 57 | 58 | [eventBus subscribeTo:SHORT_BREAK_START subscriber:^(id eventData) { 59 | [ui switchToShortBreakState]; 60 | [self dispatchNewNotification:@"Short break started"]; 61 | }]; 62 | 63 | [eventBus subscribeTo:LONG_BREAK_START subscriber:^(id eventData) { 64 | [ui switchToLongBreakState]; 65 | [self dispatchNewNotification:@"Long break started"]; 66 | }]; 67 | 68 | [eventBus subscribeTo:TIMER_TICK subscriber:^(id timerContext) { 69 | [ui updateRemainingTime:[timerContext getRemainingSeconds] withMode:TYAppUIRemainingTimeModeDefault]; 70 | }]; 71 | 72 | [eventBus subscribeTo:TIMER_START subscriber:^(id timerContext) { 73 | [ui updateRemainingTime:[timerContext getRemainingSeconds] withMode:TYAppUIRemainingTimeModeStart]; 74 | }]; 75 | 76 | [eventBus subscribeTo:POMODORO_COUNT_CHANGE subscriber:^(NSNumber *pomodoroCount) { 77 | [ui updatePomodoroCount:[pomodoroCount intValue]]; 78 | }]; 79 | 80 | [eventBus subscribeTo:PREFERENCE_CHANGE subscriber:^(NSString *preferenceKey) { 81 | if ([preferenceKey isEqualToString:PREF_STATUS_ICON_TIME_FORMAT]) { 82 | [ui setStatusIconTextFormat:(TYAppUIStatusIconTextFormat) [preferences getInt:PREF_STATUS_ICON_TIME_FORMAT]]; 83 | } 84 | }]; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Event/TYDefaultEventBus.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYEventBus.h" 10 | 11 | @interface TYDefaultEventBus : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Event/TYDefaultEventBus.m: -------------------------------------------------------------------------------- 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 | #import "TYDefaultEventBus.h" 9 | 10 | @implementation TYDefaultEventBus 11 | { 12 | NSMutableDictionary *map; 13 | } 14 | 15 | - (id)init 16 | { 17 | self = [super init]; 18 | if(self) 19 | { 20 | map = [NSMutableDictionary dictionaryWithCapacity:8]; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)subscribeTo:(TYEventType)eventType subscriber:(TYEventSubscriber)subscriber 26 | { 27 | if (subscriber == nil) 28 | return; 29 | 30 | NSMutableArray *subscribers = [self produceSubscriberListForEventType:eventType]; 31 | [subscribers addObject:[subscriber copy]]; 32 | } 33 | 34 | - (void)publish:(TYEventType)eventType data:(id)data 35 | { 36 | NSMutableArray *subscribers = map[@(eventType)]; 37 | for(TYEventSubscriber subscriber in subscribers) 38 | { 39 | subscriber(data); 40 | } 41 | } 42 | 43 | - (NSMutableArray *)produceSubscriberListForEventType:(TYEventType)eventType 44 | { 45 | NSMutableArray *subscribers = map[@(eventType)]; 46 | if(!subscribers) 47 | { 48 | subscribers = [NSMutableArray arrayWithCapacity:8]; 49 | map[@(eventType)] = subscribers; 50 | } 51 | return subscribers; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Event/TYEventBus.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | typedef NS_ENUM(NSInteger, TYEventType) 11 | { 12 | APP_INIT = 0, 13 | 14 | TIMER_START = 1, 15 | TIMER_TICK = 2, 16 | TIMER_STOP = 3, 17 | TIMER_ABORT = 4, 18 | TIMER_GOES_OFF = 5, 19 | 20 | POMODORO_START = 6, 21 | BREAK_START = 7, 22 | SHORT_BREAK_START = 8, 23 | LONG_BREAK_START = 9, 24 | 25 | POMODORO_COMPLETE = 10, 26 | POMODORO_COUNT_CHANGE = 11, 27 | 28 | PREFERENCE_CHANGE = 12, 29 | READY_FOR_NEXT_TIMER = 13 30 | }; 31 | 32 | typedef void (^TYEventSubscriber)(id eventData); 33 | 34 | @protocol TYEventBus 35 | 36 | - (void)subscribeTo:(TYEventType)eventType subscriber:(TYEventSubscriber)subscriber; 37 | - (void)publish:(TYEventType)eventType data:(id)data; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Event/TYSyntheticEventPublisher.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | #import "TYEventBus.h" 11 | 12 | @interface TYSyntheticEventPublisher : NSObject 13 | 14 | - (void)publishSyntheticEventsInResponseToOtherEventsFrom:(id )eventBus; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Event/TYSyntheticEventPublisher.m: -------------------------------------------------------------------------------- 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 | #import "TYSyntheticEventPublisher.h" 9 | #import "TYTimerContext.h" 10 | 11 | @implementation TYSyntheticEventPublisher 12 | 13 | - (void)publishSyntheticEventsInResponseToOtherEventsFrom:(id )eventBus 14 | { 15 | [eventBus subscribeTo:TIMER_START subscriber:^(id timerContext) 16 | { 17 | if([timerContext getContextType] == POMODORO) 18 | { 19 | [eventBus publish:POMODORO_START data:timerContext]; 20 | } 21 | else if([timerContext getContextType] == SHORT_BREAK) 22 | { 23 | [eventBus publish:BREAK_START data:timerContext]; 24 | [eventBus publish:SHORT_BREAK_START data:timerContext]; 25 | } 26 | else if([timerContext getContextType] == LONG_BREAK) 27 | { 28 | [eventBus publish:BREAK_START data:timerContext]; 29 | [eventBus publish:LONG_BREAK_START data:timerContext]; 30 | } 31 | }]; 32 | 33 | [eventBus subscribeTo:TIMER_STOP subscriber:^(id timerContext) 34 | { 35 | if([timerContext getRemainingSeconds] > 0) 36 | { 37 | [eventBus publish:TIMER_ABORT data:timerContext]; 38 | } 39 | else 40 | { 41 | [eventBus publish:TIMER_GOES_OFF data:timerContext]; 42 | if([timerContext getContextType] == POMODORO) 43 | { 44 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 45 | } 46 | } 47 | }]; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Hotkey/TYHotkey.h: -------------------------------------------------------------------------------- 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 | 9 | /// @credits 10 | #import 11 | 12 | @class NSColor; 13 | @class NSFont; 14 | @class TYHotkey; 15 | 16 | @protocol MVTHotkeyView 17 | 18 | - (void)setHotkey:(TYHotkey*)hotkey; 19 | 20 | @optional 21 | - (TYHotkey*)hotkey; 22 | 23 | @end 24 | /** 25 | @brief A helper class to handle and represent OS X hotkey. 26 | */ 27 | @interface TYHotkey : NSObject 28 | 29 | @property (readonly) BOOL ctrl; 30 | @property (readonly) BOOL alt; 31 | @property (readonly) BOOL shift; 32 | @property (readonly) BOOL cmd; 33 | @property (readonly) BOOL valid; 34 | @property (nonatomic, assign) SInt32 code; 35 | @property (nonatomic, assign) UInt32 flags; 36 | @property (readonly) UInt32 carbonFlags; 37 | @property (nonatomic, strong) NSString *string; 38 | 39 | + (id)hotkeyWithCode:(CGKeyCode)code flags:(UInt32)flags; 40 | + (id)hotkeyWithString:(NSString*)string; 41 | 42 | - (NSMutableAttributedString*)mutableAttributedString:(NSFont*)font 43 | textColor:(NSColor*)textColor 44 | inactiveColor:(NSColor*)inactiveColor; 45 | @end 46 | 47 | #define enter(oid) NSLog(@"%s %@", __PRETTY_FUNCTION__, oid) 48 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Hotkey/TYHotkey.m: -------------------------------------------------------------------------------- 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 | #import 9 | #import 10 | #import "TYHotkey.h" 11 | 12 | static NSString* const MVTControlKeyString = @"^"; 13 | static NSString* const MVTAlternativeKeyString = @"⌥"; 14 | static NSString* const MVTShiftKeyString = @"⇧"; 15 | static NSString* const MVTCommandKeyString = @"⌘"; 16 | 17 | @implementation TYHotkey { 18 | NSDictionary *_keymap; 19 | } 20 | 21 | #pragma mark Init 22 | 23 | + (id)hotkeyWithCode:(CGKeyCode)code flags:(UInt32)flags 24 | { 25 | TYHotkey *key = [[TYHotkey alloc] init]; 26 | if(key) { 27 | [key _withCodeAndFlags:code flags:flags]; 28 | } 29 | return key; 30 | } 31 | 32 | + (id)hotkeyWithString:(NSString*)str 33 | { 34 | TYHotkey *key = [[TYHotkey alloc] init]; 35 | key.string = str; 36 | return key; 37 | } 38 | 39 | - (id)init 40 | { 41 | self = [super init]; 42 | if(self != nil) 43 | _keymap = [self _makeKeymap]; 44 | return self; 45 | } 46 | 47 | #pragma mark Properties 48 | 49 | - (void)setString:(NSString *)string 50 | { 51 | _flags = 0; 52 | _code = -1; 53 | for(int i = 0; i < string.length; i++) { 54 | NSString *s = [string substringWithRange:NSMakeRange(i, 1)]; 55 | if([s isEqualToString:MVTControlKeyString]) 56 | _flags |= NSControlKeyMask; 57 | else if([s isEqualToString:MVTAlternativeKeyString]) 58 | _flags |= NSAlternateKeyMask; 59 | else if([s isEqualToString:MVTShiftKeyString]) 60 | _flags |= NSShiftKeyMask; 61 | else if([s isEqualToString:MVTCommandKeyString]) 62 | _flags |= NSCommandKeyMask; 63 | else { 64 | NSString *s = [string 65 | substringWithRange:NSMakeRange(i, string.length - i)]; 66 | _code = [self _stringToKeyCode:s]; 67 | break; 68 | } 69 | } 70 | if(![self valid]) { 71 | _code = -1; 72 | _flags = -1; 73 | } 74 | } 75 | 76 | - (NSString *)string 77 | { 78 | NSMutableArray *ar = [[NSMutableArray alloc] init]; 79 | if(self.ctrl) 80 | [ar addObject:MVTControlKeyString]; 81 | if(self.alt) 82 | [ar addObject:MVTAlternativeKeyString]; 83 | if(self.shift) 84 | [ar addObject:MVTShiftKeyString]; 85 | if(self.cmd) 86 | [ar addObject:MVTCommandKeyString]; 87 | if([ar count] != 0 && _code != -1) { 88 | [ar addObject:[self _keyCodeToString:_code]]; 89 | return [ar componentsJoinedByString:@""]; 90 | } 91 | return nil; 92 | } 93 | 94 | - (UInt32)carbonFlags 95 | { 96 | UInt32 cf = 0; 97 | if(self.ctrl) 98 | cf |= controlKey; 99 | if(self.alt) 100 | cf |= optionKey; 101 | if(self.shift) 102 | cf |= shiftKey; 103 | if(self.cmd) 104 | cf |= cmdKey; 105 | return cf; 106 | } 107 | 108 | - (BOOL)ctrl 109 | { 110 | return _flags & NSControlKeyMask ? TRUE : FALSE; 111 | } 112 | 113 | - (BOOL)alt 114 | { 115 | return _flags & NSAlternateKeyMask ? TRUE : FALSE; 116 | } 117 | 118 | - (BOOL)shift 119 | { 120 | return _flags & NSShiftKeyMask ? TRUE : FALSE; 121 | } 122 | 123 | - (BOOL)cmd 124 | { 125 | return _flags & NSCommandKeyMask ? TRUE : FALSE; 126 | } 127 | 128 | - (BOOL)valid 129 | { 130 | return (([self ctrl] || [self alt] || [self shift] || [self cmd]) && 131 | (_code != -1 && _code != kVK_Tab)); 132 | } 133 | 134 | #pragma mark Methods 135 | 136 | - (NSMutableAttributedString*)mutableAttributedString:(NSFont*)font 137 | textColor:(NSColor *)textColor 138 | inactiveColor:(NSColor *)inactiveColor 139 | { 140 | NSMutableAttributedString *ret = nil; 141 | NSString *str = [NSString stringWithFormat:@"%@%@%@%@%@", 142 | MVTControlKeyString, MVTAlternativeKeyString, 143 | MVTShiftKeyString, MVTCommandKeyString, 144 | [self _keyCodeToString:_code ]]; 145 | 146 | ret = [[NSMutableAttributedString alloc] initWithString:str]; 147 | 148 | if(font) 149 | [ret addAttribute:NSFontAttributeName value:font 150 | range:NSMakeRange(0, str.length)]; 151 | 152 | if(textColor) 153 | [ret addAttribute:NSForegroundColorAttributeName value:textColor 154 | range:NSMakeRange(0, str.length)]; 155 | 156 | if(inactiveColor) { 157 | if(!self.ctrl) 158 | [ret addAttribute:NSForegroundColorAttributeName 159 | value:inactiveColor 160 | range:NSMakeRange(0,MVTControlKeyString.length)]; 161 | if(!self.alt) 162 | [ret addAttribute:NSForegroundColorAttributeName 163 | value:inactiveColor 164 | range:NSMakeRange(1,1)]; 165 | if(!self.shift) 166 | [ret addAttribute:NSForegroundColorAttributeName 167 | value:inactiveColor 168 | range:NSMakeRange(2,1)]; 169 | if(!self.cmd) 170 | [ret addAttribute:NSForegroundColorAttributeName 171 | value:inactiveColor 172 | range:NSMakeRange(3,1)]; 173 | } 174 | return ret; 175 | 176 | } 177 | 178 | - (void)_withCodeAndFlags:(CGKeyCode)code flags:(UInt32)flags 179 | { 180 | _code = code; 181 | _flags = flags; 182 | if(![self valid]) { 183 | _code = -1; 184 | _flags = -1; 185 | } 186 | } 187 | 188 | - (NSString*)_keyCodeToString:(CGKeyCode)code 189 | { 190 | return [_keymap objectForKey:@(code)]; 191 | } 192 | 193 | - (SInt32)_stringToKeyCode:(NSString*)str 194 | { 195 | SInt32 __block ret = -1; 196 | [_keymap keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) { 197 | *stop = [obj isEqualToString:str] ? YES : NO; 198 | if(stop) ret = [key intValue]; 199 | return *stop; 200 | }]; 201 | return ret; 202 | } 203 | 204 | - (NSDictionary*)_makeKeymap 205 | { 206 | static dispatch_once_t once; 207 | static NSDictionary *keymap = nil; 208 | dispatch_once (&once, ^{ 209 | keymap = @{ 210 | @(kVK_F1): @"F1", 211 | @(kVK_F2): @"F2", 212 | @(kVK_F3): @"F3", 213 | @(kVK_F4): @"F4", 214 | @(kVK_F5): @"F5", 215 | @(kVK_F6): @"F6", 216 | @(kVK_F7): @"F7", 217 | @(kVK_F8): @"F8", 218 | @(kVK_F9): @"F9", 219 | @(kVK_F10): @"F10", 220 | @(kVK_F11): @"F11", 221 | @(kVK_F12): @"F12", 222 | @(kVK_F13): @"F13", 223 | @(kVK_F14): @"F14", 224 | @(kVK_F15): @"F15", 225 | @(kVK_F16): @"F16", 226 | @(kVK_F17): @"F17", 227 | @(kVK_F18): @"F18", 228 | @(kVK_F19): @"F19", 229 | @(kVK_ANSI_0): @"0", 230 | @(kVK_ANSI_1): @"1", 231 | @(kVK_ANSI_2): @"2", 232 | @(kVK_ANSI_3): @"3", 233 | @(kVK_ANSI_4): @"4", 234 | @(kVK_ANSI_5): @"5", 235 | @(kVK_ANSI_6): @"6", 236 | @(kVK_ANSI_7): @"7", 237 | @(kVK_ANSI_8): @"8", 238 | @(kVK_ANSI_9): @"9", 239 | @(kVK_ANSI_A): @"A", 240 | @(kVK_ANSI_B): @"B", 241 | @(kVK_ANSI_C): @"C", 242 | @(kVK_ANSI_D): @"D", 243 | @(kVK_ANSI_E): @"E", 244 | @(kVK_ANSI_F): @"F", 245 | @(kVK_ANSI_G): @"G", 246 | @(kVK_ANSI_H): @"H", 247 | @(kVK_ANSI_I): @"I", 248 | @(kVK_ANSI_J): @"J", 249 | @(kVK_ANSI_K): @"K", 250 | @(kVK_ANSI_L): @"L", 251 | @(kVK_ANSI_M): @"M", 252 | @(kVK_ANSI_N): @"N", 253 | @(kVK_ANSI_O): @"O", 254 | @(kVK_ANSI_P): @"P", 255 | @(kVK_ANSI_Q): @"Q", 256 | @(kVK_ANSI_R): @"R", 257 | @(kVK_ANSI_S): @"S", 258 | @(kVK_ANSI_T): @"T", 259 | @(kVK_ANSI_U): @"U", 260 | @(kVK_ANSI_V): @"V", 261 | @(kVK_ANSI_W): @"W", 262 | @(kVK_ANSI_X): @"X", 263 | @(kVK_ANSI_Y): @"Y", 264 | @(kVK_ANSI_Z): @"Z", 265 | @(kVK_ANSI_Grave): @"`", 266 | @(kVK_ANSI_Minus): @"-", 267 | @(kVK_ANSI_Equal): @"=", 268 | @(kVK_Delete): @"Delete", 269 | @(kVK_ANSI_LeftBracket): @"[", 270 | @(kVK_ANSI_RightBracket): @"]", 271 | @(kVK_ANSI_Backslash): @"\\", 272 | @(kVK_ANSI_RightBracket): @"]", 273 | @(kVK_CapsLock): @"Capslock", 274 | @(kVK_ANSI_Semicolon): @";", 275 | @(kVK_ANSI_Quote): @"'", 276 | @(kVK_ANSI_Comma): @",", 277 | @(kVK_ANSI_Period): @".", 278 | @(kVK_ANSI_Slash): @"/", 279 | @(kVK_Space): @"Space", 280 | @(kVK_LeftArrow): @"←", 281 | @(kVK_DownArrow): @"↓", 282 | @(kVK_RightArrow): @"→", 283 | @(kVK_UpArrow): @"↑", 284 | @(kVK_Return): @"Enter", 285 | @(kVK_Escape): @"Escape" 286 | }; 287 | }); 288 | return keymap; 289 | } 290 | 291 | @end 292 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Hotkey/TYHotkeyCell.h: -------------------------------------------------------------------------------- 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 | #import "TYHotkey.h" 9 | /** 10 | @brief NSTextFieldCell subclass to draw hotkey string. 11 | 12 | @discussion TYHotkeyCell respects IB's text alignment settings. Nothing else yet. 13 | */ 14 | @interface TYHotkeyCell : NSTextFieldCell 15 | 16 | - (void)setHotkey:(TYHotkey*)key; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Hotkey/TYHotkeyCell.m: -------------------------------------------------------------------------------- 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 | #import "TYHotkey.h" 9 | #import "TYHotkeyCell.h" 10 | 11 | @implementation TYHotkeyCell 12 | 13 | - (void)setHotkey:(TYHotkey*)key 14 | { 15 | // Respect IB text alignment settings 16 | NSMutableParagraphStyle *ps = [[NSMutableParagraphStyle alloc] init]; 17 | ps.alignment = self.alignment; 18 | NSMutableAttributedString *as; 19 | as = [key mutableAttributedString:self.font 20 | textColor:self.textColor 21 | inactiveColor:[NSColor controlHighlightColor]]; 22 | [as addAttribute:NSParagraphStyleAttributeName value:ps 23 | range:NSMakeRange(0, as.length)]; 24 | 25 | [self setAttributedStringValue:as]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Hotkey/TYHotkeyControl.h: -------------------------------------------------------------------------------- 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 | #import "TYHotkey.h" 9 | 10 | /** 11 | @brief NSControl subclass for capturing and displaying user clicked hotkeys. 12 | 13 | @discussion When activated, this view listens to keyboard events and records 14 | the keystroke. Pressed keys are highlighted. 15 | 16 | MVTHotkeyView ignores Tab, because it's used in navigation. 17 | 18 | */ 19 | @interface TYHotkeyControl : NSControl 20 | @property (nonatomic, strong) TYHotkey* hotkey; 21 | @end 22 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Hotkey/TYHotkeyControl.m: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYHotkeyControl.h" 10 | #import "TYHotkeyCell.h" 11 | #import "TYHotkey.h" 12 | 13 | IB_DESIGNABLE 14 | 15 | @implementation TYHotkeyControl { 16 | BOOL _key_valid; 17 | TYHotkey *_key; 18 | } 19 | 20 | #pragma mark Initialization 21 | 22 | /// Sets control defaults. Those can be overriden in IB 23 | - (void)setDefaults 24 | { 25 | _key = nil; 26 | } 27 | 28 | - (id)initWithFrame:(CGRect)frame { 29 | self = [super initWithFrame:frame]; 30 | 31 | if(self != nil) { 32 | [self setDefaults]; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | - (id)initWithCoder:(NSCoder *)coder { 39 | self = [super initWithCoder:coder]; 40 | 41 | if(self != nil) { 42 | [self setDefaults]; 43 | } 44 | 45 | return self; 46 | } 47 | 48 | #pragma mark Properties 49 | - (void)setHotkey:(TYHotkey*)hotkey 50 | { 51 | if(hotkey.valid) { 52 | _key = hotkey; 53 | [_cell setHotkey:_key]; 54 | [self sendAction:self.action to:self.target]; 55 | } 56 | } 57 | 58 | - (TYHotkey*)hotkey 59 | { 60 | return _key; 61 | } 62 | 63 | #pragma mark Responders 64 | 65 | - (void)keyDown:(NSEvent *)theEvent 66 | { 67 | TYHotkey *key = [TYHotkey hotkeyWithCode:[theEvent keyCode] 68 | flags:theEvent.modifierFlags]; 69 | if(key.valid) 70 | [self setHotkey:key]; 71 | else 72 | [[super window] keyDown:theEvent]; 73 | } 74 | 75 | - (void)mouseDown:(NSEvent *)theEvent 76 | { 77 | // Prevent background redraw by doing nothing. 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Preferences/TYPreferences.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | extern NSString * const PREF_TIME_POMODORO; 11 | extern NSString * const PREF_TIME_SHORT_BREAK; 12 | extern NSString * const PREF_TIME_LONG_BREAK; 13 | extern NSString * const PREF_TIME_CYCLE; 14 | extern NSString * const PREF_PLAY_SOUND_WHEN_TIMER_STARTS; 15 | extern NSString * const PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF; 16 | extern NSString * const PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO; 17 | extern NSString * const PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK; 18 | extern NSString * const PREF_STATUS_ICON_TIME_FORMAT; 19 | extern int const PREF_STATUS_ICON_TIME_FORMAT_NONE; 20 | extern int const PREF_STATUS_ICON_TIME_FORMAT_MINUTES; 21 | extern int const PREF_STATUS_ICON_TIME_FORMAT_SECONDS; 22 | extern NSString * const PREF_HOTKEY_START; 23 | extern NSString * const PREF_HOTKEY_STOP; 24 | extern NSString * const PREF_CONTINUOUS_MODE; 25 | extern NSString * const PREF_ENABLE_NOTIFICATIONS; 26 | 27 | @protocol TYPreferences 28 | 29 | - (int)getInt:(NSString *)key; 30 | - (NSString*)getString:(NSString *)key; 31 | - (void)setInt:(NSString *)key value:(int)value; 32 | - (void)setString:(NSString *)key value:(NSString*)value; 33 | @end 34 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Preferences/TYUserDefaultsPreferences.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | #import "TYEventBus.h" 11 | #import "TYPreferences.h" 12 | 13 | @interface TYUserDefaultsPreferences : NSObject 14 | 15 | - (id)initWith:(id )anEventBus; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Preferences/TYUserDefaultsPreferences.m: -------------------------------------------------------------------------------- 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 | #import "TYUserDefaultsPreferences.h" 9 | #import "TYEventBus.h" 10 | 11 | NSString * const PREF_TIME_POMODORO = @"org.tomighty.time.pomodoro"; 12 | NSString * const PREF_TIME_SHORT_BREAK = @"org.tomighty.time.short_break"; 13 | NSString * const PREF_TIME_LONG_BREAK = @"org.tomighty.time.long_break"; 14 | NSString * const PREF_TIME_CYCLE = @"org.tomighty.time.cycle"; 15 | NSString * const PREF_PLAY_SOUND_WHEN_TIMER_STARTS = @"org.tomighty.sound.play_on_timer_start"; 16 | NSString * const PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF = @"org.tomighty.sound.play_on_timer_stop"; 17 | NSString * const PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO = @"org.tomighty.sound.play_tick_tock_during_pomodoro"; 18 | NSString * const PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK = @"org.tomighty.sound.play_tick_tock_during_break"; 19 | NSString * const PREF_CONTINUOUS_MODE = @"org.tomighty.continuous"; 20 | NSString * const PREF_STATUS_ICON_TIME_FORMAT = @"org.tomighty.general.status_icon_time_format"; 21 | // formats must have same values as TYAppUIStatusIconTextFormat enum in TYAppUI.h 22 | // TODO : move this values to some common place? 23 | int const PREF_STATUS_ICON_TIME_FORMAT_NONE = 0; 24 | int const PREF_STATUS_ICON_TIME_FORMAT_MINUTES = 1; 25 | int const PREF_STATUS_ICON_TIME_FORMAT_SECONDS = 2; 26 | NSString * const PREF_HOTKEY_START = @"org.tomighty.hotkey.start"; 27 | NSString * const PREF_HOTKEY_STOP = @"org.tomighty.hotkey.stop"; 28 | NSString * const PREF_ENABLE_NOTIFICATIONS = @"org.tomighty.enable_notifications"; 29 | 30 | @implementation TYUserDefaultsPreferences 31 | { 32 | id eventBus; 33 | } 34 | 35 | - (id)initWith:(id )anEventBus 36 | { 37 | self = [super init]; 38 | if(self) 39 | { 40 | eventBus = anEventBus; 41 | 42 | NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary]; 43 | 44 | [defaultValues setObject:[NSNumber numberWithInt:25] forKey:PREF_TIME_POMODORO]; 45 | [defaultValues setObject:[NSNumber numberWithInt:5] forKey:PREF_TIME_SHORT_BREAK]; 46 | [defaultValues setObject:[NSNumber numberWithInt:15] forKey:PREF_TIME_LONG_BREAK]; 47 | [defaultValues setObject:[NSNumber numberWithInt:4] forKey:PREF_TIME_CYCLE]; 48 | [defaultValues setObject:[NSNumber numberWithInt:true] forKey:PREF_PLAY_SOUND_WHEN_TIMER_STARTS]; 49 | [defaultValues setObject:[NSNumber numberWithInt:true] forKey:PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF]; 50 | [defaultValues setObject:[NSNumber numberWithInt:true] forKey:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO]; 51 | [defaultValues setObject:[NSNumber numberWithInt:true] forKey:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK]; 52 | [defaultValues setObject:[NSNumber numberWithInt:true] forKey:PREF_CONTINUOUS_MODE]; 53 | [defaultValues setObject:[NSNumber numberWithInt:false] forKey:PREF_ENABLE_NOTIFICATIONS]; 54 | [defaultValues setObject:[NSNumber numberWithInt:PREF_STATUS_ICON_TIME_FORMAT_NONE] forKey:PREF_STATUS_ICON_TIME_FORMAT]; 55 | [defaultValues setObject:@"^⌘P" forKey:PREF_HOTKEY_START]; 56 | [defaultValues setObject:@"^⌘S" forKey:PREF_HOTKEY_STOP]; 57 | [[NSUserDefaults standardUserDefaults] registerDefaults:defaultValues]; 58 | } 59 | return self; 60 | } 61 | 62 | - (int)getInt:(NSString *)key 63 | { 64 | return (int)[[NSUserDefaults standardUserDefaults] integerForKey:key]; 65 | } 66 | 67 | - (void)setInt:(NSString *)key value:(int)value 68 | { 69 | int actualValue = [self getInt:key]; 70 | if(value != actualValue) 71 | { 72 | [[NSUserDefaults standardUserDefaults] setInteger:value forKey:key]; 73 | [[NSUserDefaults standardUserDefaults] synchronize]; 74 | [eventBus publish:PREFERENCE_CHANGE data:key]; 75 | } 76 | } 77 | 78 | - (NSString*)getString:(NSString*)key 79 | { 80 | NSString *ret = [[NSUserDefaults standardUserDefaults] objectForKey:key]; 81 | return ret; 82 | } 83 | 84 | - (void)setString:(NSString *)key value:(NSString *)value 85 | { 86 | NSString *v = [self getString:key]; 87 | if(![v isEqualToString:value]) { 88 | [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; 89 | [eventBus publish:PREFERENCE_CHANGE data:key]; 90 | } 91 | } 92 | @end 93 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Sound/TYDefaultSoundPlayer.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYSoundPlayer.h" 10 | 11 | @interface TYDefaultSoundPlayer : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Sound/TYDefaultSoundPlayer.m: -------------------------------------------------------------------------------- 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 | #import "TYDefaultSoundPlayer.h" 9 | 10 | NSString * const SOUND_TIMER_START = @"timer_start"; 11 | NSString * const SOUND_TIMER_TICK = @"timer_tick"; 12 | NSString * const SOUND_TIMER_GOES_OFF = @"timer_goes_off"; 13 | 14 | @implementation TYDefaultSoundPlayer 15 | { 16 | NSMutableDictionary *soundClipCache; 17 | NSSound *currentLoopingSoundClip; 18 | } 19 | 20 | - (id)init 21 | { 22 | self = [super init]; 23 | if(self) 24 | { 25 | soundClipCache = [[NSMutableDictionary alloc] initWithCapacity:4]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)play:(NSString *)soundClipName 31 | { 32 | [self play:soundClipName loops:false]; 33 | } 34 | 35 | - (void)loop:(NSString *)soundClipName 36 | { 37 | [self play:soundClipName loops:true]; 38 | } 39 | 40 | - (void)stopCurrentLoop 41 | { 42 | [currentLoopingSoundClip stop]; 43 | } 44 | 45 | - (void)play:(NSString *)soundClipName loops:(BOOL)loops 46 | { 47 | if(loops) 48 | { 49 | [self stopCurrentLoop]; 50 | } 51 | 52 | NSSound *soundClip = [self getSoundClip:soundClipName]; 53 | [soundClip setLoops:loops]; 54 | [soundClip play]; 55 | 56 | if(loops) 57 | { 58 | currentLoopingSoundClip = soundClip; 59 | } 60 | } 61 | 62 | - (NSSound *)getSoundClip:(NSString *)soundClipName 63 | { 64 | NSSound *soundClip = [soundClipCache objectForKey:soundClipName]; 65 | if(!soundClip) 66 | { 67 | soundClip = [NSSound soundNamed:soundClipName]; 68 | soundClipCache[soundClipName] = soundClip; 69 | } 70 | return soundClip; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Sound/TYSoundPlayer.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | extern NSString * const SOUND_TIMER_START; 11 | extern NSString * const SOUND_TIMER_TICK; 12 | extern NSString * const SOUND_TIMER_GOES_OFF; 13 | 14 | @protocol TYSoundPlayer 15 | 16 | - (void)play:(NSString *)soundClipName; 17 | - (void)loop:(NSString *)soundClipName; 18 | - (void)stopCurrentLoop; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /src/Tomighty/Core/TYDefaultTomighty.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | #import "TYEventBus.h" 11 | #import "TYPreferences.h" 12 | #import "TYTimer.h" 13 | #import "TYTomighty.h" 14 | 15 | @interface TYDefaultTomighty : NSObject 16 | 17 | - (id)initWith:(id )timer 18 | preferences:(id )preferences 19 | eventBus:(id )eventBus; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /src/Tomighty/Core/TYDefaultTomighty.m: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYDefaultTimerContext.h" 10 | #import "TYDefaultTomighty.h" 11 | #import "TYEventBus.h" 12 | #import "TYPreferences.h" 13 | #import "TYHotkey.h" 14 | 15 | @implementation TYDefaultTomighty 16 | { 17 | int pomodoroCount; 18 | int pomodoroCycle; 19 | 20 | id timer; 21 | id preferences; 22 | id eventBus; 23 | BOOL continuousMode; 24 | EventHotKeyRef startHotkeyRef, stopHotkeyRef; 25 | } 26 | 27 | - (id)initWith:(id )aTimer 28 | preferences:(id )aPreferences 29 | eventBus:(id )anEventBus 30 | { 31 | self = [super init]; 32 | if(self) 33 | { 34 | pomodoroCount = 0; 35 | timer = aTimer; 36 | preferences = aPreferences; 37 | eventBus = anEventBus; 38 | continuousMode = [preferences getInt:PREF_CONTINUOUS_MODE]; 39 | pomodoroCycle = [preferences getInt:PREF_TIME_CYCLE]; 40 | 41 | [eventBus subscribeTo:POMODORO_COMPLETE subscriber:^(id eventData) 42 | { 43 | [self incrementPomodoroCount]; 44 | 45 | }]; 46 | 47 | [eventBus subscribeTo:READY_FOR_NEXT_TIMER subscriber:^(id eventData) { 48 | if ([preferences getInt:PREF_CONTINUOUS_MODE] == YES) { 49 | //start the next timer, depending on the previous context 50 | id context = eventData; 51 | switch ([context getContextType]) { 52 | case POMODORO: 53 | if (pomodoroCount < pomodoroCycle) { 54 | [self startShortBreak]; 55 | } 56 | else { 57 | [self startLongBreak]; 58 | } 59 | break; 60 | 61 | default: 62 | [self startPomodoro]; 63 | break; 64 | } 65 | } 66 | 67 | }]; 68 | 69 | [eventBus subscribeTo:PREFERENCE_CHANGE subscriber:^(id eventData) { 70 | [self registerHotkeys]; 71 | }]; 72 | 73 | [self installHotkeyEventHandler]; 74 | [self registerHotkeys]; 75 | } 76 | return self; 77 | } 78 | 79 | - (void)startTimer:(TYTimerContextType)contextType 80 | contextName:(NSString *)contextName 81 | minutes:(int)minutes 82 | { 83 | id timerContext = [TYDefaultTimerContext 84 | ofType:contextType 85 | name:contextName 86 | remainingSeconds:minutes * 60]; 87 | [timer start:timerContext]; 88 | } 89 | 90 | - (void)startPomodoro 91 | { 92 | [self startTimer:POMODORO 93 | contextName:@"Pomodoro" 94 | minutes:[preferences getInt:PREF_TIME_POMODORO]]; 95 | } 96 | 97 | - (void)startShortBreak 98 | { 99 | [self startTimer:SHORT_BREAK 100 | contextName:@"Short break" 101 | minutes:[preferences getInt:PREF_TIME_SHORT_BREAK]]; 102 | } 103 | 104 | - (void)startLongBreak 105 | { 106 | [self startTimer:LONG_BREAK 107 | contextName:@"Long break" 108 | minutes:[preferences getInt:PREF_TIME_LONG_BREAK]]; 109 | 110 | } 111 | 112 | - (void)stopTimer 113 | { 114 | [timer stop]; 115 | } 116 | 117 | - (void)setPomodoroCount:(int)newCount 118 | { 119 | pomodoroCount = newCount; 120 | [eventBus publish:POMODORO_COUNT_CHANGE data:[NSNumber numberWithInt:pomodoroCount]]; 121 | } 122 | 123 | - (void)resetPomodoroCount 124 | { 125 | [self setPomodoroCount:0]; 126 | } 127 | 128 | - (void)incrementPomodoroCount 129 | { 130 | int newCount = pomodoroCount + 1; 131 | 132 | if(newCount > pomodoroCycle) 133 | { 134 | newCount = 1; 135 | } 136 | 137 | [self setPomodoroCount:newCount]; 138 | } 139 | 140 | // From here: http://stpeterandpaul.ca/tiger/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/Reference/reference.html 141 | - (void)installHotkeyEventHandler 142 | { 143 | EventTypeSpec eventType; 144 | eventType.eventClass=kEventClassKeyboard; 145 | eventType.eventKind=kEventHotKeyPressed; 146 | 147 | InstallEventHandler(GetApplicationEventTarget(), &TYHotkeyHandler, 148 | 1, &eventType, (__bridge void*)self, NULL); 149 | } 150 | 151 | - (void)unregisterKeys 152 | { 153 | if(startHotkeyRef) { 154 | UnregisterEventHotKey(startHotkeyRef); 155 | startHotkeyRef = nil; 156 | } 157 | if(stopHotkeyRef) { 158 | UnregisterEventHotKey(stopHotkeyRef); 159 | stopHotkeyRef = nil; 160 | } 161 | } 162 | 163 | - (void)registerHotkeys 164 | { 165 | TYHotkey *start = [TYHotkey hotkeyWithString:[preferences 166 | getString:PREF_HOTKEY_START]]; 167 | TYHotkey *stop = [TYHotkey hotkeyWithString:[preferences 168 | getString:PREF_HOTKEY_STOP]]; 169 | EventHotKeyID hotkeyID; 170 | 171 | [self unregisterKeys]; 172 | 173 | hotkeyID.signature='thk1'; // it's a UInt32 actually, value can be anything 174 | hotkeyID.id=11; 175 | RegisterEventHotKey(start.code, start.carbonFlags, hotkeyID, 176 | GetApplicationEventTarget(), 0, &startHotkeyRef); 177 | 178 | hotkeyID.signature='thk2'; 179 | hotkeyID.id=13; 180 | RegisterEventHotKey(stop.code, stop.carbonFlags, hotkeyID, 181 | GetApplicationEventTarget(), 0, &stopHotkeyRef); 182 | 183 | } 184 | 185 | OSStatus TYHotkeyHandler(EventHandlerCallRef next, EventRef evt, void *data) { 186 | TYDefaultTomighty *target = (__bridge TYDefaultTomighty*)data; 187 | EventHotKeyID hkid; 188 | GetEventParameter(evt, kEventParamDirectObject,typeEventHotKeyID, NULL, 189 | sizeof(hkid), NULL, &hkid); 190 | switch(hkid.id) { 191 | case 11: 192 | [target startPomodoro]; 193 | break; 194 | case 13: 195 | [target stopTimer]; 196 | break; 197 | } 198 | return noErr; 199 | } 200 | 201 | @end 202 | -------------------------------------------------------------------------------- /src/Tomighty/Core/TYTomighty.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | @protocol TYTomighty 11 | 12 | - (void)startPomodoro; 13 | - (void)startShortBreak; 14 | - (void)startLongBreak; 15 | - (void)stopTimer; 16 | - (void)resetPomodoroCount; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYDefaultSystemTimer.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYSystemTimer.h" 10 | 11 | @interface TYDefaultSystemTimer : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYDefaultSystemTimer.m: -------------------------------------------------------------------------------- 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 | #import "TYDefaultSystemTimer.h" 9 | 10 | @implementation TYDefaultSystemTimer 11 | { 12 | NSTimer *timer; 13 | TYSystemTimerTrigger currentTrigger; 14 | } 15 | 16 | - (void)triggerRepeatedly:(TYSystemTimerTrigger)trigger intervalInSeconds:(int)seconds 17 | { 18 | [self interrupt]; 19 | 20 | currentTrigger = trigger; 21 | 22 | timer = [NSTimer timerWithTimeInterval:seconds 23 | target:self 24 | selector:@selector(tick:) 25 | userInfo:nil 26 | repeats:YES]; 27 | 28 | [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 29 | } 30 | 31 | - (void)interrupt 32 | { 33 | [timer invalidate]; 34 | 35 | timer = nil; 36 | currentTrigger = nil; 37 | } 38 | 39 | - (void)tick:(NSTimer *)aTimer 40 | { 41 | currentTrigger(); 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYDefaultTimer.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYEventBus.h" 10 | #import "TYSystemTimer.h" 11 | #import "TYTimerContext.h" 12 | #import "TYTimer.h" 13 | 14 | @interface TYDefaultTimer : NSObject 15 | 16 | + (id)createWith:(id)anEventBus systemTimer:(id)aSystemTimer; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYDefaultTimer.m: -------------------------------------------------------------------------------- 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 | #import "TYEventBus.h" 9 | #import "TYDefaultTimer.h" 10 | #import "TYTimerContext.h" 11 | 12 | @implementation TYDefaultTimer 13 | { 14 | id eventBus; 15 | id systemTimer; 16 | id currentTimerContext; 17 | } 18 | 19 | + (id)createWith:(id)anEventBus systemTimer:(id)aSystemTimer 20 | { 21 | return [[TYDefaultTimer alloc] initWith:anEventBus systemTimer:aSystemTimer]; 22 | } 23 | 24 | - (id)initWith:(id)anEventBus systemTimer:(id)aSystemTimer 25 | { 26 | self = [super init]; 27 | if(self) 28 | { 29 | eventBus = anEventBus; 30 | systemTimer = aSystemTimer; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)start:(id)context 36 | { 37 | currentTimerContext = context; 38 | 39 | TYSystemTimerTrigger trigger = ^() 40 | { 41 | [context addSeconds:-1]; 42 | 43 | if([context getRemainingSeconds] > 0) 44 | { 45 | [eventBus publish:TIMER_TICK data:context]; 46 | } 47 | else 48 | { 49 | [self stop]; 50 | } 51 | }; 52 | [systemTimer triggerRepeatedly:trigger intervalInSeconds:1]; 53 | [eventBus publish:TIMER_START data:context]; 54 | } 55 | 56 | - (void)stop 57 | { 58 | [systemTimer interrupt]; 59 | [eventBus publish:TIMER_STOP data:currentTimerContext]; 60 | if([currentTimerContext getRemainingSeconds] <= 0) 61 | { 62 | [eventBus publish:READY_FOR_NEXT_TIMER data:currentTimerContext]; 63 | } 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYDefaultTimerContext.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYTimerContext.h" 10 | 11 | @interface TYDefaultTimerContext : NSObject 12 | 13 | + (id)ofType:(TYTimerContextType)contextType name:(NSString *)name remainingSeconds:(int)initialRemainingSeconds; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYDefaultTimerContext.m: -------------------------------------------------------------------------------- 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 | #import "TYDefaultTimerContext.h" 9 | 10 | @implementation TYDefaultTimerContext 11 | { 12 | TYTimerContextType contextType; 13 | NSString *name; 14 | int remainingSeconds; 15 | } 16 | 17 | + (id)ofType:(TYTimerContextType)contextType name:(NSString *)name remainingSeconds:(int)initialRemainingSeconds 18 | { 19 | return [[TYDefaultTimerContext alloc] initAs:contextType name:name remainingSeconds:initialRemainingSeconds]; 20 | } 21 | 22 | - (id)initAs:(TYTimerContextType)aContextType name:(NSString *)aName remainingSeconds:(int)initialRemainingSeconds 23 | { 24 | self = [super init]; 25 | if(self) { 26 | contextType = aContextType; 27 | name = aName; 28 | remainingSeconds = initialRemainingSeconds; 29 | } 30 | return self; 31 | } 32 | 33 | - (NSString *)getName 34 | { 35 | return name; 36 | } 37 | 38 | - (int)getRemainingSeconds 39 | { 40 | return remainingSeconds; 41 | } 42 | 43 | - (void)addSeconds:(int)seconds 44 | { 45 | remainingSeconds += seconds; 46 | } 47 | 48 | - (TYTimerContextType)getContextType 49 | { 50 | return contextType; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYSystemTimer.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | typedef void (^TYSystemTimerTrigger)(); 11 | 12 | @protocol TYSystemTimer 13 | 14 | - (void)triggerRepeatedly:(TYSystemTimerTrigger)trigger intervalInSeconds:(int)seconds; 15 | - (void)interrupt; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYTimer.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYTimer.h" 10 | #import "TYTimerContext.h" 11 | 12 | @protocol TYTimer 13 | 14 | - (void)start:(id)context; 15 | - (void)stop; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /src/Tomighty/Core/Timer/TYTimerContext.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | 11 | typedef NS_ENUM(NSInteger, TYTimerContextType) 12 | { 13 | POMODORO, 14 | SHORT_BREAK, 15 | LONG_BREAK 16 | }; 17 | 18 | @protocol TYTimerContext 19 | 20 | - (NSString *)getName; 21 | - (int)getRemainingSeconds; 22 | - (void)addSeconds:(int)seconds; 23 | - (TYTimerContextType)getContextType; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYAppUI.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | typedef NS_ENUM(int, TYAppUIRemainingTimeMode) { 11 | TYAppUIRemainingTimeModeDefault = 0, // no special treatment of seconds - display as is 12 | TYAppUIRemainingTimeModeStart, // timer just started - in minute view substract one second to prevent displaying 26m as remaining time 13 | TYAppUIRemainingTimeModeUseLastTime, // refresh status bar after settings change - use last known value passed with other modes 14 | }; 15 | 16 | typedef NS_ENUM(int, TYAppUIStatusIconTextFormat) { 17 | TYAppUIStatusIconTextFormatNone = 0, 18 | TYAppUIStatusIconTextFormatMinutes, 19 | TYAppUIStatusIconTextFormatSeconds 20 | }; 21 | 22 | @protocol TYAppUI 23 | 24 | - (void)switchToIdleState; 25 | - (void)switchToPomodoroState; 26 | - (void)switchToShortBreakState; 27 | - (void)switchToLongBreakState; 28 | - (void)updateRemainingTime:(int)remainingSeconds withMode:(TYAppUIRemainingTimeMode)mode; 29 | - (void)updatePomodoroCount:(int)count; 30 | - (void)setStatusIconTextFormat:(TYAppUIStatusIconTextFormat)textFormat; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYDefaultAppUI.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | #import "TYAppUI.h" 11 | #import "TYStatusIcon.h" 12 | #import "TYStatusMenu.h" 13 | 14 | @interface TYDefaultAppUI : NSObject 15 | 16 | - (id)initWith:(id )statusMenu statusIcon:(id)statusIcon; 17 | 18 | @property (nonatomic) TYAppUIStatusIconTextFormat statusIconTextFormat; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYDefaultAppUI.m: -------------------------------------------------------------------------------- 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 | #import "TYDefaultAppUI.h" 9 | 10 | @implementation TYDefaultAppUI 11 | { 12 | id statusMenu; 13 | id statusIcon; 14 | 15 | int lastKnownTime; 16 | } 17 | 18 | - (id)initWith:(id )aStatusMenu statusIcon:(id)aStatusIcon 19 | { 20 | self = [super init]; 21 | if(self) 22 | { 23 | statusMenu = aStatusMenu; 24 | statusIcon = aStatusIcon; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)switchToIdleState 30 | { 31 | [statusMenu enableStopTimerItem:false]; 32 | [statusMenu enableStartPomodoroItem:true]; 33 | [statusMenu enableStartShortBreakItem:true]; 34 | [statusMenu enableStartLongBreakItem:true]; 35 | [statusIcon changeIcon:ICON_STATUS_IDLE]; 36 | [self updateRemainingTime:0 withMode:TYAppUIRemainingTimeModeDefault]; 37 | } 38 | 39 | - (void)switchToPomodoroState 40 | { 41 | [statusMenu enableStopTimerItem:true]; 42 | [statusMenu enableStartPomodoroItem:false]; 43 | [statusMenu enableStartShortBreakItem:true]; 44 | [statusMenu enableStartLongBreakItem:true]; 45 | [statusIcon changeIcon:ICON_STATUS_POMODORO]; 46 | } 47 | 48 | - (void)switchToShortBreakState 49 | { 50 | [statusMenu enableStopTimerItem:true]; 51 | [statusMenu enableStartPomodoroItem:true]; 52 | [statusMenu enableStartShortBreakItem:false]; 53 | [statusMenu enableStartLongBreakItem:true]; 54 | [statusIcon changeIcon:ICON_STATUS_SHORT_BREAK]; 55 | } 56 | 57 | - (void)switchToLongBreakState 58 | { 59 | [statusMenu enableStopTimerItem:true]; 60 | [statusMenu enableStartPomodoroItem:true]; 61 | [statusMenu enableStartShortBreakItem:true]; 62 | [statusMenu enableStartLongBreakItem:false]; 63 | [statusIcon changeIcon:ICON_STATUS_LONG_BREAK]; 64 | } 65 | 66 | - (void)updateRemainingTime:(int)remainingSeconds withMode:(TYAppUIRemainingTimeMode)mode; 67 | { 68 | if (mode == TYAppUIRemainingTimeModeUseLastTime) { 69 | remainingSeconds = lastKnownTime; 70 | } else { 71 | lastKnownTime = remainingSeconds; 72 | } 73 | 74 | int minutes = remainingSeconds / 60; 75 | int seconds = remainingSeconds % 60; 76 | 77 | NSString *text = [NSString stringWithFormat:@"%02d:%02d", minutes, seconds]; 78 | 79 | [statusMenu setRemainingTimeText:text]; 80 | 81 | TYAppUIStatusIconTextFormat timeFormat = self.statusIconTextFormat; 82 | if (timeFormat == TYAppUIStatusIconTextFormatNone) { 83 | [statusIcon setStatusText:@""]; 84 | } else if (remainingSeconds <= 0){ 85 | [statusIcon setStatusText:@" Stopped"]; 86 | } else { 87 | if (timeFormat == TYAppUIStatusIconTextFormatMinutes) { 88 | if(minutes < 1){ 89 | text = [NSString stringWithFormat:@" %02d s", seconds]; 90 | } else { 91 | text = [NSString stringWithFormat:@" %d m", minutes + (mode == TYAppUIRemainingTimeModeStart ? 0:1)]; 92 | } 93 | } else if (timeFormat == TYAppUIStatusIconTextFormatSeconds) { 94 | text = [NSString stringWithFormat:@" %02d:%02d", minutes, seconds]; 95 | } 96 | [statusIcon setStatusText:text]; 97 | } 98 | } 99 | 100 | - (void)updatePomodoroCount:(int)count 101 | { 102 | BOOL isPlural = count > 1; 103 | 104 | NSString *text = count > 0 ? 105 | [NSString stringWithFormat:@"%d pomodoro%@", count, isPlural ? @"s" : @""] 106 | : @"No pomodoros"; 107 | 108 | [statusMenu setPomodoroCountText:text]; 109 | [statusMenu enableResetPomodoroCountItem:count > 0]; 110 | } 111 | 112 | - (void)setStatusIconTextFormat:(TYAppUIStatusIconTextFormat)statusIconTextFormat { 113 | if (_statusIconTextFormat != statusIconTextFormat) { 114 | _statusIconTextFormat = statusIconTextFormat; 115 | [self updateRemainingTime:0 withMode:TYAppUIRemainingTimeModeUseLastTime]; 116 | } 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYDefaultStatusIcon.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | #import "TYStatusIcon.h" 11 | #import "TYImageLoader.h" 12 | 13 | @interface TYDefaultStatusIcon : NSObject 14 | 15 | - (id)initWith:(NSMenu *)menu imageLoader:(TYImageLoader *)imageLoader; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYDefaultStatusIcon.m: -------------------------------------------------------------------------------- 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 | #import "TYDefaultStatusIcon.h" 9 | 10 | NSString * const ICON_STATUS_IDLE = @"icon-status-idle"; 11 | NSString * const ICON_STATUS_POMODORO = @"icon-status-pomodoro"; 12 | NSString * const ICON_STATUS_SHORT_BREAK = @"icon-status-short-break"; 13 | NSString * const ICON_STATUS_LONG_BREAK = @"icon-status-long-break"; 14 | NSString * const ICON_STATUS_ALTERNATE = @"icon-status-alternate"; 15 | 16 | @implementation TYDefaultStatusIcon 17 | { 18 | NSStatusItem *statusItem; 19 | NSMutableDictionary *iconImageCache; 20 | TYImageLoader *imageLoader; 21 | } 22 | 23 | - (id)initWith:(NSMenu *)aMenu imageLoader:(TYImageLoader *)anImageLoader 24 | { 25 | self = [super init]; 26 | if(self) 27 | { 28 | imageLoader = anImageLoader; 29 | iconImageCache = [[NSMutableDictionary alloc] initWithCapacity:8]; 30 | statusItem = [self createStatusItem:aMenu]; 31 | } 32 | return self; 33 | } 34 | 35 | - (NSStatusItem *)createStatusItem:(NSMenu *)menu 36 | { 37 | NSStatusItem *newStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; 38 | 39 | [newStatusItem setHighlightMode:YES]; 40 | [newStatusItem setImage:[self getIconImage:ICON_STATUS_IDLE template:YES]]; 41 | [newStatusItem setAlternateImage:[self getIconImage:ICON_STATUS_ALTERNATE template:YES]]; 42 | [newStatusItem setMenu:menu]; 43 | 44 | return newStatusItem; 45 | } 46 | 47 | - (void)changeIcon:(NSString *)iconName 48 | { 49 | [statusItem setImage:[self getIconImage:iconName template:NO]]; 50 | } 51 | 52 | - (NSImage *)getIconImage:(NSString *)iconName template:(BOOL)isTemplate 53 | { 54 | NSImage *image = [iconImageCache objectForKey:iconName]; 55 | if(!image) 56 | { 57 | image = [imageLoader loadIcon:iconName]; 58 | iconImageCache[iconName] = image; 59 | [image setTemplate:isTemplate]; 60 | } 61 | return image; 62 | } 63 | 64 | - (void)setStatusText:(NSString *)statusText 65 | { 66 | statusItem.title = statusText; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYImageLoader.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | @interface TYImageLoader : NSObject 11 | 12 | - (NSImage*)loadIcon:(NSString*)name; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYImageLoader.m: -------------------------------------------------------------------------------- 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 | #import "TYImageLoader.h" 9 | 10 | @implementation TYImageLoader 11 | 12 | int const ICON_SIZE = 16; 13 | 14 | - (NSImage*)loadIcon:(NSString*)name 15 | { 16 | return [self loadTiffImage:name withSize:ICON_SIZE]; 17 | } 18 | 19 | - (NSImage*)loadTiffImage:(NSString*)name withSize:(int)widthAndHeight 20 | { 21 | NSString* fileName = [NSString stringWithFormat:@"%@.tiff", name]; 22 | NSImage* image = [NSImage imageNamed:fileName]; 23 | 24 | NSSize size; 25 | size.width = widthAndHeight; 26 | size.height = widthAndHeight; 27 | 28 | [image setSize:size]; 29 | 30 | return image; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYPreferencesWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TYPreferencesWindowController.h 3 | // Tomighty 4 | // 5 | // Created by Célio Cidral Jr on 12/03/14. 6 | // Copyright (c) 2014 Gig Software. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TYHotkeyControl.h" 11 | 12 | @interface TYPreferencesWindowController : NSWindowController 13 | 14 | - (id)initWithPreferences:(id )preferences; 15 | 16 | @property (weak) IBOutlet NSTextField *text_time_pomodoro; 17 | @property (weak) IBOutlet NSTextField *text_time_short_break; 18 | @property (weak) IBOutlet NSTextField *text_time_long_break; 19 | @property (weak) IBOutlet NSTextField *text_time_cycle; 20 | @property (weak) IBOutlet NSButton *check_play_sound_when_timer_starts; 21 | @property (weak) IBOutlet NSButton *check_play_sound_when_timer_goes_off; 22 | @property (weak) IBOutlet NSButton *check_play_ticktock_sound_during_pomodoro; 23 | @property (weak) IBOutlet NSButton *check_play_ticktock_sound_during_break; 24 | @property (weak) IBOutlet NSButton *check_show_notifications; 25 | @property (weak) IBOutlet NSButton *check_continuous_mode; 26 | @property (weak) IBOutlet NSPopUpButton *popup_status_icon_time_format; 27 | @property (weak) IBOutlet TYHotkeyControl *text_hotkey_start; 28 | @property (weak) IBOutlet TYHotkeyControl *text_hotkey_stop; 29 | 30 | 31 | - (IBAction)save_time_pomodoro:(id)sender; 32 | - (IBAction)save_time_short_break:(id)sender; 33 | - (IBAction)save_time_long_break:(id)sender; 34 | - (IBAction)save_time_cycle:(id)sender; 35 | - (IBAction)save_play_sound_when_timer_starts:(id)sender; 36 | - (IBAction)save_play_sound_when_timer_goes_off:(id)sender; 37 | - (IBAction)save_play_ticktock_sound_during_pomodoro:(id)sender; 38 | - (IBAction)save_play_ticktock_sound_during_break:(id)sender; 39 | - (IBAction)save_status_icon_time_format:(id)sender; 40 | - (IBAction)save_hotkey_start:(id)sender; 41 | - (IBAction)save_hotkey_stop:(id)sender; 42 | - (IBAction)save_show_notifications:(id)sender; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYPreferencesWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TYPreferencesWindowController.m 3 | // Tomighty 4 | // 5 | // Created by Célio Cidral Jr on 12/03/14. 6 | // Copyright (c) 2014 Gig Software. All rights reserved. 7 | // 8 | 9 | #import "TYPreferences.h" 10 | #import "TYPreferencesWindowController.h" 11 | 12 | @interface TYPreferencesWindowController () 13 | 14 | @end 15 | 16 | @implementation TYPreferencesWindowController 17 | { 18 | id preferences; 19 | } 20 | 21 | - (id)initWithPreferences:(id )aPreferences 22 | { 23 | self = [super initWithWindowNibName:@"PreferencesWindow"]; 24 | { 25 | preferences = aPreferences; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)windowDidLoad 31 | { 32 | [super windowDidLoad]; 33 | [self.text_time_pomodoro setIntValue:[preferences getInt:PREF_TIME_POMODORO]]; 34 | [self.text_time_short_break setIntValue:[preferences getInt:PREF_TIME_SHORT_BREAK]]; 35 | [self.text_time_long_break setIntValue:[preferences getInt:PREF_TIME_LONG_BREAK]]; 36 | [self.text_time_cycle setIntValue:[preferences getInt:PREF_TIME_CYCLE]]; 37 | [self.check_play_sound_when_timer_starts setState:[preferences getInt:PREF_PLAY_SOUND_WHEN_TIMER_STARTS]]; 38 | [self.check_play_sound_when_timer_goes_off setState:[preferences getInt:PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF]]; 39 | [self.check_play_ticktock_sound_during_pomodoro setState:[preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO]]; 40 | [self.check_play_ticktock_sound_during_break setState:[preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK]]; 41 | [self.check_show_notifications setState:[preferences getInt:PREF_ENABLE_NOTIFICATIONS]]; 42 | [self.check_continuous_mode setState:[preferences getInt:PREF_CONTINUOUS_MODE]]; 43 | [self.popup_status_icon_time_format selectItemAtIndex:[preferences getInt:PREF_STATUS_ICON_TIME_FORMAT]]; 44 | [self.text_hotkey_start 45 | setHotkey:[TYHotkey hotkeyWithString:[preferences 46 | getString:PREF_HOTKEY_START]]]; 47 | [self.text_hotkey_stop 48 | setHotkey:[TYHotkey hotkeyWithString:[preferences 49 | getString:PREF_HOTKEY_STOP]]]; 50 | } 51 | 52 | - (void)windowWillClose:(NSNotification *)notification { 53 | // Force text controls to end editing before close 54 | [self.window makeFirstResponder:nil]; 55 | } 56 | 57 | - (IBAction)save_time_pomodoro:(id)sender { 58 | [preferences setInt:PREF_TIME_POMODORO value:[self.text_time_pomodoro intValue]]; 59 | } 60 | 61 | - (IBAction)save_time_short_break:(id)sender { 62 | [preferences setInt:PREF_TIME_SHORT_BREAK value:[self.text_time_short_break intValue]]; 63 | } 64 | 65 | - (IBAction)save_time_long_break:(id)sender { 66 | [preferences setInt:PREF_TIME_LONG_BREAK value:[self.text_time_long_break intValue]]; 67 | } 68 | 69 | - (IBAction)save_time_cycle:(id)sender { 70 | [preferences setInt:PREF_TIME_CYCLE value:[self.text_time_cycle intValue]]; 71 | } 72 | 73 | - (IBAction)save_play_sound_when_timer_starts:(id)sender { 74 | [preferences setInt:PREF_PLAY_SOUND_WHEN_TIMER_STARTS value:(int)[self.check_play_sound_when_timer_starts state]]; 75 | } 76 | 77 | - (IBAction)save_play_sound_when_timer_goes_off:(id)sender { 78 | [preferences setInt:PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF value:(int)[self.check_play_sound_when_timer_goes_off state]]; 79 | } 80 | 81 | - (IBAction)save_play_ticktock_sound_during_pomodoro:(id)sender { 82 | [preferences setInt:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO value:(int)[self.check_play_ticktock_sound_during_pomodoro state]]; 83 | } 84 | 85 | - (IBAction)save_play_ticktock_sound_during_break:(id)sender { 86 | [preferences setInt:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK value:(int)[self.check_play_ticktock_sound_during_break state]]; 87 | } 88 | 89 | - (IBAction)save_show_notifications:(id)sender { 90 | [preferences setInt:PREF_ENABLE_NOTIFICATIONS value:(int)[self.check_show_notifications state]]; 91 | } 92 | 93 | - (IBAction)save_continuous_mode:(id)sender { 94 | [preferences setInt:PREF_CONTINUOUS_MODE value:(int)[self.check_continuous_mode state]]; 95 | } 96 | 97 | - (IBAction)save_status_icon_time_format:(id)sender { 98 | [preferences setInt:PREF_STATUS_ICON_TIME_FORMAT value:(int)self.popup_status_icon_time_format.indexOfSelectedItem]; 99 | } 100 | 101 | - (IBAction)save_hotkey_start:(id)sender 102 | { 103 | // Note that we don't use [_t.. stringValue] because it'll return the key 104 | // with all modifiers, not just those which are pressed 105 | [preferences setString:PREF_HOTKEY_START 106 | value:_text_hotkey_start.hotkey.string]; 107 | } 108 | 109 | - (IBAction)save_hotkey_stop:(id)sender 110 | { 111 | // Note that we don't use [_t.. stringValue] because it'll return the key 112 | // with all modifiers, not just those which are pressed 113 | [preferences setString:PREF_HOTKEY_STOP 114 | value:_text_hotkey_stop.hotkey.string]; 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYStatusIcon.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | extern NSString * const ICON_STATUS_IDLE; 11 | extern NSString * const ICON_STATUS_POMODORO; 12 | extern NSString * const ICON_STATUS_SHORT_BREAK; 13 | extern NSString * const ICON_STATUS_LONG_BREAK; 14 | 15 | @protocol TYStatusIcon 16 | 17 | - (void)setStatusText:(NSString *)statusText; 18 | 19 | - (void)changeIcon:(NSString *)iconName; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /src/Tomighty/Core/UI/TYStatusMenu.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | @protocol TYStatusMenu 11 | 12 | - (void)enableStopTimerItem:(BOOL)enable; 13 | - (void)enableStartPomodoroItem:(BOOL)enable; 14 | - (void)enableStartShortBreakItem:(BOOL)enable; 15 | - (void)enableStartLongBreakItem:(BOOL)enable; 16 | - (void)enableResetPomodoroCountItem:(BOOL)enable; 17 | - (void)setRemainingTimeText:(NSString *)text; 18 | - (void)setPomodoroCountText:(NSString *)text; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /src/Tomighty/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /src/Tomighty/TYAppDelegate.h: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | #import "TYStatusMenu.h" 11 | #import "TYStatusIcon.h" 12 | 13 | @interface TYAppDelegate : NSObject 14 | 15 | @property (weak) IBOutlet NSMenu *statusMenu; 16 | @property (weak) IBOutlet NSMenuItem *remainingTimeMenuItem; 17 | @property (weak) IBOutlet NSMenuItem *stopTimerMenuItem; 18 | @property (weak) IBOutlet NSMenuItem *pomodoroCountMenuItem; 19 | @property (weak) IBOutlet NSMenuItem *resetPomodoroCountMenuItem; 20 | @property (weak) IBOutlet NSMenuItem *startPomodoroMenuItem; 21 | @property (weak) IBOutlet NSMenuItem *startShortBreakMenuItem; 22 | @property (weak) IBOutlet NSMenuItem *startLongBreakMenuItem; 23 | 24 | - (IBAction)startPomodoro:(id)sender; 25 | - (IBAction)startShortBreak:(id)sender; 26 | - (IBAction)startLongBreak:(id)sender; 27 | - (IBAction)stopTimer:(id)sender; 28 | - (IBAction)resetPomodoroCount:(id)sender; 29 | - (IBAction)showPreferences:(id)sender; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /src/Tomighty/TYAppDelegate.m: -------------------------------------------------------------------------------- 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 | #import "TYAppDelegate.h" 9 | 10 | #import "TYTomighty.h" 11 | #import "TYSoundAgent.h" 12 | #import "TYSyntheticEventPublisher.h" 13 | #import "TYUserInterfaceAgent.h" 14 | 15 | #import "TYAppUI.h" 16 | #import "TYEventBus.h" 17 | #import "TYImageLoader.h" 18 | #import "TYPreferences.h" 19 | #import "TYSoundPlayer.h" 20 | #import "TYStatusIcon.h" 21 | #import "TYStatusMenu.h" 22 | #import "TYSystemTimer.h" 23 | #import "TYTimer.h" 24 | 25 | #import "TYDefaultAppUI.h" 26 | #import "TYDefaultEventBus.h" 27 | #import "TYDefaultSoundPlayer.h" 28 | #import "TYDefaultStatusIcon.h" 29 | #import "TYDefaultSystemTimer.h" 30 | #import "TYDefaultTimer.h" 31 | #import "TYDefaultTomighty.h" 32 | #import "TYUserDefaultsPreferences.h" 33 | 34 | #import "TYPreferencesWindowController.h" 35 | 36 | @implementation TYAppDelegate 37 | { 38 | id tomighty; 39 | id preferences; 40 | TYSoundAgent *soundAgent; 41 | TYSyntheticEventPublisher *syntheticEventPublisher; 42 | TYUserInterfaceAgent *userInterfaceAgent; 43 | TYPreferencesWindowController *preferencesWindow; 44 | } 45 | 46 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 47 | { 48 | TYImageLoader *imageLoader = [[TYImageLoader alloc] init]; 49 | 50 | id eventBus = [[TYDefaultEventBus alloc] init]; 51 | id systemTimer = [[TYDefaultSystemTimer alloc] init]; 52 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 53 | id soundPlayer = [[TYDefaultSoundPlayer alloc] init]; 54 | id statusIcon = [[TYDefaultStatusIcon alloc] initWith:self.statusMenu imageLoader:imageLoader]; 55 | id statusMenu = self; 56 | id appUi = [[TYDefaultAppUI alloc] initWith:statusMenu statusIcon:statusIcon]; 57 | 58 | preferences = [[TYUserDefaultsPreferences alloc] initWith:eventBus]; 59 | soundAgent = [[TYSoundAgent alloc] initWith:soundPlayer preferences:preferences]; 60 | syntheticEventPublisher = [[TYSyntheticEventPublisher alloc] init]; 61 | userInterfaceAgent = [[TYUserInterfaceAgent alloc] initWith:appUi preferences:preferences]; 62 | tomighty = [[TYDefaultTomighty alloc] initWith:timer preferences:preferences eventBus:eventBus]; 63 | 64 | [syntheticEventPublisher publishSyntheticEventsInResponseToOtherEventsFrom:eventBus]; 65 | [soundAgent playSoundsInResponseToEventsFrom:eventBus]; 66 | [userInterfaceAgent updateAppUiInResponseToEventsFrom:eventBus]; 67 | [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; 68 | 69 | [self initMenuItemsIcons:imageLoader]; 70 | 71 | [eventBus publish:APP_INIT data:nil]; 72 | } 73 | 74 | - (void)initMenuItemsIcons:(TYImageLoader *)imageLoader { 75 | NSImage *clockIcon = [imageLoader loadIcon:@"icon-clock"]; 76 | [clockIcon setTemplate:TRUE]; 77 | [self.remainingTimeMenuItem setImage:clockIcon]; 78 | NSImage *stopIcon = [imageLoader loadIcon:@"icon-stop-timer"]; 79 | [stopIcon setTemplate:TRUE]; 80 | [self.stopTimerMenuItem setImage:stopIcon]; 81 | [self.startPomodoroMenuItem setImage:[imageLoader loadIcon:@"icon-start-pomodoro"]]; 82 | [self.startShortBreakMenuItem setImage:[imageLoader loadIcon:@"icon-start-short-break"]]; 83 | [self.startLongBreakMenuItem setImage:[imageLoader loadIcon:@"icon-start-long-break"]]; 84 | } 85 | 86 | - (IBAction)startPomodoro:(id)sender 87 | { 88 | [tomighty startPomodoro]; 89 | } 90 | 91 | - (IBAction)startShortBreak:(id)sender 92 | { 93 | [tomighty startShortBreak]; 94 | } 95 | 96 | - (IBAction)startLongBreak:(id)sender 97 | { 98 | [tomighty startLongBreak]; 99 | } 100 | 101 | - (IBAction)stopTimer:(id)sender 102 | { 103 | [tomighty stopTimer]; 104 | } 105 | 106 | - (IBAction)resetPomodoroCount:(id)sender 107 | { 108 | [tomighty resetPomodoroCount]; 109 | } 110 | 111 | - (IBAction)showPreferences:(id)sender 112 | { 113 | if(!preferencesWindow) 114 | { 115 | preferencesWindow = [[TYPreferencesWindowController alloc] initWithPreferences:preferences]; 116 | } 117 | [preferencesWindow showWindow:nil]; 118 | [NSApp activateIgnoringOtherApps:YES]; 119 | } 120 | 121 | - (void)enableStopTimerItem:(BOOL)enable 122 | { 123 | [self.stopTimerMenuItem setEnabled:enable]; 124 | } 125 | 126 | - (void)enableTimerMenuItem:(NSMenuItem *)menuItem enable:(BOOL)enable 127 | { 128 | [menuItem setEnabled:enable]; 129 | [menuItem setState:enable ? NSOffState : NSOnState]; 130 | } 131 | 132 | - (void)enableStartPomodoroItem:(BOOL)enable 133 | { 134 | [self enableTimerMenuItem:self.startPomodoroMenuItem enable:enable]; 135 | } 136 | 137 | - (void)enableStartShortBreakItem:(BOOL)enable 138 | { 139 | [self enableTimerMenuItem:self.startShortBreakMenuItem enable:enable]; 140 | } 141 | 142 | - (void)enableStartLongBreakItem:(BOOL)enable 143 | { 144 | [self enableTimerMenuItem:self.startLongBreakMenuItem enable:enable]; 145 | } 146 | 147 | - (void)enableResetPomodoroCountItem:(BOOL)enable 148 | { 149 | [self.resetPomodoroCountMenuItem setEnabled:enable]; 150 | } 151 | 152 | - (void)setRemainingTimeText:(NSString *)text 153 | { 154 | [self.remainingTimeMenuItem setTitle:text]; 155 | } 156 | 157 | - (void)setPomodoroCountText:(NSString *)text 158 | { 159 | [self.pomodoroCountMenuItem setTitle:text]; 160 | } 161 | 162 | - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification 163 | { 164 | return YES; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /src/Tomighty/Tomighty-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | tomighty 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.2 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.productivity 27 | LSMinimumSystemVersion 28 | ${MACOSX_DEPLOYMENT_TARGET} 29 | LSUIElement 30 | 31 | NSHumanReadableCopyright 32 | Copyright © 2014 Célio Cidral Jr. All rights reserved. 33 | NSMainNibFile 34 | MainMenu 35 | NSPrincipalClass 36 | NSApplication 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/Tomighty/Tomighty-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /src/Tomighty/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;\red128\green128\blue128;} 4 | \paperw11900\paperh16840\vieww9600\viewh8400\viewkind0 5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 6 | 7 | \f0\fs24 \cf0 www.tomighty.org\ 8 | \ 9 | Tomighty is a simple, unintrusive desktop timer application specifically designed for the Pomodoro Technique\'ae.\ 10 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc 11 | \cf0 \ 12 | Licensed under the terms of the Apache License 2.0 \ 13 | \ 14 | 15 | \fs20 \cf2 Pomodoro Technique\'ae and Pomodoro\'99 are registered and filed trademarks owned by Francesco Cirillo. Tomighty is not affiliated by, associated with nor endorsed by Francesco Cirillo.\ 16 | 17 | \fs22 \ 18 | Tomato icon designed by Jos\'e9 Campos \ 19 | \ 20 | Clock icon designed by Thomas Le Bas } -------------------------------------------------------------------------------- /src/Tomighty/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /src/Tomighty/main.m: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | int main(int argc, const char * argv[]) 11 | { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/Agent/TYSoundAgentTests.m: -------------------------------------------------------------------------------- 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 | #define HC_SHORTHAND 9 | #import 10 | 11 | #define MOCKITO_SHORTHAND 12 | #import 13 | 14 | #import 15 | 16 | #import "TYMockEventBus.h" 17 | #import "TYPreferences.h" 18 | #import "TYSoundAgent.h" 19 | #import "TYSoundPlayer.h" 20 | #import "TYTimerContext.h" 21 | 22 | @interface TYSoundAgentTests : XCTestCase 23 | 24 | @end 25 | 26 | @implementation TYSoundAgentTests 27 | { 28 | TYSoundAgent *soundAgent; 29 | TYMockEventBus *eventBus; 30 | id preferences; 31 | id soundPlayer; 32 | id timerContext; 33 | } 34 | 35 | - (void)setUp 36 | { 37 | [super setUp]; 38 | 39 | eventBus = [[TYMockEventBus alloc] init]; 40 | preferences = mockProtocol(@protocol(TYPreferences)); 41 | soundPlayer = mockProtocol(@protocol(TYSoundPlayer)); 42 | timerContext = mockProtocol(@protocol(TYTimerContext)); 43 | soundAgent = [[TYSoundAgent alloc] initWith:soundPlayer preferences:preferences]; 44 | 45 | [soundAgent playSoundsInResponseToEventsFrom:eventBus]; 46 | } 47 | 48 | - (void)tearDown 49 | { 50 | [super tearDown]; 51 | } 52 | 53 | - (void)test_play_sound_when_timer_starts_if_enabled_by_preference 54 | { 55 | [given([preferences getInt:PREF_PLAY_SOUND_WHEN_TIMER_STARTS]) willReturnInteger:true]; 56 | [eventBus publish:TIMER_START data:timerContext]; 57 | [verify(soundPlayer) play:SOUND_TIMER_START]; 58 | } 59 | 60 | - (void)test_do_not_play_sound_when_timer_starts_if_disabled_by_preference 61 | { 62 | [given([preferences getInt:PREF_PLAY_SOUND_WHEN_TIMER_STARTS]) willReturnInteger:false]; 63 | [eventBus publish:TIMER_START data:timerContext]; 64 | [verifyCount(soundPlayer, never()) play:anything()]; 65 | } 66 | 67 | - (void)test_play_sound_when_timer_goes_off_if_enabled_by_preference 68 | { 69 | [given([preferences getInt:PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF]) willReturnInteger:true]; 70 | [eventBus publish:TIMER_GOES_OFF data:timerContext]; 71 | [verify(soundPlayer) play:SOUND_TIMER_GOES_OFF]; 72 | } 73 | 74 | - (void)test_do_not_play_sound_when_timer_goes_off_if_disabled_by_preference 75 | { 76 | [given([preferences getInt:PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF]) willReturnInteger:false]; 77 | [eventBus publish:TIMER_GOES_OFF data:timerContext]; 78 | [verifyCount(soundPlayer, never()) play:anything()]; 79 | } 80 | 81 | - (void)test_play_ticking_sound_when_pomodoro_starts_if_enabled_by_preference 82 | { 83 | [given([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO]) willReturnInteger:true]; 84 | [given([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK]) willReturnInteger:false]; 85 | [eventBus publish:POMODORO_START data:timerContext]; 86 | [verify(soundPlayer) loop:SOUND_TIMER_TICK]; 87 | } 88 | 89 | - (void)test_do_not_play_ticking_sound_when_pomodoro_starts_if_disabled_by_preference 90 | { 91 | [given([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO]) willReturnInteger:false]; 92 | [given([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK]) willReturnInteger:true]; 93 | [eventBus publish:POMODORO_START data:timerContext]; 94 | [verifyCount(soundPlayer, never()) loop:anything()]; 95 | } 96 | 97 | - (void)test_play_ticking_sound_when_break_starts_if_enabled_by_preference 98 | { 99 | [given([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO]) willReturnInteger:false]; 100 | [given([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK]) willReturnInteger:true]; 101 | [eventBus publish:BREAK_START data:timerContext]; 102 | [verify(soundPlayer) loop:SOUND_TIMER_TICK]; 103 | } 104 | 105 | - (void)test_do_not_play_ticking_sound_when_break_starts_if_disabled_by_preference 106 | { 107 | [given([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO]) willReturnInteger:true]; 108 | [given([preferences getInt:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK]) willReturnInteger:false]; 109 | [eventBus publish:BREAK_START data:timerContext]; 110 | [verifyCount(soundPlayer, never()) loop:anything()]; 111 | } 112 | 113 | - (void)test_stop_any_looping_sound_when_timer_stops 114 | { 115 | [eventBus publish:TIMER_STOP data:timerContext]; 116 | [verify(soundPlayer) stopCurrentLoop]; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/Agent/TYUserInterfaceAgentTests.m: -------------------------------------------------------------------------------- 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 | #define HC_SHORTHAND 9 | #import 10 | 11 | #define MOCKITO_SHORTHAND 12 | #import 13 | 14 | #import 15 | 16 | #import "TYAppUI.h" 17 | #import "TYMockEventBus.h" 18 | #import "TYTimerContext.h" 19 | #import "TYUserInterfaceAgent.h" 20 | 21 | @interface TYUserInterfaceAgentTests : XCTestCase 22 | 23 | @end 24 | 25 | @implementation TYUserInterfaceAgentTests 26 | { 27 | id ui; 28 | id preferences; 29 | TYMockEventBus *eventBus; 30 | TYUserInterfaceAgent *uiAgent; 31 | } 32 | 33 | - (void)setUp 34 | { 35 | [super setUp]; 36 | ui = mockProtocol(@protocol(TYAppUI)); 37 | preferences = mockProtocol(@protocol(TYPreferences)); 38 | [given([preferences getInt:PREF_STATUS_ICON_TIME_FORMAT]) willReturnInt:PREF_STATUS_ICON_TIME_FORMAT_MINUTES]; 39 | 40 | eventBus = [[TYMockEventBus alloc] init]; 41 | uiAgent = [[TYUserInterfaceAgent alloc] initWith:ui preferences:preferences]; 42 | 43 | [uiAgent updateAppUiInResponseToEventsFrom:eventBus]; 44 | } 45 | 46 | - (void)tearDown 47 | { 48 | [super tearDown]; 49 | } 50 | 51 | - (void)test_switch_ui_to_idle_state_when_application_is_initialized 52 | { 53 | [eventBus publish:APP_INIT data:nil]; 54 | [verify(ui) switchToIdleState]; 55 | } 56 | 57 | - (void)test_update_remaining_time_to_zero_when_application_is_initialized 58 | { 59 | [eventBus publish:APP_INIT data:nil]; 60 | [verify(ui) updateRemainingTime:0 withMode:TYAppUIRemainingTimeModeDefault]; 61 | } 62 | 63 | - (void)test_update_pomodoro_count_to_zero_when_application_is_initialized 64 | { 65 | [eventBus publish:APP_INIT data:nil]; 66 | [verify(ui) updatePomodoroCount:0]; 67 | } 68 | 69 | - (void)test_read_status_text_format_from_preferences_when_initialized 70 | { 71 | [eventBus publish:APP_INIT data:nil]; 72 | [verify(ui) setStatusIconTextFormat:TYAppUIStatusIconTextFormatMinutes]; 73 | } 74 | 75 | - (void)test_switch_ui_to_idle_state_when_timer_stops 76 | { 77 | [eventBus publish:TIMER_STOP data:nil]; 78 | [verify(ui) switchToIdleState]; 79 | } 80 | 81 | - (void)test_switch_ui_to_pomodoro_state_when_pomodoro_starts 82 | { 83 | [eventBus publish:POMODORO_START data:nil]; 84 | [verify(ui) switchToPomodoroState]; 85 | } 86 | 87 | - (void)test_switch_ui_to_short_break_state_when_short_break_starts 88 | { 89 | [eventBus publish:SHORT_BREAK_START data:nil]; 90 | [verify(ui) switchToShortBreakState]; 91 | } 92 | 93 | - (void)test_switch_ui_to_long_break_state_when_long_break_starts 94 | { 95 | [eventBus publish:LONG_BREAK_START data:nil]; 96 | [verify(ui) switchToLongBreakState]; 97 | } 98 | 99 | - (void)test_update_remaining_time_when_timer_ticks 100 | { 101 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 102 | 103 | [given([timerContext getRemainingSeconds]) willReturnInt:270]; 104 | 105 | [eventBus publish:TIMER_TICK data:timerContext]; 106 | [verify(ui) updateRemainingTime:[timerContext getRemainingSeconds] withMode:TYAppUIRemainingTimeModeDefault]; 107 | } 108 | 109 | - (void)test_update_remaining_time_when_timer_starts 110 | { 111 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 112 | 113 | [given([timerContext getRemainingSeconds]) willReturnInt:270]; 114 | 115 | [eventBus publish:TIMER_START data:timerContext]; 116 | [verify(ui) updateRemainingTime:[timerContext getRemainingSeconds] withMode:TYAppUIRemainingTimeModeStart]; 117 | } 118 | 119 | - (void)test_update_pomodoro_count_when_it_changes 120 | { 121 | NSNumber *pomodoroCount = [NSNumber numberWithInt:3]; 122 | [eventBus publish:POMODORO_COUNT_CHANGE data:pomodoroCount]; 123 | [verify(ui) updatePomodoroCount:3]; 124 | } 125 | 126 | - (void)test_change_status_text_format_when_preferences_change 127 | { 128 | [given([preferences getInt:PREF_STATUS_ICON_TIME_FORMAT]) willReturnInt:PREF_STATUS_ICON_TIME_FORMAT_SECONDS]; 129 | 130 | [eventBus publish:PREFERENCE_CHANGE data:PREF_STATUS_ICON_TIME_FORMAT]; 131 | 132 | [verify(ui) setStatusIconTextFormat:TYAppUIStatusIconTextFormatSeconds]; 133 | } 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/Event/TYEventBusTests.m: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYEventBus.h" 10 | #import "TYDefaultEventBus.h" 11 | 12 | @interface TYEventBusTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation TYEventBusTests 17 | { 18 | id eventBus; 19 | } 20 | 21 | - (void)setUp 22 | { 23 | [super setUp]; 24 | eventBus = [[TYDefaultEventBus alloc] init]; 25 | } 26 | 27 | - (void)test_subscriber_receives_event_of_desired_type 28 | { 29 | __block BOOL hasReceivedEvent = false; 30 | 31 | [eventBus subscribeTo:TIMER_START subscriber:^(id eventData) 32 | { 33 | hasReceivedEvent = true; 34 | }]; 35 | 36 | [eventBus publish:TIMER_START data:nil]; 37 | 38 | XCTAssertTrue(hasReceivedEvent); 39 | } 40 | 41 | - (void)test_subscriber_does_not_receive_event_of_undesired_type 42 | { 43 | __block BOOL hasReceivedEvent = false; 44 | 45 | [eventBus subscribeTo:TIMER_START subscriber:^(id eventData) 46 | { 47 | hasReceivedEvent = true; 48 | }]; 49 | 50 | [eventBus publish:TIMER_STOP data:nil]; 51 | 52 | XCTAssertFalse(hasReceivedEvent); 53 | } 54 | 55 | - (void)test_subscriber_receives_the_correct_event_data 56 | { 57 | __block id receivedEventData; 58 | id expectedEventData = @"Foo bar"; 59 | 60 | [eventBus subscribeTo:TIMER_START subscriber:^(id eventData) 61 | { 62 | receivedEventData = eventData; 63 | }]; 64 | 65 | [eventBus publish:TIMER_START data:expectedEventData]; 66 | 67 | XCTAssertEqualObjects(receivedEventData, expectedEventData); 68 | } 69 | 70 | - (void)test_two_subscribers_receive_event_of_desired_type 71 | { 72 | __block BOOL hasFirstSubscriberReceivedEvent = false; 73 | __block BOOL hasSecondSubscriberReceivedEvent = false; 74 | 75 | [eventBus subscribeTo:TIMER_START subscriber:^(id eventData) 76 | { 77 | hasFirstSubscriberReceivedEvent = true; 78 | }]; 79 | 80 | [eventBus subscribeTo:TIMER_START subscriber:^(id eventData) 81 | { 82 | hasSecondSubscriberReceivedEvent = true; 83 | }]; 84 | 85 | [eventBus publish:TIMER_START data:nil]; 86 | 87 | XCTAssertTrue(hasFirstSubscriberReceivedEvent); 88 | XCTAssertTrue(hasSecondSubscriberReceivedEvent); 89 | } 90 | 91 | - (void)test_subscribers_only_receive_events_of_desired_type 92 | { 93 | __block BOOL hasFirstSubscriberReceivedEvent = false; 94 | __block BOOL hasSecondSubscriberReceivedEvent = false; 95 | __block BOOL hasThirdSubscriberReceivedEvent = false; 96 | 97 | [eventBus subscribeTo:TIMER_START subscriber:^(id eventData) 98 | { 99 | hasFirstSubscriberReceivedEvent = true; 100 | }]; 101 | 102 | [eventBus subscribeTo:TIMER_TICK subscriber:^(id eventData) 103 | { 104 | hasSecondSubscriberReceivedEvent = true; 105 | }]; 106 | 107 | [eventBus subscribeTo:TIMER_START subscriber:^(id eventData) 108 | { 109 | hasThirdSubscriberReceivedEvent = true; 110 | }]; 111 | 112 | [eventBus publish:TIMER_TICK data:nil]; 113 | 114 | XCTAssertFalse(hasFirstSubscriberReceivedEvent); 115 | XCTAssertTrue(hasSecondSubscriberReceivedEvent); 116 | XCTAssertFalse(hasThirdSubscriberReceivedEvent); 117 | } 118 | 119 | - (void)test_do_not_give_error_when_publishing_event_with_no_subscribers 120 | { 121 | [eventBus publish:TIMER_TICK data:nil]; 122 | } 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/Event/TYSyntheticEventPublisherTests.m: -------------------------------------------------------------------------------- 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 | #define HC_SHORTHAND 9 | #import 10 | 11 | #define MOCKITO_SHORTHAND 12 | #import 13 | 14 | #import 15 | 16 | #import "TYMockEventBus.h" 17 | #import "TYSyntheticEventPublisher.h" 18 | #import "TYTimerContext.h" 19 | 20 | @interface TYSyntheticEventPublisherTests : XCTestCase 21 | 22 | @end 23 | 24 | @implementation TYSyntheticEventPublisherTests 25 | { 26 | TYSyntheticEventPublisher *syntheticEventPublisher; 27 | TYMockEventBus *eventBus; 28 | id timerContext; 29 | } 30 | 31 | - (void)setUp 32 | { 33 | [super setUp]; 34 | 35 | eventBus = [[TYMockEventBus alloc] init]; 36 | timerContext = mockProtocol(@protocol(TYTimerContext)); 37 | syntheticEventPublisher = [[TYSyntheticEventPublisher alloc] init]; 38 | 39 | [syntheticEventPublisher publishSyntheticEventsInResponseToOtherEventsFrom:eventBus]; 40 | } 41 | 42 | - (void)tearDown 43 | { 44 | [super tearDown]; 45 | } 46 | 47 | - (void)test_publish_POMODORO_START_event_when_timer_starts_in_pomodoro_context 48 | { 49 | [given([timerContext getContextType]) willReturnInt:POMODORO]; 50 | 51 | [eventBus publish:TIMER_START data:timerContext]; 52 | 53 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)2); 54 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_START withData:timerContext atPosition:1]); 55 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_START withData:timerContext atPosition:2]); 56 | } 57 | 58 | - (void)test_publish_BREAK_START_and_SHORT_BREAK_START_events_when_timer_starts_in_short_break_context 59 | { 60 | [given([timerContext getContextType]) willReturnInt:SHORT_BREAK]; 61 | 62 | [eventBus publish:TIMER_START data:timerContext]; 63 | 64 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)3); 65 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_START withData:timerContext atPosition:1]); 66 | XCTAssertTrue([eventBus hasPublishedEvent:BREAK_START withData:timerContext atPosition:2]); 67 | XCTAssertTrue([eventBus hasPublishedEvent:SHORT_BREAK_START withData:timerContext atPosition:3]); 68 | } 69 | 70 | - (void)test_publish_BREAK_START_and_LONG_BREAK_START_events_when_timer_starts_in_long_break_context 71 | { 72 | [given([timerContext getContextType]) willReturnInt:LONG_BREAK]; 73 | 74 | [eventBus publish:TIMER_START data:timerContext]; 75 | 76 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)3); 77 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_START withData:timerContext atPosition:1]); 78 | XCTAssertTrue([eventBus hasPublishedEvent:BREAK_START withData:timerContext atPosition:2]); 79 | XCTAssertTrue([eventBus hasPublishedEvent:LONG_BREAK_START withData:timerContext atPosition:3]); 80 | } 81 | 82 | - (void)test_publish_TIMER_GOES_OFF_event_when_timer_stops_and_remaining_seconds_is_zero 83 | { 84 | [given([timerContext getContextType]) willReturnInt:-1]; 85 | [given([timerContext getRemainingSeconds]) willReturnInt:0]; 86 | 87 | [eventBus publish:TIMER_STOP data:timerContext]; 88 | 89 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)2); 90 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_STOP withData:timerContext atPosition:1]); 91 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_GOES_OFF withData:timerContext atPosition:2]); 92 | } 93 | 94 | - (void)test_publish_POMODORO_COMPLETE_event_when_timer_stops_in_pomodoro_context_and_remaining_seconds_is_zero 95 | { 96 | [given([timerContext getContextType]) willReturnInt:POMODORO]; 97 | [given([timerContext getRemainingSeconds]) willReturnInt:0]; 98 | 99 | [eventBus publish:TIMER_STOP data:timerContext]; 100 | 101 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)3); 102 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_STOP withData:timerContext atPosition:1]); 103 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_GOES_OFF withData:timerContext atPosition:2]); 104 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COMPLETE withData:timerContext atPosition:3]); 105 | } 106 | 107 | - (void)test_publish_TIMER_ABORT_event_when_timer_stops_and_remaining_seconds_is_greater_than_zero 108 | { 109 | [given([timerContext getRemainingSeconds]) willReturnInt:1]; 110 | 111 | [eventBus publish:TIMER_STOP data:timerContext]; 112 | 113 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)2); 114 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_STOP withData:timerContext atPosition:1]); 115 | XCTAssertTrue([eventBus hasPublishedEvent:TIMER_ABORT withData:timerContext atPosition:2]); 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/Hotkey/TYHotkeyTests.m: -------------------------------------------------------------------------------- 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 | #import 9 | #import 10 | #import "TYHotkey.h" 11 | 12 | @interface TYHotkeyTests : XCTestCase { 13 | TYHotkey *key; 14 | } 15 | 16 | @end 17 | 18 | @implementation TYHotkeyTests 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | // ⇧⌘S 23 | key = [TYHotkey hotkeyWithCode:0x1 24 | flags:(0 | NSCommandKeyMask | NSShiftKeyMask)]; 25 | } 26 | 27 | - (void)tearDown { 28 | [super tearDown]; 29 | } 30 | 31 | - (void)test_should_have_command { 32 | XCTAssertTrue(key.cmd); 33 | } 34 | 35 | - (void)test_should_have_shift { 36 | XCTAssertTrue(key.shift); 37 | } 38 | 39 | - (void)test_should_not_have_alt { 40 | XCTAssertFalse(key.alt); 41 | } 42 | 43 | - (void)test_should_not_have_control { 44 | XCTAssertFalse(key.ctrl); 45 | } 46 | 47 | - (void)test_should_have_correct_code { 48 | XCTAssertTrue(key.code == 0x1); 49 | } 50 | 51 | - (void)test_should_have_correct_string { 52 | XCTAssertTrue([key.string isEqualToString:@"⇧⌘S"]); 53 | } 54 | 55 | - (void)test_should_return_full_attributed_string 56 | { 57 | NSAttributedString *str = [key mutableAttributedString:nil 58 | textColor:[NSColor redColor] 59 | inactiveColor:nil]; 60 | XCTAssertTrue([str.string isEqualToString:@"^⌥⇧⌘S"]); 61 | } 62 | 63 | - (void)test_should_set_color_to_active_key { 64 | NSAttributedString *str = [key mutableAttributedString:nil 65 | textColor:[NSColor redColor] 66 | inactiveColor:[NSColor greenColor]]; 67 | NSDictionary *attr = [str attributesAtIndex:4 effectiveRange:nil]; 68 | XCTAssertTrue([[attr objectForKey:NSForegroundColorAttributeName] 69 | isEqual:[NSColor redColor]]); 70 | } 71 | 72 | - (void)test_should_set_color_to_active_modifier { 73 | NSAttributedString *str = [key mutableAttributedString:nil 74 | textColor:[NSColor redColor] 75 | inactiveColor:[NSColor greenColor]]; 76 | NSDictionary *attr = [str attributesAtIndex:3 effectiveRange:nil]; 77 | XCTAssertTrue([[attr objectForKey:NSForegroundColorAttributeName] 78 | isEqual:[NSColor redColor]]); 79 | } 80 | 81 | - (void)test_should_set_inactive_color_to_inactive_modifier { 82 | NSAttributedString *str = [key mutableAttributedString:nil 83 | textColor:[NSColor redColor] 84 | inactiveColor:[NSColor greenColor]]; 85 | NSDictionary *attr = [str attributesAtIndex:0 effectiveRange:nil]; 86 | XCTAssertTrue([[attr objectForKey:NSForegroundColorAttributeName] 87 | isEqual:[NSColor greenColor]]); 88 | } 89 | 90 | - (void)test_should_parse_string_correctly { 91 | key.string = @"^D"; 92 | XCTAssertTrue(key.ctrl && key.code == 2); 93 | } 94 | 95 | - (void)test_should_report_invalid_without_key_code { 96 | key.string = @"^"; 97 | XCTAssertFalse(key.valid); 98 | } 99 | 100 | - (void)test_should_report_invalid_if_tab { 101 | key = [TYHotkey hotkeyWithCode:kVK_Tab 102 | flags:(0 | NSCommandKeyMask)]; 103 | XCTAssertFalse(key.valid); 104 | } 105 | 106 | - (void)test_should_report_invalid_if_no_modifier 107 | { 108 | key = [TYHotkey hotkeyWithCode:0x1 flags:0]; 109 | XCTAssertFalse(key.valid); 110 | } 111 | 112 | - (void)test_should_have_code_minum_one_if_invalid 113 | { 114 | key.string = @"R"; 115 | XCTAssertTrue(key.code == -1); 116 | } 117 | 118 | - (void)test_should_have_flags_minus_one_if_invalid 119 | { 120 | key.string = @""; 121 | XCTAssertTrue(key.flags == -1); 122 | } 123 | 124 | - (void)test_string_should_return_nil_if_invalid 125 | { 126 | key.string = @""; 127 | XCTAssertTrue(key.string == nil); 128 | } 129 | 130 | - (void)test_sets_correct_carbon_flags { 131 | key.string = @"⇧⌘S"; 132 | XCTAssertTrue(key.carbonFlags & (shiftKey + cmdKey)); 133 | } 134 | 135 | - (void)test_does_not_set_incorrect_carbon_flags 136 | { 137 | key.string = @"⇧⌘S"; 138 | XCTAssertFalse(key.carbonFlags & optionKey); 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/Preferences/TYPreferencesTests.m: -------------------------------------------------------------------------------- 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 | #import 9 | 10 | #import "TYPreferences.h" 11 | #import "TYUserDefaultsPreferences.h" 12 | #import "TYEventBus.h" 13 | #import "TYMockEventBus.h" 14 | 15 | @interface TYPreferencesTests : XCTestCase 16 | 17 | @end 18 | 19 | static NSString* const HOTKEY_START = @"^⌘P"; 20 | static NSString* const HOTKEY_STOP = @"^⌘S"; 21 | 22 | @implementation TYPreferencesTests 23 | { 24 | id preferences; 25 | TYMockEventBus *eventBus; 26 | } 27 | 28 | - (void)setUp 29 | { 30 | [super setUp]; 31 | 32 | [self removeUserDefaults]; 33 | 34 | eventBus = [[TYMockEventBus alloc] init]; 35 | preferences = [[TYUserDefaultsPreferences alloc] initWith:eventBus]; 36 | } 37 | 38 | - (void)tearDown 39 | { 40 | [self removeUserDefaults]; 41 | [super tearDown]; 42 | } 43 | 44 | - (void)removeUserDefaults 45 | { 46 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:PREF_TIME_POMODORO]; 47 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:PREF_TIME_SHORT_BREAK]; 48 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:PREF_TIME_LONG_BREAK]; 49 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:PREF_PLAY_SOUND_WHEN_TIMER_STARTS]; 50 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF]; 51 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO]; 52 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK]; 53 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:PREF_STATUS_ICON_TIME_FORMAT]; 54 | } 55 | 56 | - (void)test_register_default_values_on_instantiation 57 | { 58 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:PREF_TIME_POMODORO], (NSInteger)25); 59 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:PREF_TIME_SHORT_BREAK], (NSInteger)5); 60 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:PREF_TIME_LONG_BREAK], (NSInteger)15); 61 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:PREF_PLAY_SOUND_WHEN_TIMER_STARTS], (NSInteger)true); 62 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:PREF_PLAY_SOUND_WHEN_TIMER_GOES_OFF], (NSInteger)true); 63 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:PREF_PLAY_TICKTOCK_SOUND_DURING_POMODORO], (NSInteger)true); 64 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:PREF_PLAY_TICKTOCK_SOUND_DURING_BREAK], (NSInteger)true); 65 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:PREF_STATUS_ICON_TIME_FORMAT], (NSInteger)PREF_STATUS_ICON_TIME_FORMAT_NONE); 66 | } 67 | 68 | - (void)test_get_any_default_value 69 | { 70 | XCTAssertEqual([preferences getInt:PREF_TIME_POMODORO], 25); 71 | } 72 | 73 | - (void)test_set_and_get_integer_value 74 | { 75 | [preferences setInt:PREF_TIME_POMODORO value:123]; 76 | XCTAssertEqual([preferences getInt:PREF_TIME_POMODORO], 123); 77 | } 78 | 79 | - (void)test_set_and_get_string_value 80 | { 81 | [preferences setString:PREF_HOTKEY_START value:@"deadbeef"]; 82 | XCTAssertTrue([@"deadbeef" isEqualToString:[preferences 83 | getString:PREF_HOTKEY_START]]); 84 | [preferences setString:PREF_HOTKEY_START value:HOTKEY_START]; 85 | } 86 | 87 | - (void)test_fire_event_when_string_value_changes_on_set 88 | { 89 | [preferences setString:PREF_HOTKEY_STOP value:@""]; 90 | [preferences setString:PREF_HOTKEY_STOP value:@"deadbeef"]; 91 | 92 | XCTAssertNotEqual([eventBus getPublishedEventCount], (NSUInteger)0); 93 | XCTAssertTrue([eventBus hasPublishedEvent:PREFERENCE_CHANGE 94 | withData:PREF_HOTKEY_STOP 95 | atPosition:1]); 96 | [preferences setString:PREF_HOTKEY_STOP value:HOTKEY_STOP]; 97 | } 98 | 99 | - (void)test_fire_event_when_integer_value_changes_on_set 100 | { 101 | [preferences setInt:PREF_TIME_POMODORO value:987]; 102 | 103 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)1); 104 | XCTAssertTrue([eventBus hasPublishedEvent:PREFERENCE_CHANGE withData:PREF_TIME_POMODORO atPosition:1]); 105 | } 106 | 107 | - (void)test_do_not_fire_any_event_when_integer_value_does_not_change_on_set 108 | { 109 | [preferences setInt:PREF_TIME_POMODORO value:25]; 110 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)0); 111 | } 112 | 113 | - (void)test_fire_PREFERENCE_CHANGE_event_only_after_integer_value_is_changed 114 | { 115 | __block int valueWhenEventIsFired; 116 | 117 | [eventBus subscribeTo:PREFERENCE_CHANGE subscriber:^(id eventData) 118 | { 119 | valueWhenEventIsFired = [preferences getInt:PREF_TIME_POMODORO]; 120 | }]; 121 | 122 | [preferences setInt:PREF_TIME_POMODORO value:999]; 123 | 124 | XCTAssertEqual(valueWhenEventIsFired, 999); 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/TYTomightyTests.m: -------------------------------------------------------------------------------- 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 | #define HC_SHORTHAND 9 | #import 10 | 11 | #define MOCKITO_SHORTHAND 12 | #import 13 | 14 | #import 15 | 16 | #import "TYDefaultTomighty.h" 17 | #import "TYMockEventBus.h" 18 | #import "TYPreferences.h" 19 | #import "TYTimer.h" 20 | #import "TYTimerContext.h" 21 | #import "TYTomighty.h" 22 | 23 | @interface TYTomightyTests : XCTestCase 24 | 25 | @end 26 | 27 | @implementation TYTomightyTests 28 | { 29 | id tomighty; 30 | id timer; 31 | id preferences; 32 | TYMockEventBus *eventBus; 33 | MKTArgumentCaptor *timerContextArgument; 34 | } 35 | 36 | - (void)setUp 37 | { 38 | [super setUp]; 39 | timer = mockProtocol(@protocol(TYTimer)); 40 | preferences = mockProtocol(@protocol(TYPreferences)); 41 | eventBus = [[TYMockEventBus alloc] init]; 42 | tomighty = [[TYDefaultTomighty alloc] initWith:timer preferences:preferences eventBus:eventBus]; 43 | timerContextArgument = [[MKTArgumentCaptor alloc] init]; 44 | 45 | [given([preferences getInt:PREF_TIME_POMODORO]) willReturnInt:25]; 46 | [given([preferences getInt:PREF_TIME_LONG_BREAK]) willReturnInt:15]; 47 | [given([preferences getInt:PREF_TIME_SHORT_BREAK]) willReturnInt:5]; 48 | } 49 | 50 | - (void)assertTimerContext:(id )timerContext isOfType:(TYTimerContextType)type hasName:(NSString*)name hasRemainingSeconds:(int)remainingSeconds 51 | { 52 | assertThat([timerContext getName], equalTo(name)); 53 | assertThatInt([timerContext getContextType], equalToInt(type)); 54 | assertThatInt([timerContext getRemainingSeconds], equalToInt(remainingSeconds)); 55 | } 56 | 57 | - (void)test_start_timer_in_pomodoro_context_when_starting_a_pomodoro 58 | { 59 | [tomighty startPomodoro]; 60 | 61 | [verify(timer) start:[timerContextArgument capture]]; 62 | 63 | [self assertTimerContext:[timerContextArgument value] 64 | isOfType:POMODORO 65 | hasName:@"Pomodoro" 66 | hasRemainingSeconds:25 * 60]; 67 | } 68 | 69 | - (void)test_start_timer_in_short_break_context_when_starting_a_short_break 70 | { 71 | [tomighty startShortBreak]; 72 | 73 | [verify(timer) start:[timerContextArgument capture]]; 74 | 75 | [self assertTimerContext:[timerContextArgument value] 76 | isOfType:SHORT_BREAK 77 | hasName:@"Short break" 78 | hasRemainingSeconds:5 * 60]; 79 | } 80 | 81 | - (void)test_start_timer_in_long_break_context_when_starting_a_long_break 82 | { 83 | [tomighty startLongBreak]; 84 | 85 | [verify(timer) start:[timerContextArgument capture]]; 86 | 87 | [self assertTimerContext:[timerContextArgument value] 88 | isOfType:LONG_BREAK 89 | hasName:@"Long break" 90 | hasRemainingSeconds:15 * 60]; 91 | } 92 | 93 | - (void)test_stop_timer 94 | { 95 | [tomighty stopTimer]; 96 | [(id) verify(timer) stop]; 97 | } 98 | 99 | - (void)test_publish_POMODORO_COUNT_CHANGE_each_time_when_a_POMODORO_COMPLETE_event_is_published 100 | { 101 | NSNumber *expectedPomodoroCount; 102 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 103 | 104 | 105 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 106 | 107 | expectedPomodoroCount = [NSNumber numberWithInt:1]; 108 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)2); 109 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COMPLETE withData:timerContext atPosition:1]); 110 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COUNT_CHANGE withData:expectedPomodoroCount atPosition:2]); 111 | 112 | 113 | [eventBus clearPublishedEvents]; 114 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 115 | 116 | expectedPomodoroCount = [NSNumber numberWithInt:2]; 117 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)2); 118 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COMPLETE withData:timerContext atPosition:1]); 119 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COUNT_CHANGE withData:expectedPomodoroCount atPosition:2]); 120 | 121 | 122 | [eventBus clearPublishedEvents]; 123 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 124 | 125 | expectedPomodoroCount = [NSNumber numberWithInt:3]; 126 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)2); 127 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COMPLETE withData:timerContext atPosition:1]); 128 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COUNT_CHANGE withData:expectedPomodoroCount atPosition:2]); 129 | } 130 | 131 | - (void)test_set_pomodoro_count_back_to_one_when_a_pomodoro_completes_after_four_completed_pomodoros 132 | { 133 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 134 | 135 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 136 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 137 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 138 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 139 | 140 | [eventBus clearPublishedEvents]; 141 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 142 | 143 | NSNumber *expectedPomodoroCount = [NSNumber numberWithInt:1]; 144 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)2); 145 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COMPLETE withData:timerContext atPosition:1]); 146 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COUNT_CHANGE withData:expectedPomodoroCount atPosition:2]); 147 | } 148 | 149 | - (void)test_reset_pomodoro_count 150 | { 151 | [tomighty resetPomodoroCount]; 152 | 153 | NSNumber *expectedPomodoroCount = [NSNumber numberWithInt:0]; 154 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)1); 155 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COUNT_CHANGE withData:expectedPomodoroCount atPosition:1]); 156 | } 157 | 158 | - (void)test_pomodoro_count_after_the_count_is_reset 159 | { 160 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 161 | 162 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 163 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 164 | 165 | [tomighty resetPomodoroCount]; 166 | 167 | [eventBus clearPublishedEvents]; 168 | [eventBus publish:POMODORO_COMPLETE data:timerContext]; 169 | 170 | NSNumber *expectedPomodoroCount = [NSNumber numberWithInt:1]; 171 | XCTAssertEqual([eventBus getPublishedEventCount], (NSUInteger)2); 172 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COMPLETE withData:timerContext atPosition:1]); 173 | XCTAssertTrue([eventBus hasPublishedEvent:POMODORO_COUNT_CHANGE withData:expectedPomodoroCount atPosition:2]); 174 | } 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/Timer/TYTimerContextTests.m: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYTimerContext.h" 10 | #import "TYDefaultTimerContext.h" 11 | 12 | @interface TYTimerContextTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation TYTimerContextTests 17 | { 18 | id timerContext; 19 | } 20 | 21 | - (void)test_context_type 22 | { 23 | timerContext = [TYDefaultTimerContext ofType:SHORT_BREAK name:nil remainingSeconds:0]; 24 | XCTAssertEqual(SHORT_BREAK, [timerContext getContextType]); 25 | 26 | timerContext = [TYDefaultTimerContext ofType:POMODORO name:nil remainingSeconds:0]; 27 | XCTAssertEqual(POMODORO, [timerContext getContextType]); 28 | } 29 | 30 | - (void)test_context_name 31 | { 32 | timerContext = [TYDefaultTimerContext ofType:POMODORO name:@"Foo bar" remainingSeconds:0]; 33 | XCTAssertEqualObjects([timerContext getName], @"Foo bar"); 34 | 35 | timerContext = [TYDefaultTimerContext ofType:POMODORO name:@"Hello" remainingSeconds:0]; 36 | XCTAssertEqualObjects([timerContext getName], @"Hello"); 37 | } 38 | 39 | - (void)test_initial_remaining_seconds 40 | { 41 | timerContext = [TYDefaultTimerContext ofType:POMODORO name:nil remainingSeconds:579]; 42 | XCTAssertEqual([timerContext getRemainingSeconds], 579); 43 | } 44 | 45 | - (void)test_add_seconds 46 | { 47 | timerContext = [TYDefaultTimerContext ofType:POMODORO name:nil remainingSeconds:10]; 48 | [timerContext addSeconds:3]; 49 | XCTAssertEqual([timerContext getRemainingSeconds], 13); 50 | } 51 | 52 | - (void)test_subtract_seconds 53 | { 54 | timerContext = [TYDefaultTimerContext ofType:POMODORO name:nil remainingSeconds:10]; 55 | [timerContext addSeconds:-2]; 56 | XCTAssertEqual([timerContext getRemainingSeconds], 8); 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/Timer/TYTimerTests.m: -------------------------------------------------------------------------------- 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 | #define HC_SHORTHAND 9 | #import 10 | 11 | #define MOCKITO_SHORTHAND 12 | #import 13 | 14 | #import 15 | 16 | #import "TYEventBus.h" 17 | #import "TYDefaultTimer.h" 18 | #import "TYTimer.h" 19 | #import "TYTimerContext.h" 20 | #import "TYSystemTimer.h" 21 | #import "TYMockSystemTimer.h" 22 | #import "TYMockTimerContext.h" 23 | 24 | @interface TYTimerTest : XCTestCase 25 | 26 | @end 27 | 28 | @implementation TYTimerTest 29 | 30 | - (void)test_schedule_system_timer_to_trigger_each_second_on_start 31 | { 32 | id eventBus = mockProtocol(@protocol(TYEventBus)); 33 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 34 | id systemTimer = mockProtocol(@protocol(TYSystemTimer)); 35 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 36 | 37 | [timer start:timerContext]; 38 | 39 | [verify(systemTimer) triggerRepeatedly:anything() intervalInSeconds:1]; 40 | } 41 | 42 | - (void)test_stop_system_timer_on_stop 43 | { 44 | id eventBus = mockProtocol(@protocol(TYEventBus)); 45 | id systemTimer = mockProtocol(@protocol(TYSystemTimer)); 46 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 47 | 48 | [timer stop]; 49 | 50 | [verify(systemTimer) interrupt]; 51 | } 52 | 53 | - (void)test_fire_TIMER_START_event_on_start 54 | { 55 | id eventBus = mockProtocol(@protocol(TYEventBus)); 56 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 57 | id systemTimer = mockProtocol(@protocol(TYSystemTimer)); 58 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 59 | 60 | [timer start:timerContext]; 61 | 62 | [verify(eventBus) publish:TIMER_START data:timerContext]; 63 | } 64 | 65 | - (void)test_fire_TIMER_STOP_event_on_stop 66 | { 67 | id eventBus = mockProtocol(@protocol(TYEventBus)); 68 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 69 | id systemTimer = mockProtocol(@protocol(TYSystemTimer)); 70 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 71 | 72 | [timer start:timerContext]; 73 | [verify(eventBus) publish:TIMER_START data:timerContext]; 74 | 75 | [timer stop]; 76 | [verify(eventBus) publish:TIMER_STOP data:timerContext]; 77 | } 78 | 79 | - (void)test_fire_TIMER_TICK_event_each_time_a_second_ellapses 80 | { 81 | id eventBus = mockProtocol(@protocol(TYEventBus)); 82 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 83 | TYMockSystemTimer *systemTimer = [[TYMockSystemTimer alloc] init]; 84 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 85 | 86 | [given([timerContext getRemainingSeconds]) willReturnInt:999]; 87 | 88 | [timer start:timerContext]; 89 | [verify(eventBus) publish:TIMER_START data:timerContext]; 90 | 91 | [systemTimer tick]; 92 | [verify(eventBus) publish:TIMER_TICK data:timerContext]; 93 | 94 | [systemTimer tick]; 95 | [verifyCount(eventBus, times(2)) publish:TIMER_TICK data:timerContext]; 96 | 97 | [systemTimer tick]; 98 | [verifyCount(eventBus, times(3)) publish:TIMER_TICK data:timerContext]; 99 | } 100 | 101 | - (void)test_subtract_one_second_from_the_timer_context_each_time_a_second_ellapses 102 | { 103 | id eventBus = mockProtocol(@protocol(TYEventBus)); 104 | id timerContext = mockProtocol(@protocol(TYTimerContext)); 105 | TYMockSystemTimer *systemTimer = [[TYMockSystemTimer alloc] init]; 106 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 107 | 108 | [given([timerContext getRemainingSeconds]) willReturnInt:999]; 109 | 110 | [timer start:timerContext]; 111 | 112 | [systemTimer tick]; 113 | [verify(timerContext) addSeconds:-1]; 114 | 115 | [systemTimer tick]; 116 | [verifyCount(timerContext, times(2)) addSeconds:-1]; 117 | 118 | [systemTimer tick]; 119 | [verifyCount(timerContext, times(3)) addSeconds:-1]; 120 | } 121 | 122 | - (void)test_fire_TIMER_STOP_event_when_the_timer_goes_off 123 | { 124 | id eventBus = mockProtocol(@protocol(TYEventBus)); 125 | TYMockTimerContext *timerContext = [[TYMockTimerContext alloc] initWithRemainingSeconds:3]; 126 | TYMockSystemTimer *systemTimer = [[TYMockSystemTimer alloc] init]; 127 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 128 | 129 | [timer start:timerContext]; 130 | [verify(eventBus) publish:TIMER_START data:timerContext]; 131 | 132 | [systemTimer tick]; 133 | [verify(eventBus) publish:TIMER_TICK data:timerContext]; 134 | 135 | [systemTimer tick]; 136 | [verifyCount(eventBus, times(2)) publish:TIMER_TICK data:timerContext]; 137 | 138 | [systemTimer tick]; 139 | [verify(eventBus) publish:TIMER_STOP data:timerContext]; 140 | } 141 | 142 | - (void)test_interrupt_system_timer_when_timer_goes_off 143 | { 144 | id eventBus = mockProtocol(@protocol(TYEventBus)); 145 | TYMockTimerContext *timerContext = [[TYMockTimerContext alloc] initWithRemainingSeconds:3]; 146 | TYMockSystemTimer *systemTimer = [[TYMockSystemTimer alloc] init]; 147 | id timer = [TYDefaultTimer createWith:eventBus systemTimer:systemTimer]; 148 | 149 | [timer start:timerContext]; 150 | XCTAssertFalse([systemTimer isInterrupted]); 151 | 152 | [systemTimer tick]; 153 | XCTAssertFalse([systemTimer isInterrupted]); 154 | 155 | [systemTimer tick]; 156 | XCTAssertFalse([systemTimer isInterrupted]); 157 | 158 | [systemTimer tick]; 159 | XCTAssertTrue([systemTimer isInterrupted]); 160 | } 161 | 162 | @end 163 | -------------------------------------------------------------------------------- /src/TomightyTests/Core/UI/TYAppUITests.m: -------------------------------------------------------------------------------- 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 | #define HC_SHORTHAND 9 | #import 10 | 11 | #define MOCKITO_SHORTHAND 12 | #import 13 | 14 | #import 15 | 16 | #import "TYAppUI.h" 17 | #import "TYDefaultAppUI.h" 18 | #import "TYStatusIcon.h" 19 | #import "TYStatusMenu.h" 20 | 21 | @interface TYAppUITests : XCTestCase 22 | 23 | @end 24 | 25 | @implementation TYAppUITests 26 | { 27 | id appUi; 28 | id statusMenu; 29 | id statusIcon; 30 | } 31 | 32 | - (void)setUp 33 | { 34 | [super setUp]; 35 | statusMenu = mockProtocol(@protocol(TYStatusMenu)); 36 | statusIcon = mockProtocol(@protocol(TYStatusIcon)); 37 | appUi = [[TYDefaultAppUI alloc] initWith:statusMenu statusIcon:statusIcon]; 38 | } 39 | 40 | - (void)test_switch_to_idle_state 41 | { 42 | [appUi switchToIdleState]; 43 | 44 | [verify(statusMenu) enableStopTimerItem:false]; 45 | [verify(statusMenu) enableStartPomodoroItem:true]; 46 | [verify(statusMenu) enableStartShortBreakItem:true]; 47 | [verify(statusMenu) enableStartLongBreakItem:true]; 48 | [verify(statusMenu) setRemainingTimeText:@"00:00"]; 49 | [verify(statusIcon) changeIcon:ICON_STATUS_IDLE]; 50 | } 51 | 52 | - (void)test_switch_to_pomodoro_state 53 | { 54 | [appUi switchToPomodoroState]; 55 | 56 | [verify(statusMenu) enableStopTimerItem:true]; 57 | [verify(statusMenu) enableStartPomodoroItem:false]; 58 | [verify(statusMenu) enableStartShortBreakItem:true]; 59 | [verify(statusMenu) enableStartLongBreakItem:true]; 60 | [verifyCount(statusMenu, never()) setRemainingTimeText:anything()]; 61 | [verify(statusIcon) changeIcon:ICON_STATUS_POMODORO]; 62 | } 63 | 64 | - (void)test_switch_to_short_break_state 65 | { 66 | [appUi switchToShortBreakState]; 67 | 68 | [verify(statusMenu) enableStopTimerItem:true]; 69 | [verify(statusMenu) enableStartPomodoroItem:true]; 70 | [verify(statusMenu) enableStartShortBreakItem:false]; 71 | [verify(statusMenu) enableStartLongBreakItem:true]; 72 | [verifyCount(statusMenu, never()) setRemainingTimeText:anything()]; 73 | [verify(statusIcon) changeIcon:ICON_STATUS_SHORT_BREAK]; 74 | } 75 | 76 | - (void)test_switch_to_long_break_state 77 | { 78 | [appUi switchToLongBreakState]; 79 | 80 | [verify(statusMenu) enableStopTimerItem:true]; 81 | [verify(statusMenu) enableStartPomodoroItem:true]; 82 | [verify(statusMenu) enableStartShortBreakItem:true]; 83 | [verify(statusMenu) enableStartLongBreakItem:false]; 84 | [verifyCount(statusMenu, never()) setRemainingTimeText:anything()]; 85 | [verify(statusIcon) changeIcon:ICON_STATUS_LONG_BREAK]; 86 | } 87 | 88 | - (void)test_update_remaining_time_to_zero_seconds 89 | { 90 | [appUi updateRemainingTime:0 withMode:TYAppUIRemainingTimeModeDefault]; 91 | [verify(statusMenu) setRemainingTimeText:@"00:00"]; 92 | [verify(statusIcon) setStatusText:@""]; 93 | 94 | [appUi setStatusIconTextFormat:TYAppUIStatusIconTextFormatMinutes]; 95 | [verify(statusIcon) setStatusText:@" Stopped"]; 96 | } 97 | 98 | - (void)test_update_remaining_time_to_one_second 99 | { 100 | [appUi updateRemainingTime:1 withMode:TYAppUIRemainingTimeModeDefault]; 101 | [verify(statusMenu) setRemainingTimeText:@"00:01"]; 102 | [verify(statusIcon) setStatusText:@""]; 103 | 104 | [appUi setStatusIconTextFormat:TYAppUIStatusIconTextFormatMinutes]; 105 | [verify(statusIcon) setStatusText:@" 1 m"]; 106 | 107 | [appUi setStatusIconTextFormat:TYAppUIStatusIconTextFormatSeconds]; 108 | [verify(statusIcon) setStatusText:@" 00:01"]; 109 | } 110 | 111 | - (void)test_update_remaining_time_to_fifty_nine_seconds 112 | { 113 | [appUi updateRemainingTime:59 withMode:TYAppUIRemainingTimeModeDefault]; 114 | [verify(statusMenu) setRemainingTimeText:@"00:59"]; 115 | } 116 | 117 | - (void)test_update_remaining_time_to_one_minute 118 | { 119 | [appUi updateRemainingTime:60 withMode:TYAppUIRemainingTimeModeDefault]; 120 | [verify(statusMenu) setRemainingTimeText:@"01:00"]; 121 | } 122 | 123 | - (void)test_update_remaining_time_to_zero_fifty_nine_minutes_and_fifty_nine_seconds 124 | { 125 | [appUi updateRemainingTime:59 * 60 + 59 withMode:TYAppUIRemainingTimeModeDefault]; 126 | [verify(statusMenu) setRemainingTimeText:@"59:59"]; 127 | [verify(statusIcon) setStatusText:@""]; 128 | 129 | [appUi setStatusIconTextFormat:TYAppUIStatusIconTextFormatMinutes]; 130 | [verify(statusIcon) setStatusText:@" 60 m"]; 131 | 132 | [appUi setStatusIconTextFormat:TYAppUIStatusIconTextFormatSeconds]; 133 | [verify(statusIcon) setStatusText:@" 59:59"]; 134 | } 135 | 136 | - (void)test_update_remaining_time_with_start_mode 137 | { 138 | [appUi setStatusIconTextFormat:TYAppUIStatusIconTextFormatMinutes]; 139 | 140 | [appUi updateRemainingTime:25 * 60 withMode:TYAppUIRemainingTimeModeStart]; 141 | [verify(statusMenu) setRemainingTimeText:@"25:00"]; 142 | [verify(statusIcon) setStatusText:@" 25 m"]; 143 | 144 | [appUi updateRemainingTime:23 * 60 + 59 withMode:TYAppUIRemainingTimeModeDefault]; 145 | [verify(statusMenu) setRemainingTimeText:@"23:59"]; 146 | [verify(statusIcon) setStatusText:@" 24 m"]; 147 | 148 | } 149 | 150 | - (void)test_update_remaining_time_to_fifty_nine_seconds_with_format_minutes 151 | { 152 | [appUi setStatusIconTextFormat:TYAppUIStatusIconTextFormatMinutes]; 153 | 154 | [appUi updateRemainingTime:59 withMode:TYAppUIRemainingTimeModeDefault]; 155 | [verify(statusIcon) setStatusText:@" 59 s"]; 156 | } 157 | 158 | - (void)test_update_pomodoro_count_to_zero 159 | { 160 | [appUi updatePomodoroCount:0]; 161 | [verify(statusMenu) setPomodoroCountText:@"No pomodoros"]; 162 | } 163 | 164 | - (void)test_update_pomodoro_count_to_one 165 | { 166 | [appUi updatePomodoroCount:1]; 167 | [verify(statusMenu) setPomodoroCountText:@"1 pomodoro"]; 168 | } 169 | 170 | - (void)test_update_pomodoro_count_to_two 171 | { 172 | [appUi updatePomodoroCount:2]; 173 | [verify(statusMenu) setPomodoroCountText:@"2 pomodoros"]; 174 | } 175 | 176 | - (void)test_disable_pomodoro_count_reset_menu_item_when_pomodoro_count_is_zero 177 | { 178 | [appUi updatePomodoroCount:0]; 179 | [verify(statusMenu) enableResetPomodoroCountItem:false]; 180 | } 181 | 182 | - (void)test_enable_pomodoro_count_reset_menu_item_when_pomodoro_count_is_greater_than_zero 183 | { 184 | [appUi updatePomodoroCount:1]; 185 | [verify(statusMenu) enableResetPomodoroCountItem:true]; 186 | } 187 | 188 | @end 189 | -------------------------------------------------------------------------------- /src/TomightyTests/Mock/TYMockEventBus.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYEventBus.h" 10 | 11 | @interface TYMockEventBus : NSObject 12 | 13 | - (void)clearPublishedEvents; 14 | - (NSUInteger)getPublishedEventCount; 15 | - (BOOL)hasPublishedEvent:(TYEventType)eventType withData:(id)eventData; 16 | - (BOOL)hasPublishedEvent:(TYEventType)eventType withData:(id)eventData atPosition:(NSUInteger)position; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /src/TomightyTests/Mock/TYMockEventBus.m: -------------------------------------------------------------------------------- 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 | #import "TYMockEventBus.h" 9 | #import "TYPublishedEvent.h" 10 | 11 | @implementation TYMockEventBus 12 | { 13 | NSMutableDictionary *subscribers; 14 | NSMutableArray *publishedEvents; 15 | } 16 | 17 | - (id)init 18 | { 19 | self = [super init]; 20 | if(self) 21 | { 22 | subscribers = [NSMutableDictionary dictionaryWithCapacity:8]; 23 | publishedEvents = [NSMutableArray arrayWithCapacity:8]; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)subscribeTo:(TYEventType)eventType subscriber:(TYEventSubscriber)subscriber 29 | { 30 | subscribers[@(eventType)] = subscriber; 31 | } 32 | 33 | - (void)publish:(TYEventType)eventType data:(id)data 34 | { 35 | [publishedEvents addObject:[[TYPublishedEvent alloc] initWith:eventType data:data]]; 36 | 37 | TYEventSubscriber subscriber = [subscribers objectForKey:@(eventType)]; 38 | if(subscriber) 39 | { 40 | subscriber(data); 41 | } 42 | } 43 | 44 | - (void)clearPublishedEvents 45 | { 46 | [publishedEvents removeAllObjects]; 47 | } 48 | 49 | - (BOOL)hasPublishedEvent:(TYEventType)eventType withData:(id)eventData 50 | { 51 | for(NSUInteger index = 0; index < [publishedEvents count]; index++) 52 | { 53 | TYPublishedEvent *publishedEvent = [publishedEvents objectAtIndex:index]; 54 | if([publishedEvent matches:eventType data:eventData]) 55 | { 56 | return true; 57 | } 58 | } 59 | return false; 60 | } 61 | 62 | - (BOOL)hasPublishedEvent:(TYEventType)eventType withData:(id)eventData atPosition:(NSUInteger)position 63 | { 64 | if(position > [publishedEvents count] || position <= 0) 65 | { 66 | return false; 67 | } 68 | TYPublishedEvent *publishedEvent = [publishedEvents objectAtIndex:(position - 1)]; 69 | if(!publishedEvent) 70 | { 71 | return false; 72 | } 73 | return [publishedEvent matches:eventType data:eventData]; 74 | } 75 | 76 | - (NSUInteger)getPublishedEventCount 77 | { 78 | return [publishedEvents count]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /src/TomightyTests/Mock/TYMockSystemTimer.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYSystemTimer.h" 10 | 11 | @interface TYMockSystemTimer : NSObject 12 | 13 | - (void)tick; 14 | - (BOOL)isInterrupted; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /src/TomightyTests/Mock/TYMockSystemTimer.m: -------------------------------------------------------------------------------- 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 | #import "TYMockSystemTimer.h" 9 | 10 | @implementation TYMockSystemTimer 11 | { 12 | TYSystemTimerTrigger trigger; 13 | BOOL isInterrupted; 14 | } 15 | 16 | - (id)init 17 | { 18 | self = [super init]; 19 | if(self) 20 | { 21 | isInterrupted = false; 22 | } 23 | return self; 24 | } 25 | 26 | - (void)tick 27 | { 28 | trigger(); 29 | } 30 | 31 | - (void)triggerRepeatedly:(TYSystemTimerTrigger)aTrigger intervalInSeconds:(int)seconds 32 | { 33 | trigger = aTrigger; 34 | } 35 | 36 | - (void)interrupt 37 | { 38 | isInterrupted = true; 39 | } 40 | 41 | - (BOOL)isInterrupted 42 | { 43 | return isInterrupted; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /src/TomightyTests/Mock/TYMockTimerContext.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYTimerContext.h" 10 | 11 | @interface TYMockTimerContext : NSObject 12 | 13 | - (id)initWithRemainingSeconds:(int)initialRemainingSeconds; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /src/TomightyTests/Mock/TYMockTimerContext.m: -------------------------------------------------------------------------------- 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 | #import "TYMockTimerContext.h" 9 | 10 | @implementation TYMockTimerContext 11 | { 12 | int remainingSeconds; 13 | } 14 | 15 | - (id)initWithRemainingSeconds:(int)initialRemainingSeconds 16 | { 17 | self = [super init]; 18 | if(self) 19 | { 20 | remainingSeconds = initialRemainingSeconds; 21 | } 22 | return self; 23 | } 24 | 25 | - (NSString *)getName 26 | { 27 | return nil; 28 | } 29 | 30 | - (int)getRemainingSeconds 31 | { 32 | return remainingSeconds; 33 | } 34 | 35 | - (void)addSeconds:(int)seconds 36 | { 37 | remainingSeconds += seconds; 38 | } 39 | 40 | - (TYTimerContextType)getContextType 41 | { 42 | return -1; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /src/TomightyTests/Mock/TYPublishedEvent.h: -------------------------------------------------------------------------------- 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 | #import 9 | #import "TYEventBus.h" 10 | 11 | @interface TYPublishedEvent : NSObject 12 | 13 | - (id)initWith:(TYEventType)eventType data:(id)data; 14 | - (BOOL)matches:(TYEventType)eventType data:(id)data; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /src/TomightyTests/Mock/TYPublishedEvent.m: -------------------------------------------------------------------------------- 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 | #import "TYPublishedEvent.h" 9 | 10 | @implementation TYPublishedEvent 11 | { 12 | TYEventType eventType; 13 | id eventData; 14 | } 15 | 16 | - (id)initWith:(TYEventType)anEventType data:(id)someEventData 17 | { 18 | self = [super init]; 19 | if(self) 20 | { 21 | eventType = anEventType; 22 | eventData = someEventData; 23 | } 24 | return self; 25 | } 26 | 27 | - (BOOL)matches:(TYEventType)anotherEventType data:(id)anotherEventData 28 | { 29 | return 30 | eventType == anotherEventType && 31 | eventData == anotherEventData; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /src/TomightyTests/TomightyTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/TomightyTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /src/tomighty.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomighty/tomighty-osx/63ee855bfdfffbfda0a0c2ddc1da38c1706e9d47/src/tomighty.icns -------------------------------------------------------------------------------- /svg/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /svg/export-svg-to-png.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm *.png 4 | rm *.tiff 5 | 6 | INKSCAPE=/Applications/Inkscape.app/Contents/Resources/bin/inkscape 7 | 8 | for svg_file in *.svg 9 | do 10 | filename="${svg_file%.*}" 11 | png_file="$filename.png" 12 | echo "----------------------------------------------" 13 | echo "Exporting $svg_file to $png_file" 14 | $INKSCAPE -z --export-area-page --export-png "$png_file" "$svg_file" 15 | done 16 | -------------------------------------------------------------------------------- /svg/start-long-break.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 48 | 49 | 50 | 82 | -------------------------------------------------------------------------------- /svg/start-pomodoro.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 48 | 49 | 50 | 82 | -------------------------------------------------------------------------------- /svg/start-short-break.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 48 | 49 | 50 | 82 | -------------------------------------------------------------------------------- /svg/status-long-break.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 48 | 49 | 50 | 85 | -------------------------------------------------------------------------------- /svg/status-pomodoro.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 48 | 49 | 50 | 82 | -------------------------------------------------------------------------------- /svg/status-short-break.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 48 | 49 | 50 | 82 | -------------------------------------------------------------------------------- /svg/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 44 | 50 | -------------------------------------------------------------------------------- /svg/tomighty-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 48 | 49 | 50 | 82 | --------------------------------------------------------------------------------