├── .gitignore
├── .idea
├── .gitignore
├── artifacts
│ └── MyTomato_jar.xml
├── compiler.xml
├── description.html
├── encodings.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── libraries
│ ├── bridj_0_7_0.xml
│ ├── jl1_0_1.xml
│ └── log4j_1_2_17.xml
├── misc.xml
├── modules.xml
├── uiDesigner.xml
└── vcs.xml
├── MyTomato.iml
├── README.assets
└── 1.jpg
├── README.md
├── lib
├── bridj-0.7.0.jar
├── fastjson-1.2.61.jar
├── jl1.0.1.jar
└── log4j-1.2.17.jar
├── prefsdemo.xml
├── res
├── image
│ └── tomato.png
├── json
│ └── tomatoTaskData.json
├── properties
│ └── log4j.properties
└── sound
│ ├── add_tasks_ahead_of_time.mp3
│ ├── bgm_Ticking.mp3
│ ├── bgm_WindThroughTrees.mp3
│ ├── old_add_tasks_ahead_of_time.mp3
│ ├── respite_finished.mp3
│ └── work_finished.mp3
└── src
├── META-INF
└── MANIFEST.MF
├── app
├── Main.java
├── control
│ ├── GirdColumn.java
│ ├── GirdColumnFactory.java
│ ├── GridPane.java
│ ├── OnTopAlert.java
│ ├── StackedPanes.java
│ ├── TextWrapCell.java
│ └── mytomato
│ │ ├── GridPane.java
│ │ ├── StackedPanes.java
│ │ └── TitledPane.java
├── model
│ └── TomatoTask.java
├── util
│ ├── BriefReport.java
│ ├── CountDown.java
│ ├── DataManager.java
│ ├── DateAndTime.java
│ ├── GL.java
│ ├── ListJson.java
│ ├── MapJson.java
│ ├── Mp3Player.java
│ ├── PropertiesManager.java
│ ├── ResGetter.java
│ ├── TaskBarProgressbar.java
│ └── TimeStringPolisher.java
└── view
│ ├── Close_512x512.png
│ ├── Controller.java
│ ├── DarkTheme.css
│ ├── EditDialog.fxml
│ ├── EditDialogControl.java
│ ├── Edit_512x512.png
│ ├── FinishDialog.fxml
│ ├── FinishDialogController.java
│ ├── MainLayout.fxml
│ ├── MainLayoutController.java
│ ├── PlusDialog.fxml
│ ├── PlusDialogController.java
│ ├── RootLayout.fxml
│ ├── RootLayoutController.java
│ ├── SettingDialog.fxml
│ ├── SettingDialogController.java
│ ├── StackedPanesController.java
│ ├── Test.fxml
│ ├── TestController.java
│ ├── Trash_67981_512x512.png
│ ├── modena.css
│ └── stylesheet.css
├── sample
├── Parent.java
├── Sample.java
└── test.fxml
└── test
├── Demo.java
├── JavaStringFormat.java
├── LocalTimeTest.java
├── OldPlay.java
├── PathGetMain.java
└── newPlay.java
/.gitignore:
--------------------------------------------------------------------------------
1 | /out
2 | /.idea/workspace.xml
3 | /srcText/app/view/FinishDialog.fxml
4 | /res/properties/Settings.properties
5 | /res/log4j.properties
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/artifacts/MyTomato_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/../MyTomatoAPP
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/description.html:
--------------------------------------------------------------------------------
1 | Simple JavaFX 2.0 application that includes simple .fxml file with attached controller and Main class to quick start. Artifact to build JavaFX application is provided.
2 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/libraries/bridj_0_7_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/jl1_0_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/libraries/log4j_1_2_17.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
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 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MyTomato.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/README.assets/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/README.assets/1.jpg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [MyTomato简介](https://github.com/Andy-AO/MyTomato)
2 |
3 | 用JavaFX写的跨平台个人番茄工作法工具,[开源免费](https://github.com/Andy-AO/MyTomato),轻量而纯粹,没有任何多余的功能。
4 |
5 | 已经作为主力番茄工作法工具使用接近1年,现有的方案基本上都是Electron,会占用大量资源,而且很多都收费。
6 |
7 | 看到 V2EX 上的那个帖子,所以准备贡献出来,给和我一样不太喜欢 Electron 的人用。
8 |
9 | 本来是自用的,能用就行,所以很长时间没改进了,后期可能会继续改进。
10 |
11 | [我的软件“wnr”上架了 Product Hunt! - V2EX](https://www.v2ex.com/t/698913#reply22)
12 |
13 |
14 |
15 |
16 |
17 | # 下载
18 |
19 | [Releases · Andy-AO/MyTomato](https://github.com/Andy-AO/MyTomato/releases)
20 |
21 | 解压后运行`MyTomato.jar`,需要Java8及以上环境。
22 |
23 | 理论上跨平台,但并未实际测试。
--------------------------------------------------------------------------------
/lib/bridj-0.7.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/lib/bridj-0.7.0.jar
--------------------------------------------------------------------------------
/lib/fastjson-1.2.61.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/lib/fastjson-1.2.61.jar
--------------------------------------------------------------------------------
/lib/jl1.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/lib/jl1.0.1.jar
--------------------------------------------------------------------------------
/lib/log4j-1.2.17.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/lib/log4j-1.2.17.jar
--------------------------------------------------------------------------------
/prefsdemo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/res/image/tomato.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/res/image/tomato.png
--------------------------------------------------------------------------------
/res/properties/log4j.properties:
--------------------------------------------------------------------------------
1 | ### 设置###
2 | log4j.rootLogger = debug,stdout,R
3 |
4 | ### 输出信息到控制台 ###
5 | log4j.appender.stdout = org.apache.log4j.ConsoleAppender
6 | log4j.appender.stdout.Target = System.out
7 | log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
8 | log4j.appender.stdout.layout.ConversionPattern =%-5p [%t] %l%n%m%n%n
9 |
10 | log4j.appender.R=org.apache.log4j.RollingFileAppender
11 | log4j.appender.R.File=MyTomato.log
12 | log4j.appender.R.MaxFileSize=1024KB
13 | log4j.appender.R.MaxBackupIndex=5
14 | log4j.appender.R.layout=org.apache.log4j.PatternLayout
15 | log4j.appender.R.layout.ConversionPattern=%d{yyy-MM-dd HH:mm:ss} - %-5p [%t] %l%n%m%n%n
--------------------------------------------------------------------------------
/res/sound/add_tasks_ahead_of_time.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/res/sound/add_tasks_ahead_of_time.mp3
--------------------------------------------------------------------------------
/res/sound/bgm_Ticking.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/res/sound/bgm_Ticking.mp3
--------------------------------------------------------------------------------
/res/sound/bgm_WindThroughTrees.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/res/sound/bgm_WindThroughTrees.mp3
--------------------------------------------------------------------------------
/res/sound/old_add_tasks_ahead_of_time.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/res/sound/old_add_tasks_ahead_of_time.mp3
--------------------------------------------------------------------------------
/res/sound/respite_finished.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/res/sound/respite_finished.mp3
--------------------------------------------------------------------------------
/res/sound/work_finished.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/res/sound/work_finished.mp3
--------------------------------------------------------------------------------
/src/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Main-Class: app.Main
3 |
4 |
--------------------------------------------------------------------------------
/src/app/Main.java:
--------------------------------------------------------------------------------
1 | package app;
2 |
3 | import app.control.OnTopAlert;
4 | import app.control.mytomato.StackedPanes;
5 | import app.util.*;
6 | import app.view.*;
7 | import javafx.application.Application;
8 | import javafx.application.Platform;
9 | import javafx.beans.value.ChangeListener;
10 | import javafx.beans.value.ObservableValue;
11 | import javafx.collections.FXCollections;
12 | import javafx.collections.ListChangeListener;
13 | import javafx.collections.ObservableList;
14 | import javafx.collections.ObservableMap;
15 | import javafx.event.EventHandler;
16 | import javafx.fxml.FXMLLoader;
17 | import javafx.scene.Scene;
18 | import javafx.scene.control.Alert;
19 | import javafx.scene.control.ButtonType;
20 | import javafx.scene.control.TabPane;
21 | import javafx.scene.image.Image;
22 | import javafx.scene.layout.AnchorPane;
23 | import javafx.scene.layout.BorderPane;
24 | import javafx.stage.Stage;
25 | import app.model.TomatoTask;
26 | import javafx.stage.WindowEvent;
27 |
28 | import java.io.File;
29 | import java.io.IOException;
30 | import java.net.URL;
31 | import java.time.Duration;
32 | import java.time.LocalDate;
33 | import java.util.HashMap;
34 | import java.util.List;
35 |
36 | public class Main extends Application {
37 |
38 | public static final int PRIMARY_STAGE_MIN_WIDTH = 415;
39 | private static final ObservableMap> TOMATO_TASKS_MAP = FXCollections.observableMap(new HashMap());
40 | public static final Duration DEFAULT_WORK_DURATION = Duration.ofMinutes(25);
41 | public static final CountDown WORK_COUNT_DOWN = new CountDown(DEFAULT_WORK_DURATION);
42 | public static final Duration DEFAULT_RESPITE_DURATION = Duration.ofMinutes(5);
43 | public static final CountDown RESPITE_COUNT_DOWN = new CountDown(DEFAULT_RESPITE_DURATION);
44 | public static final Duration DEVELOPMENT_DURATION = Duration.ofSeconds(3);
45 | public static final Mp3Player WORK_DURATION_MP3_PLAYER = new Mp3Player(new File(ResGetter.getResFile(), "sound/bgm_Ticking.mp3"));
46 | public static final Mp3Player RESPITE_DURATION_MP3_PLAYER = new Mp3Player(new File(ResGetter.getResFile(), "sound/bgm_WindThroughTrees.mp3"));
47 |
48 | public static final Mp3Player WORK_FINISHED_MP3_PLAYER = new Mp3Player(new File(ResGetter.getResFile(), "sound/work_finished.mp3"));
49 | public static final Mp3Player RESPITE_FINISHED_MP3_PLAYER = new Mp3Player(new File(ResGetter.getResFile(), "sound/respite_finished.mp3"));
50 |
51 | public static final PropertiesManager PROPERTIES_MANAGER = PropertiesManager.getPropertiesManager();
52 |
53 | private BorderPane rootLayout;
54 | private AnchorPane mainLayout;
55 |
56 | private Stage primaryStage;
57 | private Stage finishDialogStage = null;
58 | private Stage plusDialogStage = null;
59 | private Stage startSettingStage = null;
60 |
61 | private Image tomatoImage = new Image(ResGetter.getResURLString() + "image/tomato.png");
62 | private AnchorPane editDialog;
63 | private EditDialogControl editDialogController;
64 | private Stage editDialogStage = null;
65 | private AnchorPane plusDialog;
66 | private PlusDialogController plusDialogController;
67 | private StackedPanesController stackedPanesController;
68 | private DataManager tomatoTaskDataMapJson;
69 |
70 | private AnchorPane finishDialog;
71 | private StackedPanes stackedPanes;
72 | private FinishDialogController finishDialogController;
73 |
74 | private MainLayoutController mainLayoutController;
75 | private RootLayoutController rootLayoutController;
76 | private TabPane settingDialog;
77 | private SettingDialogController settingDialogController;
78 |
79 | private ObservableList REDO_TOMATO_TASKS = FXCollections.observableArrayList();
80 |
81 | private final File JSON_FILE = new File(ResGetter.getResFile(), "json\\tomatoTaskData.json");
82 |
83 | public TabPane getSettingDialog() {
84 | return settingDialog;
85 | }
86 |
87 | public void setSettingDialog(TabPane settingDialog) {
88 | this.settingDialog = settingDialog;
89 | }
90 |
91 | public SettingDialogController getSettingDialogController() {
92 | return settingDialogController;
93 | }
94 |
95 | public void setSettingDialogController(SettingDialogController settingDialogController) {
96 | this.settingDialogController = settingDialogController;
97 | }
98 |
99 |
100 | public FinishDialogController getFinishDialogController() {
101 | return finishDialogController;
102 | }
103 |
104 | public void setFinishDialogController(FinishDialogController finishDialogController) {
105 | this.finishDialogController = finishDialogController;
106 | }
107 |
108 | public Stage getPrimaryStage() {
109 | return primaryStage;
110 | }
111 |
112 | public void setPrimaryStage(Stage primaryStage) {
113 | this.primaryStage = primaryStage;
114 | }
115 |
116 | public BorderPane getRootLayout() {
117 | return rootLayout;
118 | }
119 |
120 | public void setRootLayout(BorderPane rootLayout) {
121 | this.rootLayout = rootLayout;
122 | }
123 |
124 | public AnchorPane getMainLayout() {
125 | return mainLayout;
126 | }
127 |
128 | public void setMainLayout(AnchorPane mainLayout) {
129 | this.mainLayout = mainLayout;
130 | }
131 |
132 |
133 | public AnchorPane getFinishDialog() {
134 | return finishDialog;
135 | }
136 |
137 | public void setFinishDialog(AnchorPane finishDialog) {
138 | this.finishDialog = finishDialog;
139 | }
140 |
141 | public static ObservableMap> getTomatoTasksMap() {
142 | return TOMATO_TASKS_MAP;
143 | }
144 |
145 | public DataManager getTomatoTaskDataMapJson() {
146 | return tomatoTaskDataMapJson;
147 | }
148 |
149 | public MainLayoutController getMainLayoutController() {
150 | return mainLayoutController;
151 | }
152 |
153 | public void setMainLayoutController(MainLayoutController mainLayoutController) {
154 | this.mainLayoutController = mainLayoutController;
155 | }
156 |
157 | public RootLayoutController getRootLayoutController() {
158 | return rootLayoutController;
159 | }
160 |
161 | public void setRootLayoutController(RootLayoutController rootLayoutController) {
162 | this.rootLayoutController = rootLayoutController;
163 | }
164 |
165 |
166 | @Override
167 | public void start(Stage primaryStage) throws Exception {
168 | this.primaryStage = primaryStage;
169 | loadLayout();
170 | intiTomatoTaskData();
171 | setMainAndInitLayout();
172 | initPrimaryStage();
173 | this.primaryStage.show();
174 | }
175 |
176 | private void setMainAndInitLayout() {
177 | mainLayoutController.setMainAndInit(this);
178 | finishDialogController.setMainAndInit(this);
179 | plusDialogController.setMainAndInit(this);
180 | rootLayoutController.setMainAndInit(this);
181 | settingDialogController.setMainAndInit(this);
182 | editDialogController.setMainAndInit(this);
183 | stackedPanesController.setMainAndInit(this);
184 |
185 | }
186 |
187 | private void loadLayout() throws IOException {
188 |
189 |
190 | loadRootLayout();
191 | loadMainLayout();
192 | loadFinishDialog();
193 | loadPlusDialog();
194 | loadSettingDialog();
195 | loadEditDialog();
196 | loadStackedPanes();
197 | }
198 |
199 |
200 | public Main(TabPane settingDialog) {
201 | this.settingDialog = settingDialog;
202 | }
203 |
204 | private void loadSettingDialog() {
205 | try {
206 | FXMLLoader loader = new FXMLLoader();
207 | URL name = getClass().getResource("view/SettingDialog.fxml");
208 | loader.setLocation(name);
209 | settingDialog = loader.load();
210 | settingDialogController = loader.getController();
211 | } catch (IOException e) {
212 | GL.logger.debug("load SettingDialog fail.",e);
213 | }
214 | }
215 |
216 | private void loadEditDialog() {
217 | try {
218 | FXMLLoader loader = new FXMLLoader();
219 | URL name = getClass().getResource("view/EditDialog.fxml");
220 | loader.setLocation(name);
221 | editDialog = loader.load();
222 | editDialogController = loader.getController();
223 | } catch (IOException e) {
224 | GL.logger.warn(getClass().getSimpleName(),e);
225 | }
226 | }
227 |
228 | private void initPrimaryStage() {
229 | primaryStage.setTitle("MyTomato");
230 | Scene primaryStageScene = new Scene(getRootLayout());
231 | primaryStageScene.getStylesheets().add(getClass().getResource("view/stylesheet.css").toExternalForm());
232 | primaryStage.setScene(primaryStageScene);
233 | primaryStage.setMinWidth(PRIMARY_STAGE_MIN_WIDTH);
234 | primaryStage.getIcons().add(tomatoImage);
235 | setPrimaryStageListener();
236 | }
237 |
238 | private void setPrimaryStageListener() {
239 | primaryStage.focusedProperty().addListener(new ChangeListener() {
240 | @Override
241 | public void changed(ObservableValue extends Boolean> isFocused, Boolean onHidden, Boolean onShown) {
242 | if (isFocused.getValue()) {
243 | mainLayoutController.initHeadText();
244 | }
245 | }
246 | });
247 |
248 | primaryStage.setOnCloseRequest(evt -> {
249 | // prevent window from closing
250 | evt.consume();
251 | // execute own shutdown procedure
252 | boolean isClosed = onStageShutdown(primaryStage);
253 | if (isClosed)
254 | System.exit(0);
255 | });
256 | }
257 |
258 | private void loadFinishDialog() {
259 | try {
260 | FXMLLoader loader = new FXMLLoader();
261 | URL name = getClass().getResource("view/FinishDialog.fxml");
262 | loader.setLocation(name);
263 | finishDialog = loader.load();
264 | finishDialogController = loader.getController();
265 | } catch (IOException e) {
266 | GL.logger.warn(getClass().getSimpleName(),e);
267 | }
268 | }
269 |
270 | public StackedPanesController getStackedPanesController() {
271 | return stackedPanesController;
272 | }
273 |
274 | public void setStackedPanesController(StackedPanesController stackedPanesController) {
275 | this.stackedPanesController = stackedPanesController;
276 | }
277 |
278 | public StackedPanes getStackedPanes() {
279 | return stackedPanes;
280 | }
281 |
282 | private void loadStackedPanes() {
283 | stackedPanesController = new StackedPanesController();
284 | stackedPanes = stackedPanesController.createScrollPane();
285 | }
286 |
287 | public Stage getPlusDialogStage() {
288 | return plusDialogStage;
289 | }
290 |
291 | public void setPlusDialogStage(Stage plusDialogStage) {
292 | this.plusDialogStage = plusDialogStage;
293 | }
294 |
295 | public AnchorPane getPlusDialog() {
296 | return plusDialog;
297 | }
298 |
299 | public void setPlusDialog(AnchorPane plusDialog) {
300 | this.plusDialog = plusDialog;
301 | }
302 |
303 | public PlusDialogController getPlusDialogController() {
304 | return plusDialogController;
305 | }
306 |
307 | public void setPlusDialogController(PlusDialogController plusDialogController) {
308 | this.plusDialogController = plusDialogController;
309 | }
310 |
311 | private void loadPlusDialog() {
312 | try {
313 | FXMLLoader loader = new FXMLLoader();
314 | URL name = getClass().getResource("view/PlusDialog.fxml");
315 | loader.setLocation(name);
316 | plusDialog = loader.load();
317 | plusDialogController = loader.getController();
318 | } catch (IOException e) {
319 | GL.logger.warn(getClass().getSimpleName(),e);
320 | }
321 | }
322 |
323 | public Stage getFinishDialogStage() {
324 | return finishDialogStage;
325 | }
326 |
327 | public void setFinishDialogStage(Stage finishDialogStage) {
328 | this.finishDialogStage = finishDialogStage;
329 | }
330 |
331 | public AnchorPane getEditDialog() {
332 | return editDialog;
333 | }
334 |
335 | public void setEditDialog(AnchorPane editDialog) {
336 | this.editDialog = editDialog;
337 | }
338 |
339 | public EditDialogControl getEditDialogController() {
340 | return editDialogController;
341 | }
342 |
343 | public void setEditDialogController(EditDialogControl editDialogController) {
344 | this.editDialogController = editDialogController;
345 | }
346 |
347 | public Stage getEditDialogStage() {
348 | return editDialogStage;
349 | }
350 |
351 | public void setEditDialogStage(Stage editDialogStage) {
352 | this.editDialogStage = editDialogStage;
353 | }
354 |
355 | public void startEditDialogAndWait(String Title) {
356 | if (editDialogStage == null) {
357 | editDialogStage = new Stage();
358 | editDialogStage.initOwner(primaryStage);
359 | editDialogStage.setTitle(Title);
360 | editDialogStage.getIcons().add(tomatoImage);
361 | editDialogStage.setScene(new Scene(editDialog));
362 | editDialogStage.setResizable(false);
363 | editDialogStage.setOnCloseRequest(evt -> {
364 | Platform.runLater(() -> {
365 | getMainLayoutController().getAddButton().setDisable(false);
366 | });
367 | });
368 | }
369 | Platform.runLater(() -> {
370 | getMainLayoutController().getAddButton().setDisable(true);
371 | });
372 |
373 | editDialogStage.showAndWait();
374 | }
375 |
376 | public void startFinishDialogAndWait() {
377 | if (finishDialogStage == null) {
378 | finishDialogStage = new Stage();
379 | finishDialogStage.setAlwaysOnTop(true);
380 | finishDialogStage.initOwner(primaryStage);
381 | finishDialogStage.setTitle("请输入刚刚完成的任务");
382 | finishDialogStage.getIcons().add(tomatoImage);
383 | finishDialogStage.setScene(new Scene(finishDialog));
384 | finishDialogStage.setResizable(false);
385 |
386 | finishDialogStage.setOnHiding(new EventHandler() {
387 | @Override
388 | public void handle(WindowEvent event) {
389 | Platform.runLater(new Runnable() {
390 | @Override
391 | public void run() {
392 | getFinishDialogController().getTextField().setText("");
393 | getPlusDialogController().getTextField().setText("");
394 |
395 | }
396 | });
397 | }
398 | });
399 |
400 | finishDialogStage.setOnCloseRequest(evt -> {
401 | evt.consume();
402 | onStageShutdown(finishDialogStage);
403 | });
404 | }
405 | finishDialogStage.showAndWait();
406 | }
407 |
408 | public void startPlusDialogAndWait() {
409 | if (plusDialogStage == null) {
410 | plusDialogStage = new Stage();
411 | plusDialogStage.setAlwaysOnTop(true);
412 | plusDialogStage.initOwner(primaryStage);
413 | plusDialogStage.setTitle("请输入要提前添加的任务:");
414 | plusDialogStage.getIcons().add(tomatoImage);
415 | plusDialogStage.setScene(new Scene(plusDialog));
416 | plusDialogStage.setResizable(false);
417 | }
418 | plusDialogStage.showAndWait();
419 | }
420 |
421 | public void startSettingDialogAndWait() {
422 | if (startSettingStage == null) {
423 | startSettingStage = new Stage();
424 | startSettingStage.setAlwaysOnTop(true);
425 | startSettingStage.setTitle("SettingDialog");
426 | startSettingStage.getIcons().add(tomatoImage);
427 | startSettingStage.setScene(new Scene(settingDialog));
428 | startSettingStage.setResizable(false);
429 | }
430 | startSettingStage.showAndWait();
431 | }
432 |
433 | private boolean onStageShutdown(Stage mainWindow) {
434 | // you could also use your logout window / whatever here instead
435 | Alert alert = new OnTopAlert(Alert.AlertType.NONE, "Really close the Windows?", ButtonType.YES, ButtonType.NO);
436 | alert.initOwner(mainWindow);
437 | if (alert.showAndWait().orElse(ButtonType.NO) == ButtonType.YES) {
438 | // you may need to close other windows or replace this with Platform.exit();
439 | mainWindow.close();
440 | return true;
441 | } else
442 | return false;
443 | }
444 |
445 |
446 | public Main() {
447 |
448 | }
449 |
450 |
451 | private void intiTomatoTaskData() {
452 | tomatoTaskDataMapJson = new MapJson(TOMATO_TASKS_MAP, JSON_FILE);
453 | tomatoTaskDataMapJson.read();
454 |
455 | setTomatoTaskDataListener();
456 | setRedoTomatoTaskDataListener();
457 | }
458 |
459 | private void setRedoTomatoTaskDataListener() {
460 | REDO_TOMATO_TASKS.addListener((ListChangeListener.Change extends TomatoTask> change) -> {
461 | if (change.next()) {
462 | if (REDO_TOMATO_TASKS.isEmpty()) {
463 | mainLayoutController.closeRedoBar();
464 | } else {
465 | mainLayoutController.showRedoBarAndSleep();
466 | }
467 | }
468 |
469 | });
470 | }
471 |
472 | public ObservableList getREDO_TOMATO_TASKS() {
473 | return REDO_TOMATO_TASKS;
474 | }
475 |
476 | public void setREDO_TOMATO_TASKS(ObservableList REDO_TOMATO_TASKS) {
477 | this.REDO_TOMATO_TASKS = REDO_TOMATO_TASKS;
478 | }
479 |
480 | private void setTomatoTaskDataListener() {
481 | getStackedPanes().titledPaneItemsChangeProperty().addListener((observable, oldChange, newChange) -> {
482 |
483 | if (newChange != null) {
484 | if (newChange.next()) {
485 | List removedItems = newChange.getRemoved();
486 | List addedSubList = newChange.getAddedSubList();
487 | if (!removedItems.isEmpty()) {
488 | REDO_TOMATO_TASKS.clear();
489 | REDO_TOMATO_TASKS.addAll(removedItems);
490 | }
491 |
492 | newChange.reset();
493 | }
494 | }
495 |
496 | });
497 |
498 | }
499 |
500 |
501 | private void loadMainLayout() throws IOException {
502 | FXMLLoader loader = new FXMLLoader();
503 | URL name = getClass().getResource("view/MainLayout.fxml");
504 | loader.setLocation(name);
505 | mainLayout = loader.load();
506 | mainLayoutController = loader.getController();
507 | }
508 |
509 | private void loadRootLayout() throws IOException {
510 | FXMLLoader loader = new FXMLLoader();
511 | loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
512 | rootLayout = loader.load();
513 | rootLayoutController = loader.getController();
514 | }
515 |
516 | public static void main(String[] args) {
517 | setDefaultUncaughtExceptionHandler();
518 | launch(args);
519 | }
520 |
521 | private static void setDefaultUncaughtExceptionHandler() {
522 | GL.logger.info("setDefaultUncaughtExceptionHandler!");
523 | Thread.setDefaultUncaughtExceptionHandler((Thread t, Throwable e) -> {
524 | GL.logger.warn("UncaughtException",e);
525 | BriefReport.formatErrorAlert(e);
526 | });
527 | }
528 |
529 |
530 | }
531 |
--------------------------------------------------------------------------------
/src/app/control/GirdColumn.java:
--------------------------------------------------------------------------------
1 | package app.control;
2 |
3 | import javafx.scene.Node;
4 |
5 | public class GirdColumn {
6 | private final String name;
7 | private GirdColumnFactory nodeFactory;
8 |
9 | public GirdColumnFactory getNodeFactory() {
10 | return nodeFactory;
11 | }
12 |
13 | public void setNodeFactory(GirdColumnFactory nodeFactory) {
14 | this.nodeFactory = nodeFactory;
15 | }
16 |
17 | public GirdColumn(String name) {
18 | this.name = name;
19 | }
20 | }
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/app/control/GirdColumnFactory.java:
--------------------------------------------------------------------------------
1 | package app.control;
2 |
3 | import javafx.scene.Node;
4 |
5 | @FunctionalInterface
6 | public interface GirdColumnFactory{
7 | Node generateNode(DataType data);
8 | }
9 |
--------------------------------------------------------------------------------
/src/app/control/GridPane.java:
--------------------------------------------------------------------------------
1 | package app.control;
2 |
3 | import javafx.collections.FXCollections;
4 | import javafx.collections.ObservableList;
5 | import javafx.scene.Node;
6 |
7 | public class GridPane extends javafx.scene.layout.GridPane {
8 | protected ObservableList items;
9 |
10 | private ObservableList> columns = FXCollections.observableArrayList();
11 |
12 | public void setItems(ObservableList items) {
13 | this.items = items;
14 | generateColumns();
15 | }
16 |
17 | protected void generateColumns() {
18 | clearGrid();
19 | int columnIndex = 0;
20 | for (GirdColumn girdColumn : columns) {
21 | int itemIndex = 0;
22 | for (DataType item : items) {
23 | Node node = girdColumn.getNodeFactory().generateNode(item);
24 | this.add(node,columnIndex,itemIndex);
25 | itemIndex++;
26 | }
27 | columnIndex++;
28 | }
29 | }
30 |
31 | private void clearGrid() {
32 | this.getChildren().clear();
33 | }
34 |
35 | public GridPane() {
36 | }
37 |
38 | public ObservableList> getColumns() {
39 | return columns;
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/control/OnTopAlert.java:
--------------------------------------------------------------------------------
1 | package app.control;
2 |
3 | import javafx.scene.control.Alert;
4 | import javafx.scene.control.ButtonType;
5 | import javafx.stage.Stage;
6 |
7 | public class OnTopAlert extends Alert {
8 | public OnTopAlert(AlertType alertType) {
9 | super(alertType);
10 | setAlertAlwaysOnTop(this);
11 | }
12 |
13 | public OnTopAlert(AlertType alertType, String contentText, ButtonType... buttons) {
14 | super(alertType, contentText, buttons);
15 | setAlertAlwaysOnTop(this);
16 | }
17 |
18 | public static Stage getAlertStage(Alert alert) {
19 | return (Stage) alert.getDialogPane().getScene().getWindow();
20 | }
21 |
22 | public static void setAlertAlwaysOnTop(Alert alert) {
23 | getAlertStage(alert).setAlwaysOnTop(true);
24 | getAlertStage(alert).toFront();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/control/StackedPanes.java:
--------------------------------------------------------------------------------
1 | package app.control;
2 |
3 | import app.util.GL;
4 | import javafx.beans.value.ChangeListener;
5 | import javafx.beans.value.ObservableValue;
6 | import javafx.geometry.Bounds;
7 | import javafx.scene.layout.VBox;
8 |
9 | public abstract class StackedPanes extends javafx.scene.control.ScrollPane{
10 |
11 | //--------------------------------------- Field
12 | protected VBox vBox = new VBox();
13 | //--------------------------------------- GS
14 |
15 | public StackedPanes() {
16 | this.setContent(vBox);
17 | this.viewportBoundsProperty().addListener((ov, oldBounds, bounds) -> vBox.setPrefWidth(bounds.getWidth()));
18 | }
19 |
20 | //--------------------------------------- Method
21 |
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/control/TextWrapCell.java:
--------------------------------------------------------------------------------
1 | package app.control;
2 |
3 | import javafx.application.Platform;
4 | import javafx.scene.control.TableCell;
5 | import javafx.scene.text.Text;
6 |
7 | public class TextWrapCell extends TableCell {
8 | public int instanceCounter = 0;
9 | public TextWrapCell() {
10 | instanceCounter++;
11 | }
12 |
13 | private Text textControl = null;
14 | public static final int CELL_TEXT_PAD = 20;
15 |
16 | private void wrap() {
17 | textControl.setWrappingWidth(getTableColumn().getWidth() - CELL_TEXT_PAD);
18 | }
19 |
20 | @Override
21 | public void updateItem(String item, boolean empty) {
22 | super.updateItem(item, empty);
23 | if (!isEmpty()) {
24 | if (textControl == null) {
25 | this.setWrapText(true);
26 | textControl = new Text(item);
27 | setGraphic(textControl);
28 | wrap();
29 | getTableColumn().widthProperty().addListener((observable, oldValue, newValue) -> wrap());
30 | } else {
31 | textControl.setText(item);
32 | }
33 | }
34 |
35 |
36 | }
37 | }
--------------------------------------------------------------------------------
/src/app/control/mytomato/GridPane.java:
--------------------------------------------------------------------------------
1 | package app.control.mytomato;
2 |
3 | import app.Main;
4 | import app.control.GirdColumn;
5 | import app.model.TomatoTask;
6 | import javafx.application.Platform;
7 | import javafx.collections.ListChangeListener;
8 | import javafx.collections.ObservableList;
9 | import javafx.scene.Parent;
10 | import javafx.scene.control.Button;
11 | import javafx.scene.image.Image;
12 | import javafx.scene.image.ImageView;
13 | import javafx.scene.text.Text;
14 |
15 |
16 | public class GridPane extends app.control.GridPane {
17 |
18 | public static final int OTHER_CONSTRAINT_WIDTH = 170;
19 | private GirdColumn nameColumn;
20 | private GirdColumn startColumn;
21 | private GirdColumn endColumn;
22 | private GirdColumn deleteColumn;
23 | private GirdColumn editColumn;
24 |
25 | public void setItems(ObservableList items) {
26 |
27 | startColumn = new GirdColumn<>("start");
28 | startColumn.setNodeFactory(data -> new Text(data.getStartTimeString()));
29 | this.getColumns().add(startColumn);
30 |
31 | endColumn = new GirdColumn<>("end");
32 | endColumn.setNodeFactory(data -> new Text(data.getEndTimeString()));
33 | this.getColumns().add(endColumn);
34 |
35 | nameColumn = new GirdColumn<>("name");
36 | nameColumn.setNodeFactory(data -> {
37 | Text text = new Text(data.getName());
38 | text.setWrappingWidth(450);
39 | // System.out.println("getWidth():" + this.getWidth());
40 | // 可以发现出现很多的 getWidth():0.0,这个意味着该getWidth()的功能已经失效?不管怎么样,设置固定宽度吧,还可能继续用一阵
41 | // text.setWrappingWidth(getWidth() - OTHER_CONSTRAINT_WIDTH);
42 | // widthProperty().addListener((observable, oldValue, newValue) -> Platform.runLater(() -> text.setWrappingWidth((Double) newValue - OTHER_CONSTRAINT_WIDTH)));
43 |
44 | return text;
45 | });
46 | this.getColumns().add(nameColumn);
47 |
48 | deleteColumn = new GirdColumn<>("delete");
49 | deleteColumn.setNodeFactory(data -> {
50 | Button deleteButton = new Button();
51 | deleteButton.getStyleClass().add("delete-button");
52 | deleteButton.setOnAction(event -> items.remove(data));
53 | return deleteButton;
54 | });
55 | this.getColumns().add(deleteColumn);
56 |
57 | editColumn = new GirdColumn<>("edit");
58 | editColumn.setNodeFactory(data -> {
59 | Button editButton = new Button();
60 | editButton.getStyleClass().add("edit-button");
61 | editButton.setOnAction(event -> {
62 | getStackedPanes().setEditingGridPane(this);
63 | getStackedPanes().getStackedPanesController().main.getEditDialogController().loadSpecifiedTask(data);
64 | getStackedPanes().getStackedPanesController().main.startEditDialogAndWait("修改任务");
65 | });
66 | return editButton;
67 | });
68 |
69 | this.getColumns().add(editColumn);
70 |
71 | super.setItems(items);
72 |
73 | addListenerToItems();
74 |
75 | }
76 |
77 | private StackedPanes getStackedPanes() {
78 | Parent parent = this.getParent();
79 | for (int i = 0;i) c -> {
91 | c.next();
92 | generateColumns();
93 | });
94 | }
95 |
96 | public void refresh() {
97 | generateColumns();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/app/control/mytomato/StackedPanes.java:
--------------------------------------------------------------------------------
1 | package app.control.mytomato;
2 |
3 | import app.model.TomatoTask;
4 | import app.view.StackedPanesController;
5 | import javafx.beans.property.SimpleObjectProperty;
6 | import javafx.collections.*;
7 | import javafx.scene.Node;
8 | import java.time.LocalDate;
9 | import java.util.ArrayList;
10 | import java.util.Collections;
11 | import java.util.Comparator;
12 | import java.util.List;
13 |
14 | public class StackedPanes extends app.control.StackedPanes {
15 |
16 | private GridPane focusedGridPane;
17 |
18 | public StackedPanes() {
19 | super();
20 | vBox.getStyleClass().add("v-box");
21 | }
22 |
23 | //--------------------------------------- Field+
24 | StackedPanesController stackedPanesController;
25 |
26 | public StackedPanesController getStackedPanesController() {
27 | return stackedPanesController;
28 | }
29 |
30 | public void setStackedPanesController(StackedPanesController stackedPanesController) {
31 | this.stackedPanesController = stackedPanesController;
32 | }
33 |
34 | private SimpleObjectProperty> titledPaneItemsChange = new SimpleObjectProperty();
35 |
36 | protected ObservableMap> itemsMap = null;
37 | protected ObservableList> itemsList = FXCollections.observableArrayList();
38 | private Comparator super Node> comparatorTitledPane = new Comparator() {
39 | @Override
40 | public int compare(Node o1, Node o2) {
41 | TitledPane t1 = (TitledPane) o1;
42 | TitledPane t2 = (TitledPane) o2;
43 | int result = t1.getLOCAL_DATE().compareTo(t2.getLOCAL_DATE());
44 | return result;
45 | }
46 | };
47 |
48 |
49 | private Comparator> comparatorTomatoTaskList = (o1, o2) -> {
50 | boolean o1IsEmpty = o1.isEmpty();
51 | boolean o2IsEmpty = o2.isEmpty();
52 | if ((o1IsEmpty) && (o2IsEmpty)) {
53 | return 0;
54 | } else if (o1IsEmpty) {
55 | return -1;
56 | } else if (o2IsEmpty) {
57 | return 1;
58 | }
59 | return (o1.get(0).getDate().compareTo(o2.get(0).getDate()));
60 | };
61 |
62 | //--------------------------------------- GS
63 |
64 |
65 | public ListChangeListener.Change extends TomatoTask> getTitledPaneItemsChange() {
66 | return titledPaneItemsChange.get();
67 | }
68 |
69 | public SimpleObjectProperty> titledPaneItemsChangeProperty() {
70 | return titledPaneItemsChange;
71 | }
72 |
73 | public void setTitledPaneItemsChange(ListChangeListener.Change extends TomatoTask> titledPaneItemsChange) {
74 | this.titledPaneItemsChange.set(titledPaneItemsChange);
75 | }
76 |
77 | public ObservableMap> getItemsMap() {
78 | return itemsMap;
79 | }
80 |
81 | //--------------------------------------- Method
82 |
83 | public void setItemsMap(ObservableMap> itemsMap) {
84 | this.itemsMap = itemsMap;
85 | convertItemsMapToSortedList();
86 | showItemsList();
87 | itemsMap.addListener((MapChangeListener>) change -> {
88 | ObservableList addList = change.getValueAdded();
89 | if (!addList.isEmpty()) {
90 | addTitledPane(addList);
91 | }
92 | });
93 | }
94 |
95 | private void addTitledPane(ObservableList addList) {
96 | if (!addList.isEmpty()) {
97 |
98 | TitledPane titledPane = creatTitledPane(addList);
99 |
100 | List list = new ArrayList(vBox.getChildren());
101 | list.add(titledPane);
102 | list.sort(comparatorTitledPane);
103 | Collections.reverse(list);
104 | vBox.getChildren().clear();
105 | vBox.getChildren().addAll(list);
106 |
107 | }
108 | }
109 |
110 |
111 | public void removeTitledPane(TitledPane titledPane) {
112 | vBox.getChildren().remove(titledPane);
113 | }
114 |
115 |
116 | private void showItemsList() {
117 | this.itemsList.forEach((list) -> {
118 | if (!list.isEmpty()) {
119 | TitledPane titledPane = creatTitledPane(list);
120 | vBox.getChildren().add(titledPane);
121 | }
122 | });
123 | }
124 |
125 | private TitledPane creatTitledPane(ObservableList list) {
126 | TitledPane titledPane = new TitledPane(list.get(0).getDate());
127 | titledPane.setItems(list);
128 | list.addListener((ListChangeListener) change -> {
129 |
130 | if (change.next()) {
131 | List removedItems = change.getRemoved();
132 | List addedSubList = change.getAddedSubList();
133 | boolean sortAble = (!addedSubList.isEmpty()) | (!removedItems.isEmpty());
134 | if(sortAble){
135 | }
136 | change.reset();
137 | }
138 | if (list.isEmpty()) {
139 | removeTitledPane(titledPane);
140 | }
141 |
142 | setTitledPaneItemsChange(change);
143 |
144 | });
145 |
146 |
147 | return titledPane;
148 | }
149 |
150 |
151 | private void convertItemsMapToSortedList() {
152 | this.itemsMap.forEach((localDate, list) -> {
153 | itemsList.add(list);
154 | itemsList.sort(comparatorTomatoTaskList);
155 | });
156 | Collections.reverse(itemsList);
157 | }
158 |
159 |
160 | public void addItems(TomatoTask... tomatoTasks) {
161 | for (int i = 0; i < tomatoTasks.length; i++) {
162 | addItem(tomatoTasks[i]);
163 | }
164 | }
165 | public void addItems(List items) {
166 | for (int i = 0; i < items.size(); i++) {
167 | addItem(items.get(i));
168 | }
169 | }
170 |
171 | private void addItem(TomatoTask tomatoTask) {
172 | ObservableList list = itemsMap.get(tomatoTask.getDate());
173 | if (list == null||list.isEmpty()) {
174 | ObservableList newList = FXCollections.observableArrayList(tomatoTask);
175 | itemsMap.put(tomatoTask.getDate(), newList);
176 | } else {
177 | list.add(tomatoTask);
178 | }
179 | }
180 |
181 | public GridPane getFocusedGridPane() {
182 | return focusedGridPane;
183 | }
184 |
185 | public void setEditingGridPane(GridPane focusedGridPane) {
186 | this.focusedGridPane = focusedGridPane;
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/app/control/mytomato/TitledPane.java:
--------------------------------------------------------------------------------
1 | package app.control.mytomato;
2 |
3 | import app.model.TomatoTask;
4 | import javafx.collections.ObservableList;
5 | import javafx.geometry.Insets;
6 | import javafx.scene.layout.AnchorPane;
7 |
8 | import java.time.LocalDate;
9 |
10 | public class TitledPane extends javafx.scene.control.TitledPane {
11 |
12 | //--------------------------------------- Field
13 |
14 | private final LocalDate LOCAL_DATE;
15 |
16 | public static final double TABLE_VIEW_MARGIN = 0;
17 | public static final int TABLE_VIEW_PADDING = 0;
18 |
19 | private AnchorPane anchorPane = new AnchorPane();
20 | private GridPane gridPane = new GridPane();
21 |
22 | private ObservableList items = null;
23 | //--------------------------------------- GS
24 |
25 | public void setItems(ObservableList list) {
26 | this.items = list;
27 | setAnchorPane();
28 | gridPane.setItems(list);
29 | gridPane.getStyleClass().add("grid-pane");
30 | this.setContent(anchorPane);
31 | }
32 |
33 | public LocalDate getLOCAL_DATE() {
34 | return LOCAL_DATE;
35 | }
36 |
37 | //--------------------------------------- Method
38 |
39 | public TitledPane(LocalDate localDate) {
40 | this.getStyleClass().add("title-pane");
41 | this.LOCAL_DATE = localDate;
42 | setTitle();
43 | }
44 |
45 | private void setTitle() {
46 | setText(LOCAL_DATE.toString());
47 | }
48 |
49 | private void setAnchorPane() {
50 | setGridView();
51 | anchorPane.getChildren().add(gridPane);
52 | anchorPane.setPadding(new Insets(TABLE_VIEW_PADDING));
53 | }
54 |
55 | private void setGridView() {
56 | AnchorPane.setBottomAnchor(gridPane, TABLE_VIEW_MARGIN);
57 | AnchorPane.setLeftAnchor(gridPane, TABLE_VIEW_MARGIN);
58 | AnchorPane.setRightAnchor(gridPane, TABLE_VIEW_MARGIN);
59 | AnchorPane.setTopAnchor(gridPane, TABLE_VIEW_MARGIN);
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/app/model/TomatoTask.java:
--------------------------------------------------------------------------------
1 | package app.model;
2 |
3 | import app.util.CountDown;
4 | import javafx.beans.property.ObjectProperty;
5 | import javafx.beans.property.SimpleObjectProperty;
6 | import javafx.beans.property.SimpleStringProperty;
7 | import javafx.beans.property.StringProperty;
8 |
9 | import java.time.LocalDate;
10 | import java.time.LocalTime;
11 |
12 | import static app.util.DateAndTime.localDateToString;
13 | import static app.util.DateAndTime.localTimeToString;
14 |
15 | public class TomatoTask {
16 |
17 |
18 | private StringProperty name;
19 | private ObjectProperty startTime = new SimpleObjectProperty<>();
20 | private ObjectProperty endTime = new SimpleObjectProperty<>();
21 | private ObjectProperty date = new SimpleObjectProperty<>();
22 | private StringProperty startTimeString;
23 | private StringProperty endTimeString;
24 | private StringProperty dateString;
25 |
26 |
27 | public LocalDate getDate() {
28 | return date.get();
29 | }
30 |
31 | public ObjectProperty dateProperty() {
32 | return date;
33 | }
34 |
35 | public void setDate(LocalDate date) {
36 | this.date.set(date);
37 | }
38 |
39 | public String getDateString() {
40 | return dateString.get();
41 | }
42 |
43 | public StringProperty dateStringProperty() {
44 | return dateString;
45 | }
46 |
47 | public void setDateString(String dateString) {
48 | this.dateString.set(dateString);
49 | }
50 |
51 | public LocalTime getEndTime() {
52 | return endTime.get();
53 | }
54 |
55 | public ObjectProperty endTimeProperty() {
56 | return endTime;
57 | }
58 |
59 | public void setEndTime(LocalTime endTime) {
60 | this.endTime.set(endTime);
61 | }
62 |
63 |
64 | public TomatoTask(String name, CountDown countDown) {
65 | initTomatoTask(name, countDown.getStartTime(), countDown.getEndTime());
66 | }
67 |
68 | public TomatoTask(String name, LocalTime startTime, LocalTime endTime) {
69 | initTomatoTask(name, startTime, endTime);
70 | }
71 | public TomatoTask(String name, LocalTime startTime, LocalTime endTime,LocalDate date) {
72 | initTomatoTask(name, startTime, endTime, date);
73 | }
74 |
75 | public void initTomatoTask(String name, LocalTime startTime, LocalTime endTime) {
76 | this.name = new SimpleStringProperty(name);
77 |
78 | addLockTimeListener();
79 | setEndTime(endTime);
80 | setStartTime(startTime);
81 | setDate(LocalDate.now());
82 | }
83 |
84 | public void initTomatoTask(String name, LocalTime startTime, LocalTime endTime,LocalDate date) {
85 | this.name = new SimpleStringProperty(name);
86 |
87 | addLockTimeListener();
88 | setEndTime(endTime);
89 | setStartTime(startTime);
90 | setDate(date);
91 | }
92 |
93 | private void addLockTimeListener() {
94 | startTime.addListener((observable, oldValue, newValue) -> {
95 | LocalTime startTime = newValue;
96 | String startTimeString = localTimeToString(startTime);
97 | this.startTimeString = new SimpleStringProperty(startTimeString);
98 | });
99 | endTime.addListener((observable, oldValue, newValue) -> {
100 | LocalTime endTime = newValue;
101 | String endTimeString = localTimeToString(endTime);
102 | this.endTimeString = new SimpleStringProperty(endTimeString);
103 | });
104 |
105 | date.addListener((observable, oldValue, newValue) -> {
106 | LocalDate date = newValue;
107 | String dateString = localDateToString(date);
108 | this.dateString = new SimpleStringProperty(dateString);
109 | });
110 | }
111 |
112 | @Override
113 | public String toString() {
114 | return "TomatoTask{" +
115 | "name=" + name +
116 | ", startTime=" + startTime +
117 | ", endTime=" + endTime +
118 | ", date=" + date +
119 | '}';
120 | }
121 |
122 | public TomatoTask(String name, LocalTime endTime) {
123 | LocalTime startTime = LocalTime.now();
124 | initTomatoTask(name, startTime, endTime);
125 | }
126 |
127 | public String getEndTimeString() {
128 | return endTimeString.get();
129 | }
130 |
131 | public StringProperty endTimeStringProperty() {
132 | return endTimeString;
133 | }
134 |
135 | public void setEndTimeString(String endTimeString) {
136 | this.endTimeString.set(endTimeString);
137 | }
138 |
139 | public String getName() {
140 | return name.get();
141 | }
142 |
143 | public StringProperty nameProperty() {
144 | return name;
145 | }
146 |
147 | public void setName(String name) {
148 | this.name.set(name);
149 | }
150 |
151 | public LocalTime getStartTime() {
152 | return startTime.get();
153 | }
154 |
155 | public ObjectProperty startTimeProperty() {
156 | return startTime;
157 | }
158 |
159 | public void setStartTime(LocalTime startTime) {
160 | this.startTime.set(startTime);
161 | }
162 |
163 | public String getStartTimeString() {
164 | return startTimeString.get();
165 | }
166 |
167 | public StringProperty startTimeStringProperty() {
168 | return startTimeString;
169 | }
170 |
171 | public void setStartTimeString(String startTimeString) {
172 | this.startTimeString.set(startTimeString);
173 | }
174 |
175 | }
176 |
--------------------------------------------------------------------------------
/src/app/util/BriefReport.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import app.control.OnTopAlert;
4 | import javafx.scene.control.Alert;
5 |
6 | import java.util.ArrayList;
7 |
8 | public class BriefReport {
9 |
10 | public static void formatErrorAlert(Throwable ex) {
11 | String contentText = getBriefReport(ex);
12 | Alert alert = new OnTopAlert(Alert.AlertType.WARNING, contentText);
13 | alert.showAndWait();
14 | }
15 |
16 | private static String getBriefReport(Throwable ex) {
17 | ArrayList causes = getCauses(ex);
18 | String briefReport = ex.getMessage() + "\r\n";
19 | for (Throwable throwable : causes) {
20 | briefReport += "Caused by: " + throwable.toString() + "\r\n";
21 | }
22 | return briefReport;
23 | }
24 |
25 | private static ArrayList getCauses(Throwable ex) {
26 | ArrayList list = new ArrayList<>();
27 | Throwable cause = ex;
28 | while (true) {
29 | cause = cause.getCause();
30 | if (cause != null)
31 | list.add(cause);
32 | else
33 | break;
34 | }
35 | return list;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/app/util/CountDown.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import javafx.beans.property.*;
4 |
5 | import java.time.Duration;
6 | import java.time.LocalTime;
7 | import java.util.Timer;
8 | import java.util.TimerTask;
9 | import java.util.concurrent.locks.ReentrantLock;
10 | import java.util.concurrent.locks.ReentrantReadWriteLock;
11 |
12 | public class CountDown {
13 |
14 | volatile private Duration duration;
15 | volatile private LocalTime startTime;
16 | volatile private LocalTime endTime;
17 | volatile private LocalTime currentTime;
18 | volatile private Duration sumDuration;
19 | volatile private Duration zeroDuration = Duration.ofSeconds(0);
20 | volatile private Duration currentDuration;
21 |
22 | volatile private SimpleBooleanProperty started = new SimpleBooleanProperty(false);
23 | volatile private SimpleBooleanProperty finished = new SimpleBooleanProperty(false);
24 |
25 | public static final int PERIOD_MSEC = 100;
26 | volatile private StringProperty textProgress = new SimpleStringProperty("");
27 | volatile private DoubleProperty barProgress = new SimpleDoubleProperty(0);
28 | volatile private static Timer time;
29 |
30 | public static final long TIMER_DELAY = 0;
31 | private ReentrantLock startOrStopLock = new ReentrantLock();
32 | private ReentrantLock startedLock = new ReentrantLock();
33 | private ReentrantLock finishedLock = new ReentrantLock();
34 |
35 | public StringProperty textProgressProperty() {
36 | return textProgress;
37 | }
38 |
39 | public void setTextProgress(String textProgress) {
40 | this.textProgress.set(textProgress);
41 | }
42 |
43 | public DoubleProperty barProgressProperty() {
44 | return barProgress;
45 | }
46 |
47 | public void setBarProgress(double barProgress) {
48 | this.barProgress.set(barProgress);
49 | }
50 |
51 | public CountDown(Duration duration) {
52 | this.duration = duration;
53 | }
54 |
55 |
56 | public SimpleBooleanProperty finishedProperty() {
57 | finishedLock.lock();
58 | try {
59 | return finished;
60 | } finally {
61 | finishedLock.unlock();
62 | }
63 | }
64 |
65 | public boolean getFinished() {
66 | finishedLock.lock();
67 | try {
68 | return finished.get();
69 | } finally {
70 | finishedLock.unlock();
71 | }
72 | }
73 |
74 | public void setFinished(boolean finished) {
75 | finishedLock.lock();
76 | try {
77 | this.finished.set(finished);
78 | }
79 | finally {
80 | finishedLock.unlock();
81 | }
82 | }
83 |
84 | public SimpleBooleanProperty startedProperty() {
85 | finishedLock.lock();
86 | try {
87 | return started;
88 | } finally {
89 | finishedLock.unlock();
90 | }
91 | }
92 |
93 | public void setStarted(boolean started) {
94 | startedLock.lock();
95 | try {
96 | this.started.set(started);
97 | } finally {
98 | startedLock.unlock();
99 | }
100 | }
101 |
102 | public void setDuration(Duration duration) {
103 | this.duration = duration;
104 | }
105 |
106 | public LocalTime getEndTime() {
107 | return endTime;
108 | }
109 |
110 | public boolean isStarted() {
111 | startedLock.lock();
112 | try {
113 | return started.get();
114 | } finally {
115 | startedLock.unlock();
116 | }
117 | }
118 |
119 | public void start() {
120 | startOrStopLock.lock();
121 | try {
122 | if (isStarted()) {
123 | throw new RuntimeException("Already started. Cannot be started again.");
124 | }
125 | setStarted(true);
126 | initProgressData();
127 | startTimer();
128 | setFinished(false);
129 | } finally {
130 | startOrStopLock.unlock();
131 | }
132 | }
133 |
134 | private void startTimer() {
135 | time = new Timer();
136 | time.schedule(new TimerTask() {
137 | @Override
138 | public void run() {
139 | updateProgressText();
140 | updateProgressBar();
141 | checkProgress();
142 | }
143 | }, TIMER_DELAY, PERIOD_MSEC);
144 | }
145 |
146 | public void cancel() {
147 | stop(false);
148 | }
149 |
150 |
151 | public void finish() {
152 | stop(true);
153 | }
154 |
155 | public void stop(boolean finished) {
156 | startOrStopLock.lock();
157 | try {
158 | if (!isStarted()) {
159 | throw new RuntimeException("Already stopped. Cannot be stopped again.");
160 | }
161 | setStarted(false);
162 | progressReturnToZero();
163 | time.cancel();
164 | setFinished(finished);
165 | } finally {
166 | startOrStopLock.unlock();
167 | }
168 | }
169 |
170 | private void checkProgress() {
171 | if (currentDuration.compareTo(zeroDuration) >= 0) {
172 | finish();
173 | }
174 | }
175 |
176 | public static Timer getTime() {
177 | return time;
178 | }
179 |
180 | public static void setTime(Timer time) {
181 | CountDown.time = time;
182 | }
183 |
184 | private void initProgressData() {
185 | currentTime = LocalTime.now();
186 | startTime = LocalTime.from(currentTime);
187 | endTime = LocalTime.from(currentTime).plus(duration);
188 | sumDuration = Duration.between(startTime, endTime);
189 | currentDuration = Duration.between(startTime, currentTime);
190 | }
191 |
192 | public static String formatDuration(Duration duration, Boolean alwaysPositive) {
193 | long seconds = duration.getSeconds();
194 | long absSeconds = Math.abs(seconds);
195 | String positive = String.format(
196 | "%d:%02d:%02d",
197 | absSeconds / 3600,
198 | (absSeconds % 3600) / 60,
199 | absSeconds % 60);
200 | return (seconds < 0) & (!(alwaysPositive)) ? "-" + positive : positive;
201 | }
202 |
203 | public LocalTime getStartTime() {
204 | return startTime;
205 | }
206 |
207 |
208 | private void progressReturnToZero() {
209 | setTextProgress("");
210 | setBarProgress(0);
211 | }
212 |
213 | private void updateProgressText() {
214 | currentTime = LocalTime.now();
215 | currentDuration = Duration.between(endTime, currentTime);
216 | setTextProgress(CountDown.formatDuration(currentDuration, true));
217 | }
218 |
219 |
220 | private void updateProgressBar() {
221 | long currentSec = Math.abs(currentDuration.toMillis());
222 | long sumSec = Math.abs(sumDuration.toMillis());
223 | double progress = (double) currentSec / (double) sumSec;
224 | setBarProgress(progress);
225 | }
226 |
227 |
228 | }
229 |
--------------------------------------------------------------------------------
/src/app/util/DataManager.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | public interface DataManager {
4 | String write();
5 | void read();
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/util/DateAndTime.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import java.time.LocalDate;
4 | import java.time.LocalTime;
5 | import java.time.format.DateTimeFormatter;
6 |
7 | public class DateAndTime {
8 | static String timeFormatterPattern = "HH:mm";
9 | static DateTimeFormatter timeFormatter =
10 | DateTimeFormatter.ofPattern(timeFormatterPattern);
11 | static String dateFormatterPattern = "MM/dd";
12 | static DateTimeFormatter dateFormatter =
13 | DateTimeFormatter.ofPattern(dateFormatterPattern);
14 |
15 | public static String localTimeToString(LocalTime localTime) {
16 | return localTime.format(timeFormatter);
17 | }
18 |
19 | public static String localDateToString(LocalDate localDate) {
20 | return localDate.format(dateFormatter);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/util/GL.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import org.apache.log4j.Logger;
4 | import org.apache.log4j.PropertyConfigurator;
5 |
6 |
7 | public class GL {
8 |
9 | static public Logger logger = Logger.getLogger(GL.class);
10 |
11 | static {
12 | String resFileAbsolutePath = ResGetter.getResFile().getAbsolutePath();
13 | PropertyConfigurator.configure(resFileAbsolutePath + "\\properties\\log4j.properties");
14 | GL.logger.info("ResFile:" + resFileAbsolutePath);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/util/ListJson.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import app.control.OnTopAlert;
4 | import app.model.TomatoTask;
5 | import com.alibaba.fastjson.JSON;
6 | import com.alibaba.fastjson.JSONObject;
7 | import javafx.scene.control.Alert;
8 |
9 | import java.io.*;
10 | import java.time.LocalDate;
11 | import java.time.LocalTime;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | public class ListJson implements DataManager {
16 |
17 | private final String EMPTY_MAP_STRING = "[]";
18 | private final File jsonFile;
19 | private List data;
20 |
21 | public List getData() {
22 | return data;
23 | }
24 |
25 | public ListJson(List data, File jsonFile) {
26 | GL.logger.info("JsonFile:" + jsonFile.getAbsolutePath());
27 | this.jsonFile = jsonFile;
28 | this.data = data;
29 | }
30 |
31 | public String readString() {
32 | char[] json = new char[(int) jsonFile.length()];
33 | String jsonString = "";
34 | try (FileReader fileReader = new FileReader(jsonFile)) {
35 | fileReader.read(json);
36 | jsonString = new String(json);
37 | } catch (FileNotFoundException e) {
38 | System.err.println("Not Found JSON ,set table empty!");
39 | jsonString = EMPTY_MAP_STRING;
40 | } catch (IOException e) {
41 | GL.logger.warn(getClass().getSimpleName(),e);
42 | } finally {
43 | return jsonString;
44 | }
45 | }
46 |
47 | public String write() {
48 | ArrayList tomatoTaskList = new ArrayList(data);
49 | String json = JSON.toJSONString(tomatoTaskList);
50 | try (FileWriter fileWriter = new FileWriter(jsonFile)) {
51 | fileWriter.write(json);
52 | } catch (IOException e) {
53 | GL.logger.warn(getClass().getSimpleName(),e);
54 | } finally {
55 | return json;
56 | }
57 | }
58 |
59 | public void read() {
60 | this.data.clear();
61 | String jsonString = readString();
62 | ArrayList jsonList = null;
63 | try {
64 | jsonList = JSON.parseObject(jsonString, ArrayList.class);
65 | jsonList.forEach(jsonMap -> mapToTask((JSONObject) jsonMap));
66 | } catch (com.alibaba.fastjson.JSONException e) {
67 | GL.logger.warn(getClass().getSimpleName(),e);
68 | Alert alert = new OnTopAlert(Alert.AlertType.WARNING, "JSON file parse exception.");
69 | alert.showAndWait();
70 | System.exit(1);
71 | }
72 | }
73 |
74 | private void mapToTask(JSONObject jsonMap) {
75 | String name = (String) jsonMap.get("name");
76 | LocalTime endTime = LocalTime.parse(((String) jsonMap.get("endTime")));
77 | LocalTime startTime = LocalTime.parse(((String) jsonMap.get("startTime")));
78 | LocalDate date = LocalDate.parse(((String) jsonMap.get("date")));
79 | data.add(new TomatoTask(name, startTime, endTime, date));
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/app/util/MapJson.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import app.model.TomatoTask;
4 | import javafx.collections.FXCollections;
5 | import javafx.collections.ObservableList;
6 | import javafx.collections.ObservableMap;
7 |
8 | import java.io.File;
9 | import java.time.LocalDate;
10 |
11 | public class MapJson implements DataManager {
12 |
13 | private final ObservableList listData = FXCollections.observableArrayList();
14 | private final ObservableMap> mapData;
15 |
16 | private final File jsonFile;
17 |
18 | private final ListJson listJson;
19 |
20 | public MapJson(ObservableMap> mapData, File jsonFile) {
21 | this.mapData = mapData;
22 | this.jsonFile = jsonFile;
23 | this.listJson = new ListJson(listData, jsonFile);
24 | }
25 |
26 | @Override
27 | public String write() {
28 | convertToList();
29 | return listJson.write();
30 | }
31 |
32 | @Override
33 | public void read() {
34 | listJson.read();
35 | convertToMap();
36 | }
37 |
38 | private void convertToMap() {
39 | listData.forEach((e) -> {
40 | if (mapData.get(e.getDate()) == null) {
41 | mapData.put(e.getDate(), FXCollections.observableArrayList());
42 | }
43 | mapData.get(e.getDate()).add(e);
44 | });
45 | }
46 |
47 | private void convertToList() {
48 | listData.clear();
49 | mapData.forEach((k, v) -> {
50 | listData.addAll(v);
51 | });
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/app/util/Mp3Player.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import javazoom.jl.decoder.JavaLayerException;
4 | import javazoom.jl.player.Player;
5 |
6 | import java.io.File;
7 | import java.io.FileInputStream;
8 | import java.io.FileNotFoundException;
9 | import java.util.concurrent.atomic.AtomicBoolean;
10 |
11 | public class Mp3Player {
12 |
13 | private File file;
14 | private FileInputStream fileInputStream;
15 | private Player player;
16 | private AtomicBoolean repeated = new AtomicBoolean(false);
17 |
18 | public Mp3Player(File file) {
19 | this.file = file;
20 | try {
21 | this.fileInputStream = new FileInputStream(this.file);
22 | player = new Player(fileInputStream);
23 | } catch (FileNotFoundException e) {
24 | GL.logger.warn(getClass().getSimpleName(),e);
25 | } catch (JavaLayerException e) {
26 | GL.logger.warn(getClass().getSimpleName(),e);
27 | }
28 | }
29 |
30 | private void repeatPlay() {
31 | repeated.set(true);
32 | while (repeated.get()) {
33 | play();
34 | }
35 | }
36 |
37 | public void close() {
38 | repeated.set(false);
39 | player.close();
40 | }
41 |
42 | public void playInNewThread() {
43 | new Thread(this::play,"playInNewThread").start();
44 | }
45 |
46 | private void play() {
47 | try {
48 | this.fileInputStream = new FileInputStream(this.file);
49 | player = new Player(fileInputStream);
50 | player.play();
51 | } catch (JavaLayerException e) {
52 | GL.logger.warn(getClass().getSimpleName(),e);
53 | } catch (FileNotFoundException e) {
54 | GL.logger.warn(getClass().getSimpleName(),e);
55 | }
56 | }
57 |
58 | public void repeatPlayInNewThread() {
59 | new Thread(this::repeatPlay,"repeatPlayInNewThread").start();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/app/util/PropertiesManager.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import java.io.*;
4 | import java.util.Properties;
5 |
6 | public class PropertiesManager {
7 |
8 | //--------------------------------------- Field
9 |
10 | private Properties settings = new Properties();
11 | private static PropertiesManager propertiesManager = null;
12 | private File propertiesFile;
13 | private static final String PROPERTIES_DIR_PATH = "properties";
14 |
15 | //--------------------------------------- Setter And Getter
16 |
17 |
18 | //--------------------------------------- Constructor
19 |
20 | private PropertiesManager() {
21 | initFile();
22 | }
23 |
24 | //--------------------------------------- Method
25 |
26 | public static PropertiesManager getPropertiesManager() {
27 | if (propertiesManager == null) {
28 | propertiesManager = new PropertiesManager();
29 | }
30 | return propertiesManager;
31 | }
32 |
33 |
34 | private void initFile() {
35 | File propertiesDir = new File(ResGetter.getResFile(),PROPERTIES_DIR_PATH);
36 | if (!propertiesDir.exists())
37 | propertiesDir.mkdir();
38 | propertiesFile = new File(propertiesDir, "Settings.properties");
39 | createFileIfNotExist();
40 | loadSettings();
41 | }
42 |
43 | private void createFileIfNotExist() {
44 | if (!(propertiesFile.exists())) {
45 | try (OutputStream out = new FileOutputStream(propertiesFile)) {
46 |
47 | } catch (FileNotFoundException e) {
48 | GL.logger.warn(getClass().getSimpleName(),e);
49 | } catch (IOException e) {
50 | GL.logger.warn(getClass().getSimpleName(),e);
51 | }
52 | }
53 | }
54 |
55 |
56 | public String getProperty(String key, String defaultValue) {
57 | loadSettings();
58 | return settings.getProperty(key,defaultValue);
59 | }
60 |
61 | private void loadSettings() {
62 | try (InputStream in = new FileInputStream(propertiesFile)) {
63 | settings.load(in);
64 | } catch (IOException e) {
65 | GL.logger.warn(getClass().getSimpleName(),e);
66 | }
67 | }
68 |
69 | public void setProperty(String key, String newValue) {
70 | settings.setProperty(key, newValue);
71 | storeSettings();
72 | }
73 |
74 | private void storeSettings() {
75 | try (OutputStream out = new FileOutputStream(propertiesFile)) {
76 | settings.store(out, "Program Properties");
77 | } catch (IOException e) {
78 | GL.logger.warn(getClass().getSimpleName(),e);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/app/util/ResGetter.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import app.Main;
4 | import app.control.OnTopAlert;
5 | import javafx.scene.control.Alert;
6 |
7 | import java.io.File;
8 |
9 | public class ResGetter {
10 | public static String getResURLString() {
11 | return getResURIString();
12 | }
13 |
14 | public static String getResURIString() {
15 | File file = getResFile();
16 | String URIString = file.toURI().toString();
17 | return URIString;
18 | }
19 |
20 | private static String getJarDirPath() {
21 | String path = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath();
22 | if (System.getProperty("os.name").contains("dows")) {
23 | path = path.substring(1);
24 | }
25 | if (path.contains("jar")) {
26 | path = path.substring(0, path.lastIndexOf("."));
27 | return path.substring(0, path.lastIndexOf("/"));
28 | }
29 | return path.replace("target/classes/", "");
30 | }
31 |
32 | public static File getResFile() {
33 | File resFile = new File("res");
34 | boolean resDirIsInWorkDir = resFile.exists() && resFile.isDirectory();
35 | if (resDirIsInWorkDir)
36 | return resFile;
37 | else {
38 | String path = getJarDirPath();
39 | resFile = new File(path, "res");
40 | boolean resDirIsInJarDir = resFile.exists() && resFile.isDirectory();
41 | if (resDirIsInJarDir) {
42 | return resFile;
43 | } else {
44 | Alert alert = new OnTopAlert(Alert.AlertType.WARNING, "res files dir is not found !");
45 | alert.showAndWait();
46 | System.exit(1);
47 | return resFile;
48 | }
49 | }
50 | }
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/app/util/TaskBarProgressbar.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import com.sun.javafx.stage.StageHelper;
4 | import java.util.concurrent.ExecutorService;
5 | import java.util.concurrent.Executors;
6 | import javafx.stage.Stage;
7 | import org.bridj.Pointer;
8 | import org.bridj.cpp.com.COMRuntime;
9 | import org.bridj.cpp.com.shell.ITaskbarList3;
10 |
11 | public final class TaskBarProgressbar {
12 |
13 | private final Stage stage;
14 |
15 | private ExecutorService es;
16 | private ITaskbarList3 list;
17 | private Pointer> hwnd;
18 |
19 | private TaskBarProgressbar() {
20 | stage = null;
21 |
22 | es = Executors.newSingleThreadExecutor(r -> {
23 | Thread t = new Thread(r);
24 |
25 | t.setDaemon(true);
26 |
27 | return t;
28 | });
29 |
30 | es.execute(() -> {
31 | try {
32 | list = COMRuntime.newInstance(ITaskbarList3.class);
33 | } catch (ClassNotFoundException e) {
34 | GL.logger.warn(getClass().getSimpleName(),e);
35 | }
36 | });
37 | }
38 |
39 | public TaskBarProgressbar(Stage stage) {
40 | this.stage = stage;
41 |
42 | es = Executors.newSingleThreadExecutor(r -> {
43 | Thread t = new Thread(r);
44 |
45 | t.setDaemon(true);
46 |
47 | return t;
48 | });
49 |
50 | es.execute(() -> {
51 | try {
52 | list = COMRuntime.newInstance(ITaskbarList3.class);
53 | } catch (ClassNotFoundException e) {
54 | GL.logger.warn(getClass().getSimpleName(),e);
55 | }
56 | });
57 | }
58 |
59 | public void close() {
60 | es.submit(() -> list.Release());
61 | }
62 |
63 | public enum TaskBarProgressbarType {
64 | ERROR {
65 | @Override
66 | ITaskbarList3.TbpFlag getPair() {
67 | return ITaskbarList3.TbpFlag.TBPF_ERROR;
68 | }
69 | },
70 | INDETERMINATE {
71 | @Override
72 | ITaskbarList3.TbpFlag getPair() {
73 | return ITaskbarList3.TbpFlag.TBPF_INDETERMINATE;
74 | }
75 | },
76 | NOPROGRESS {
77 | @Override
78 | ITaskbarList3.TbpFlag getPair() {
79 | return ITaskbarList3.TbpFlag.TBPF_NOPROGRESS;
80 | }
81 | },
82 | NORMAL {
83 | @Override
84 | ITaskbarList3.TbpFlag getPair() {
85 | return ITaskbarList3.TbpFlag.TBPF_NORMAL;
86 | }
87 | },
88 | PAUSED {
89 | @Override
90 | ITaskbarList3.TbpFlag getPair() {
91 | return ITaskbarList3.TbpFlag.TBPF_PAUSED;
92 | }
93 | };
94 |
95 | abstract ITaskbarList3.TbpFlag getPair();
96 | }
97 |
98 | public void stopProgress() {
99 | long hwndVal = com.sun.glass.ui.Window.getWindows().get(StageHelper.getStages().indexOf(stage)).getNativeWindow();
100 | hwnd = Pointer.pointerToAddress(hwndVal);
101 |
102 | es.execute(() -> {
103 | list.SetProgressState((Pointer) hwnd, TaskBarProgressbarType.NOPROGRESS.getPair());
104 | });
105 | }
106 |
107 | public void showIndeterminateProgress() {
108 | long hwndVal = com.sun.glass.ui.Window.getWindows().get(StageHelper.getStages().indexOf(stage)).getNativeWindow();
109 | hwnd = Pointer.pointerToAddress(hwndVal);
110 |
111 | es.execute(() -> {
112 | list.SetProgressState((Pointer) hwnd, TaskBarProgressbarType.INDETERMINATE.getPair());
113 | });
114 | }
115 |
116 | public void showOtherProgress(long startValue, long endValue, TaskBarProgressbarType type) {
117 | long hwndVal = com.sun.glass.ui.Window.getWindows().get(StageHelper.getStages().indexOf(stage)).getNativeWindow();
118 | hwnd = Pointer.pointerToAddress(hwndVal);
119 |
120 | es.execute(() -> {
121 | list.SetProgressValue((Pointer) hwnd, startValue, endValue);
122 | list.SetProgressState((Pointer) hwnd, type.getPair());
123 | });
124 | }
125 |
126 | public void showOtherProgress(double value, TaskBarProgressbarType type) {
127 | int endValue = 100;
128 | value *= 100;
129 | int startValue = (int) value;
130 | showOtherProgress(startValue, endValue, type);
131 | }
132 |
133 | public void showErrorProgress() {
134 | long hwndVal = com.sun.glass.ui.Window.getWindows().get(StageHelper.getStages().indexOf(stage)).getNativeWindow();
135 | hwnd = Pointer.pointerToAddress(hwndVal);
136 |
137 | es.execute(() -> {
138 | list.SetProgressValue((Pointer) hwnd, 100, 100);
139 | list.SetProgressState((Pointer) hwnd, TaskBarProgressbarType.ERROR.getPair());
140 | });
141 | }
142 |
143 | public static void stopProgress(int windowIndex) {
144 | TaskBarProgressbar progressbar = new TaskBarProgressbar();
145 |
146 | long hwndVal = com.sun.glass.ui.Window.getWindows().get(windowIndex).getNativeWindow();
147 | progressbar.hwnd = Pointer.pointerToAddress(hwndVal);
148 |
149 | progressbar.es.execute(() -> {
150 | progressbar.list.SetProgressState((Pointer) progressbar.hwnd, TaskBarProgressbarType.NOPROGRESS.getPair());
151 | });
152 | }
153 |
154 | public static void showIndeterminateProgress(int windowIndex) {
155 | TaskBarProgressbar progressbar = new TaskBarProgressbar();
156 |
157 | long hwndVal = com.sun.glass.ui.Window.getWindows().get(windowIndex).getNativeWindow();
158 | progressbar.hwnd = Pointer.pointerToAddress(hwndVal);
159 |
160 | progressbar.es.execute(() -> {
161 | progressbar.list.SetProgressState((Pointer) progressbar.hwnd, TaskBarProgressbarType.INDETERMINATE.getPair());
162 | });
163 | }
164 |
165 | public static void showOtherProgress(int windowIndex, long startValue, long endValue, TaskBarProgressbarType type) {
166 | TaskBarProgressbar progressbar = new TaskBarProgressbar();
167 |
168 | long hwndVal = com.sun.glass.ui.Window.getWindows().get(windowIndex).getNativeWindow();
169 | progressbar.hwnd = Pointer.pointerToAddress(hwndVal);
170 |
171 | progressbar.es.execute(() -> {
172 | progressbar.list.SetProgressValue((Pointer) progressbar.hwnd, startValue, endValue);
173 | progressbar.list.SetProgressState((Pointer) progressbar.hwnd, type.getPair());
174 | });
175 | }
176 |
177 | public static void showErrorProgress(int windowIndex) {
178 | TaskBarProgressbar progressbar = new TaskBarProgressbar();
179 |
180 | long hwndVal = com.sun.glass.ui.Window.getWindows().get(windowIndex).getNativeWindow();
181 | progressbar.hwnd = Pointer.pointerToAddress(hwndVal);
182 |
183 | progressbar.es.execute(() -> {
184 | progressbar.list.SetProgressValue((Pointer) progressbar.hwnd, 100, 100);
185 | progressbar.list.SetProgressState((Pointer) progressbar.hwnd, TaskBarProgressbarType.ERROR.getPair());
186 | });
187 | }
188 |
189 | }
190 |
--------------------------------------------------------------------------------
/src/app/util/TimeStringPolisher.java:
--------------------------------------------------------------------------------
1 | package app.util;
2 |
3 | import java.util.regex.Matcher;
4 | import java.util.regex.Pattern;
5 |
6 | public class TimeStringPolisher {
7 |
8 | private String srcText;
9 | public static final String CHECK_REGEX = "\\d+:\\d+";
10 | public static final String SPLIT_REGEX = "\\d+";
11 |
12 | private String minuteString;
13 | private String hourString;
14 | private int minute;
15 | private int hour;
16 | private String polishedText;
17 |
18 | public TimeStringPolisher(String srcText) {
19 | this.srcText = srcText;
20 | }
21 |
22 | public String polish(){
23 | preCheck();
24 | splitString();
25 | parseToInt();
26 | numberRangeCheck();
27 | polishedText = String.format("%02d:%02d", hour, minute);
28 | return polishedText;
29 | }
30 |
31 | private void numberRangeCheck() {
32 | boolean isHourMatched = (hour >= 0) && (hour <= 23) ;
33 | if(!isHourMatched){
34 | String hourFormat = "Text '%s' could not be parsed:valid values is 0-23.";
35 | throw new RuntimeException(String.format(hourFormat, hourString));
36 | }
37 |
38 | boolean isMinuteMatched = (minute >= 0) && (minute <= 59);
39 | if(!isMinuteMatched){
40 | String minuteFormat = "Text '%s' could not be parsed:valid values is 0-59.";
41 | throw new RuntimeException(String.format(minuteFormat, minuteString));
42 | }
43 | }
44 |
45 | private void parseToInt() {
46 | hour = Integer.valueOf(hourString);
47 | minute = Integer.valueOf(minuteString);
48 | }
49 |
50 | private void splitString() {
51 | Pattern pattern = Pattern.compile(SPLIT_REGEX);
52 | Matcher matcher = pattern.matcher(srcText);
53 | matcher.find();
54 | hourString = matcher.group();
55 | matcher.find();
56 | minuteString = matcher.group();
57 | }
58 |
59 | private void preCheck(){
60 | boolean isMatched = Pattern.matches(CHECK_REGEX, srcText);
61 | if (!isMatched) {
62 | String mes = String.format("Text'%s' could not be parsed:valid values is \\d+:\\d+(RegEx).", srcText);
63 | throw new RuntimeException(mes);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/app/view/Close_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/src/app/view/Close_512x512.png
--------------------------------------------------------------------------------
/src/app/view/Controller.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 | import app.Main;
4 |
5 | public abstract class Controller {
6 | public Main main;
7 | public void setMainAndInit(Main main) {
8 | this.main = main;
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/view/DarkTheme.css:
--------------------------------------------------------------------------------
1 | .background {
2 | -fx-background-color: #1d1d1d;
3 | }
4 |
5 | .label {
6 | -fx-font-size: 11pt;
7 | -fx-font-family: "Segoe UI Semibold";
8 | -fx-text-fill: white;
9 | -fx-opacity: 0.6;
10 | }
11 |
12 | .label-bright {
13 | -fx-font-size: 11pt;
14 | -fx-font-family: "Segoe UI Semibold";
15 | -fx-text-fill: white;
16 | -fx-opacity: 1;
17 | }
18 |
19 | .label-header {
20 | -fx-font-size: 32pt;
21 | -fx-font-family: "Segoe UI Light";
22 | -fx-text-fill: white;
23 | -fx-opacity: 1;
24 | }
25 |
26 | .table-view {
27 | -fx-base: #1d1d1d;
28 | -fx-control-inner-background: #1d1d1d;
29 | -fx-background-color: #1d1d1d;
30 | -fx-table-cell-border-color: transparent;
31 | -fx-table-header-border-color: transparent;
32 | -fx-padding: 5;
33 | }
34 |
35 | .table-view .column-header-background {
36 | -fx-background-color: transparent;
37 | }
38 |
39 | .table-view .column-header, .table-view .filler {
40 | -fx-size: 35;
41 | -fx-border-width: 0 0 1 0;
42 | -fx-background-color: transparent;
43 | -fx-border-color:
44 | transparent
45 | transparent
46 | derive(-fx-base, 80%)
47 | transparent;
48 | -fx-border-insets: 0 10 1 0;
49 | }
50 |
51 | .table-view .column-header .label {
52 | -fx-font-size: 20pt;
53 | -fx-font-family: "Segoe UI Light";
54 | -fx-text-fill: white;
55 | -fx-alignment: center-left;
56 | -fx-opacity: 1;
57 | }
58 |
59 | .table-view:focused .table-row-cell:filled:focused:selected {
60 | -fx-background-color: -fx-focus-color;
61 | }
62 |
63 | .split-pane:horizontal > .split-pane-divider {
64 | -fx-border-color: transparent #1d1d1d transparent #1d1d1d;
65 | -fx-background-color: transparent, derive(#1d1d1d,20%);
66 | }
67 |
68 | .split-pane {
69 | -fx-padding: 1 0 0 0;
70 | }
71 |
72 | .menu-bar {
73 | -fx-background-color: derive(#1d1d1d,20%);
74 | }
75 |
76 | .context-menu {
77 | -fx-background-color: derive(#1d1d1d,50%);
78 | }
79 |
80 | .menu-bar .label {
81 | -fx-font-size: 14pt;
82 | -fx-font-family: "Segoe UI Light";
83 | -fx-text-fill: white;
84 | -fx-opacity: 0.9;
85 | }
86 |
87 | .menu .left-container {
88 | -fx-background-color: black;
89 | }
90 |
91 | .text-field {
92 | -fx-font-size: 12pt;
93 | -fx-font-family: "Segoe UI Semibold";
94 | }
95 |
96 | /*
97 | * Metro style Push Button
98 | * Author: Pedro Duque Vieira
99 | * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
100 | */
101 | .button {
102 | -fx-padding: 5 22 5 22;
103 | -fx-border-color: #e2e2e2;
104 | -fx-border-width: 2;
105 | -fx-background-radius: 0;
106 | -fx-background-color: #1d1d1d;
107 | -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
108 | -fx-font-size: 11pt;
109 | -fx-text-fill: #d8d8d8;
110 | -fx-background-insets: 0 0 0 0, 0, 1, 2;
111 | }
112 |
113 | .button:hover {
114 | -fx-background-color: #3a3a3a;
115 | }
116 |
117 | .button:pressed, .button:default:hover:pressed {
118 | -fx-background-color: white;
119 | -fx-text-fill: #1d1d1d;
120 | }
121 |
122 | .button:focused {
123 | -fx-border-color: white, white;
124 | -fx-border-width: 1, 1;
125 | -fx-border-style: solid, segments(1, 1);
126 | -fx-border-radius: 0, 0;
127 | -fx-border-insets: 1 1 1 1, 0;
128 | }
129 |
130 | .button:disabled, .button:default:disabled {
131 | -fx-opacity: 0.4;
132 | -fx-background-color: #1d1d1d;
133 | -fx-text-fill: white;
134 | }
135 |
136 | .button:default {
137 | -fx-background-color: -fx-focus-color;
138 | -fx-text-fill: #ffffff;
139 | }
140 |
141 | .button:default:hover {
142 | -fx-background-color: derive(-fx-focus-color,30%);
143 | }
--------------------------------------------------------------------------------
/src/app/view/EditDialog.fxml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/src/app/view/EditDialogControl.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 | import app.Main;
4 | import app.control.OnTopAlert;
5 | import app.util.GL;
6 | import app.util.TimeStringPolisher;
7 | import app.model.TomatoTask;
8 | import javafx.application.Platform;
9 | import javafx.beans.property.SimpleObjectProperty;
10 | import javafx.beans.value.ChangeListener;
11 | import javafx.beans.value.ObservableValue;
12 | import javafx.fxml.FXML;
13 | import javafx.scene.control.*;
14 |
15 | import java.time.Duration;
16 | import java.time.LocalTime;
17 | import java.util.concurrent.atomic.AtomicBoolean;
18 |
19 | public class EditDialogControl extends Controller {
20 |
21 |
22 | private static final String EMPTY_TASK_NAME = "";
23 | public static final int MINUTES_TO_ADD = -25;
24 | public static final String CHINESE_COLON = ":";
25 | public static final String ENGLISH_COLON = ":";
26 | public static final int DIGIT_AMOUNT_OF_TIME_STRING = 4;
27 | public static final int ENGLISH_COLON_INSERT_OFFSET = 2;
28 | //--------------------------------------- Field
29 | @FXML
30 | private DatePicker datePicker;
31 |
32 | @FXML
33 | private TextField startTime;
34 |
35 | @FXML
36 | private TextField endTime;
37 |
38 | @FXML
39 | private TextArea taskName;
40 |
41 | @FXML
42 | private Button okButton;
43 |
44 | @FXML
45 | private Button cancelButton;
46 |
47 | private final SimpleObjectProperty CURRENT_TOMATO_TASK = new SimpleObjectProperty();
48 | private AtomicBoolean isNewTask = new AtomicBoolean();
49 |
50 |
51 | //--------------------------------------- Method
52 |
53 | public TomatoTask getCURRENT_TOMATO_TASK() {
54 | return CURRENT_TOMATO_TASK.get();
55 | }
56 |
57 | public SimpleObjectProperty CURRENT_TOMATO_TASKProperty() {
58 | return CURRENT_TOMATO_TASK;
59 | }
60 |
61 | public void setCURRENT_TOMATO_TASK(TomatoTask CURRENT_TOMATO_TASK) {
62 | this.CURRENT_TOMATO_TASK.set(CURRENT_TOMATO_TASK);
63 | }
64 |
65 | @FXML
66 | private void handleOkButton() {
67 |
68 | getCURRENT_TOMATO_TASK().setDate(datePicker.getValue());
69 |
70 | getCURRENT_TOMATO_TASK().setStartTime(LocalTime.parse(startTime.getText()));
71 | getCURRENT_TOMATO_TASK().setEndTime(LocalTime.parse(endTime.getText()));
72 |
73 | String taskName = this.taskName.getText();
74 | if (taskName.isEmpty()) {
75 | Alert alert = new OnTopAlert(Alert.AlertType.WARNING, "确定要提交一个空任务吗?", ButtonType.NO, ButtonType.YES);
76 | ButtonType selectButton = alert.showAndWait().get();
77 | if (selectButton.equals(ButtonType.NO)) {
78 | return;
79 | }
80 | }
81 | getCURRENT_TOMATO_TASK().setName(taskName);
82 |
83 | if(isNewTask.get()){
84 | Platform.runLater(() -> main.getStackedPanes().addItems(getCURRENT_TOMATO_TASK()));
85 | }
86 | else {
87 | main.getTomatoTaskDataMapJson().write();
88 | main.getStackedPanes().getFocusedGridPane().refresh();
89 | }
90 |
91 | main.getEditDialogStage().close();
92 |
93 | Platform.runLater(()->{
94 | main.getMainLayoutController().getAddButton().setDisable(false);
95 | });
96 | }
97 |
98 | @FXML
99 | private void handleCancelButton() {
100 | main.getEditDialogStage().close();
101 | }
102 |
103 | public void loadNewTask() {
104 | isNewTask.set(true);
105 | LocalTime endTime = LocalTime.now();
106 | setCURRENT_TOMATO_TASK(new TomatoTask(EMPTY_TASK_NAME, getStartTime(endTime), endTime));
107 | }
108 | public void loadSpecifiedTask(TomatoTask tomatoTask) {
109 | isNewTask.set(false);
110 | setCURRENT_TOMATO_TASK(tomatoTask);
111 | }
112 |
113 | private LocalTime getStartTime(LocalTime endTime) {
114 | LocalTime startTime = LocalTime.from(endTime);
115 | Duration duration = Duration.ofMinutes(MINUTES_TO_ADD);
116 | startTime = startTime.plus(duration);
117 | return startTime;
118 | }
119 |
120 | @Override
121 | public void setMainAndInit(Main main) {
122 | super.setMainAndInit(main);
123 | currentTomatoTaskBind();
124 | textFieldCheck();
125 | }
126 |
127 | public class TimeFieldTrueCheck implements ChangeListener {
128 |
129 | private TextField textField;
130 |
131 | public TimeFieldTrueCheck(TextField textField) {
132 | this.textField = textField;
133 | }
134 |
135 | @Override
136 | public void changed(ObservableValue extends String> observable, String oldText, String newText) {
137 |
138 |
139 | if (newText.contains(CHINESE_COLON)) {
140 | newText = (newText.replace(CHINESE_COLON, ENGLISH_COLON));
141 | }
142 |
143 | boolean notOnlyContainTimeChar = !onlyContainTimeChar(newText);
144 |
145 |
146 | if (notOnlyContainTimeChar) {
147 | newText = (oldText);
148 | }
149 |
150 | boolean autoSwap = onlyContainDigit(newText) &&
151 | (newText.length() == DIGIT_AMOUNT_OF_TIME_STRING);
152 | if (autoSwap) {
153 | StringBuilder sb = new StringBuilder(newText);
154 | sb.insert(ENGLISH_COLON_INSERT_OFFSET, ENGLISH_COLON);
155 | newText = (sb.toString());
156 | }
157 |
158 | this.textField.setText(newText);
159 |
160 | }
161 | }
162 |
163 | public class TimeFieldCheckWhenLoseFocus implements ChangeListener {
164 |
165 | private TextField textField;
166 | private String correctFormatString;
167 |
168 | public TimeFieldCheckWhenLoseFocus(TextField textField) {
169 | this.textField = textField;
170 | correctFormatString = textField.getText();
171 | }
172 |
173 | @Override
174 | public void changed(ObservableValue extends Boolean> observable, Boolean oldIsFocused, Boolean newIsFocused) {
175 | if (newIsFocused){
176 | correctFormatString = textField.getText();
177 | }
178 | else {
179 | TimeStringPolisher timeStringPolisher = new TimeStringPolisher(textField.getText());
180 | String newText = null;
181 | try {
182 | newText = timeStringPolisher.polish();
183 | } catch (Exception e) {
184 | GL.logger.warn(getClass().getSimpleName(),e);
185 | newText = correctFormatString;
186 | }
187 | finally {
188 | textField.setText(newText);
189 | }
190 | }
191 | }
192 | }
193 |
194 | private void textFieldCheck() {
195 | startTime.textProperty().addListener(new TimeFieldTrueCheck(startTime));
196 | endTime.textProperty().addListener(new TimeFieldTrueCheck(endTime));
197 | startTime.focusedProperty().addListener(new TimeFieldCheckWhenLoseFocus(startTime));
198 | endTime.focusedProperty().addListener(new TimeFieldCheckWhenLoseFocus(endTime));
199 | }
200 |
201 | private boolean onlyContainTimeChar(String newText) {
202 | char[] charArray = newText.toCharArray();
203 | for (char c : charArray) {
204 | if (!isDigitOrEnglishColon(c)) {
205 | return false;
206 | }
207 | }
208 | return true;
209 | }
210 | private boolean onlyContainDigit(String newText) {
211 | char[] charArray = newText.toCharArray();
212 | for (char c : charArray) {
213 | if (!Character.isDigit(c)) {
214 | return false;
215 | }
216 | }
217 | return true;
218 | }
219 |
220 | private boolean isDigitOrEnglishColon(char c) {
221 | String letterOrDigital = "0123456789";
222 | letterOrDigital += ENGLISH_COLON;
223 | return -1 != letterOrDigital.indexOf(c);
224 | }
225 |
226 |
227 | private void currentTomatoTaskBind() {
228 | CURRENT_TOMATO_TASKProperty().addListener((observable, oldTomatoTask, newTomatoTask) -> {
229 | if (newTomatoTask != null) {
230 | datePicker.setValue(newTomatoTask.getDate());
231 | startTime.setText(newTomatoTask.getStartTimeString());
232 | endTime.setText(newTomatoTask.getEndTimeString());
233 | taskName.setWrapText(true);
234 | taskName.setText(newTomatoTask.getName());
235 | }
236 | });
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/src/app/view/Edit_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/src/app/view/Edit_512x512.png
--------------------------------------------------------------------------------
/src/app/view/FinishDialog.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/app/view/FinishDialogController.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 |
4 | import app.Main;
5 | import app.control.OnTopAlert;
6 | import app.model.TomatoTask;
7 | import javafx.event.ActionEvent;
8 | import javafx.fxml.FXML;
9 | import javafx.scene.control.*;
10 |
11 | public class FinishDialogController extends Controller{
12 |
13 | @FXML
14 | private Label label;
15 | @FXML
16 | private Button okButton;
17 | @FXML
18 | private Button deleteButton;
19 |
20 | @FXML
21 | private TextField textField;
22 |
23 |
24 | @FXML
25 | private void handleDeleteButton(ActionEvent event) {
26 | main.getFinishDialogStage().close();
27 | }
28 |
29 | public TextField getTextField() {
30 | return textField;
31 | }
32 |
33 | @FXML
34 | private void initialize() {
35 | deleteButton.setOnAction(this::handleDeleteButton);
36 | textField.setOnAction(this::handleTextField);
37 | okButton.setOnAction(this::handleTextField);
38 | }
39 |
40 | @FXML
41 | private void handleTextField(ActionEvent event) {
42 |
43 | String taskName = this.textField.getText();
44 |
45 | if ((taskName==null)||(taskName.isEmpty())) {
46 | Alert alert = new OnTopAlert(Alert.AlertType.WARNING, "确定要提交一个空任务吗?", ButtonType.NO, ButtonType.YES);
47 | ButtonType selectButton = alert.showAndWait().get();
48 | if (selectButton.equals(ButtonType.NO)) {
49 | return;
50 | }
51 | }
52 | TomatoTask tomatoTask = new TomatoTask(taskName,
53 | Main.WORK_COUNT_DOWN);
54 | main.getStackedPanes().addItems(tomatoTask);
55 | Main.RESPITE_COUNT_DOWN.start();
56 | Main.RESPITE_DURATION_MP3_PLAYER.repeatPlayInNewThread();
57 |
58 |
59 | main.getFinishDialogStage().close();
60 | }
61 | @Override
62 | public void setMainAndInit(Main main) {
63 | super.setMainAndInit(main);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/app/view/MainLayout.fxml:
--------------------------------------------------------------------------------
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 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/app/view/MainLayoutController.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 | import app.*;
4 | import app.control.OnTopAlert;
5 | import app.util.*;
6 | import javafx.application.Platform;
7 | import javafx.beans.value.ChangeListener;
8 | import javafx.collections.ObservableList;
9 | import javafx.collections.ObservableMap;
10 | import javafx.fxml.FXML;
11 | import javafx.scene.Node;
12 | import javafx.scene.control.*;
13 | import javafx.scene.layout.AnchorPane;
14 | import javafx.scene.layout.FlowPane;
15 | import javafx.scene.text.Text;
16 | import app.model.TomatoTask;
17 | import java.time.LocalDate;
18 | import java.util.ArrayList;
19 | import java.util.List;
20 | import java.util.concurrent.locks.ReentrantLock;
21 |
22 | public class MainLayoutController extends Controller {
23 |
24 |
25 | //--------------------------------------- Field
26 |
27 | public static final String START = "Start";
28 | public static final String STOP = "Stop";
29 | public static final int FIRST_INDEX = 0;
30 |
31 | private final int REDO_DELETE_BAR_SHOW_MILLIS = 5000;
32 |
33 | @FXML
34 | private AnchorPane anchorPane;
35 |
36 |
37 | @FXML
38 | private TableColumn startColumn;
39 | @FXML
40 | private TableColumn endColumn;
41 | @FXML
42 | private TableColumn nameColumn;
43 | @FXML
44 | private TableColumn dateColumn;
45 |
46 | @FXML
47 | private Button startOrStopButton;
48 | @FXML
49 | private Button plusButton;
50 | @FXML
51 | private Button addButton;
52 | @FXML
53 | private Button redoDeleteButton;
54 |
55 | @FXML
56 | private Text progressText;
57 |
58 | @FXML
59 | private Text headText;
60 |
61 | @FXML
62 | private ProgressBar progressBar;
63 |
64 | TaskBarProgressbar taskProgressbar = null;
65 |
66 | @FXML
67 | private FlowPane buttonFlowPane;
68 | @FXML
69 | private FlowPane buttonFlowPaneBackground;
70 |
71 |
72 | private int todayTaskAmount = 0;
73 | private Thread showRedoBarAndSleepThread;
74 |
75 |
76 | private static final Double STACKED_PANE_MARGIN = 5.0;
77 | private static final Double STACKED_PANE_MARGIN_TOP = 80.0;
78 | private ReentrantLock startOrStopLock = new ReentrantLock();
79 |
80 | //--------------------------------------- Getter Setter
81 |
82 |
83 | public Button getStartOrStopButton() {
84 | return startOrStopButton;
85 | }
86 |
87 | public Button getAddButton() {
88 | return addButton;
89 | }
90 |
91 | public Button getPlusButton() {
92 | return plusButton;
93 | }
94 |
95 | public TableColumn getNameColumn() {
96 | return nameColumn;
97 | }
98 |
99 | public void setNameColumn(TableColumn nameColumn) {
100 | this.nameColumn = nameColumn;
101 | }
102 |
103 |
104 | //---------------------------------------handle
105 |
106 | @FXML
107 | private void handleRedoDelete() {
108 | List removedItems = main.getREDO_TOMATO_TASKS();
109 | if (!removedItems.isEmpty()) {
110 | main.getStackedPanes().addItems(removedItems);
111 | removedItems.clear();
112 | } else {
113 | }
114 | }
115 |
116 |
117 | @FXML
118 | private void handlePlusButton() {
119 | main.startPlusDialogAndWait();
120 | }
121 |
122 | @FXML
123 | private void handleAddButton() {
124 | main.getEditDialogController().loadNewTask();
125 | main.startEditDialogAndWait("添加新任务");
126 | }
127 |
128 |
129 | @FXML
130 | private void handleStartOrStopButton() {
131 | if (startOrStopButton.getText().equals(START)) {
132 | handleStartButton();
133 | } else if (startOrStopButton.getText().equals(STOP)) {
134 | handleStopButton();
135 | }
136 | }
137 |
138 | private void handleStopButton() {
139 | startOrStopLock.lock();
140 | try {
141 | if (Main.WORK_COUNT_DOWN.isStarted()) {
142 | Main.WORK_COUNT_DOWN.cancel();
143 | Main.WORK_DURATION_MP3_PLAYER.close();
144 | }
145 |
146 | if (Main.RESPITE_COUNT_DOWN.isStarted()) {
147 | Main.RESPITE_COUNT_DOWN.cancel();
148 | Main.RESPITE_DURATION_MP3_PLAYER.close();
149 | }
150 | } finally {
151 | startOrStopLock.unlock();
152 | }
153 | }
154 |
155 | private void handleStartButton() {
156 | startOrStopLock.lock();
157 | try {
158 | Main.WORK_COUNT_DOWN.start();
159 | Main.WORK_DURATION_MP3_PLAYER.repeatPlayInNewThread();
160 | } finally {
161 | startOrStopLock.unlock();
162 | }
163 | }
164 |
165 |
166 | private void handleWorkFinished() {
167 | handleWorkFinished(true);
168 | }
169 |
170 | private void handleWorkFinished(boolean ableMusic) {
171 | Main.WORK_DURATION_MP3_PLAYER.close();
172 | if (ableMusic)
173 | Main.WORK_FINISHED_MP3_PLAYER.playInNewThread();
174 | Platform.runLater(() -> {
175 | getStartOrStopButton().setDisable(true);
176 | main.startFinishDialogAndWait();
177 | getStartOrStopButton().setDisable(false);
178 | });
179 | }
180 |
181 |
182 | //--------------------------------------- Bind setSetting
183 |
184 |
185 |
186 |
187 | private void sizeBind() {
188 | anchorSizeBindAndInit();
189 | }
190 |
191 | ChangeListener super Number> selectedIndexListener = (observable, oldSelectedIndex, newSelectedIndex) -> {
192 | int selectedIndex = (Integer) newSelectedIndex;
193 | boolean disable = selectedIndex < 0;
194 | };
195 |
196 |
197 | private void setSettingListenerAndSetDuration() {
198 | boolean checked = main.getSettingDialogController().getThreeSecondModeBox().isSelected();
199 | developmentMode(checked);
200 |
201 | main.getSettingDialogController().getThreeSecondModeBox().selectedProperty().addListener((observable,
202 | oldValue,
203 | newValue) -> {
204 | developmentMode(newValue);
205 |
206 | });
207 | }
208 |
209 |
210 | private void anchorSizeBindAndInit() {
211 | anchorWidthBindAndInit();
212 | anchorHeightBindAndInit();
213 | }
214 |
215 | private void anchorHeightBindAndInit() {
216 | String heightString = Main.PROPERTIES_MANAGER.getProperty("anchorHeight", Double.toString(anchorPane.getPrefHeight()));
217 | double height = Double.parseDouble(heightString);
218 | anchorPane.setPrefHeight(height);
219 | anchorPane.heightProperty().addListener((observable, oldValue, newValue) -> {
220 | double anchorHeight = (Double) newValue;
221 | Main.PROPERTIES_MANAGER.setProperty("anchorHeight", Double.toString(anchorHeight));
222 | });
223 | }
224 |
225 | private void anchorWidthBindAndInit() {
226 | String widthString = Main.PROPERTIES_MANAGER.getProperty("anchorWidth", Double.toString(anchorPane.getPrefWidth()));
227 | double width = Double.parseDouble(widthString);
228 | anchorPane.setPrefWidth(width);
229 | anchorPane.widthProperty().addListener((observable, oldValue, newValue) -> {
230 | double anchorWidth = (Double) newValue;
231 | Main.PROPERTIES_MANAGER.setProperty("anchorWidth", Double.toString(anchorWidth));
232 | });
233 | }
234 |
235 | private void setRespiteCountDownListener() {
236 | Main.RESPITE_COUNT_DOWN.startedProperty().addListener((observable, oldValue, newValue) -> {
237 | Platform.runLater(() -> {
238 | boolean isStarted = newValue;
239 | if (isStarted){
240 | startOrStopButton.setText(STOP);
241 | progressBar.setStyle("-fx-accent: #ffc800");
242 | }
243 | else
244 | startOrStopButton.setText(START);
245 | });
246 |
247 | });
248 | Main.RESPITE_COUNT_DOWN.barProgressProperty().addListener((observable, oldValue, newValue) -> {
249 | Platform.runLater(() -> {
250 | progressBar.setProgress((Double) newValue);
251 | taskProgressbar.showOtherProgress((Double) newValue, TaskBarProgressbar.TaskBarProgressbarType.PAUSED);
252 | });
253 | });
254 |
255 | Main.RESPITE_COUNT_DOWN.textProgressProperty().addListener((observable, oldValue, newValue) -> {
256 | Platform.runLater(() -> {
257 | progressText.setText(newValue);
258 | });
259 | });
260 | Main.RESPITE_COUNT_DOWN.finishedProperty().addListener((observable, oldValue, newValue) -> {
261 | boolean finished = newValue;
262 | if (finished) {
263 | Main.RESPITE_DURATION_MP3_PLAYER.close();
264 | Main.RESPITE_FINISHED_MP3_PLAYER.playInNewThread();
265 | Platform.runLater(
266 | () -> {
267 | Alert respiteFinishedAlert = new OnTopAlert(Alert.AlertType.INFORMATION
268 | , "休息已结束,是否开启下一个番茄?"
269 | , ButtonType.YES, ButtonType.NO);
270 | respiteFinishedAlert.setTitle("休息已结束");
271 | respiteFinishedAlert.setHeaderText("休息已结束");
272 | ButtonType buttonType = respiteFinishedAlert.showAndWait().orElse(ButtonType.YES);
273 | if (buttonType.equals(ButtonType.YES))
274 | handleStartButton();
275 | });
276 | }
277 | });
278 |
279 | }
280 |
281 |
282 | private void setWorkCountDownListener() {
283 | Main.WORK_COUNT_DOWN.startedProperty().addListener((observable, oldValue, newValue) -> {
284 | Platform.runLater(() -> {
285 | boolean isStarted = newValue;
286 | if (isStarted) {
287 | startOrStopButton.setText(STOP);
288 | getPlusButton().setDisable(false);
289 | progressBar.setStyle("-fx-accent: #328e2e");
290 | } else {
291 | startOrStopButton.setText(START);
292 | getPlusButton().setDisable(true);
293 | }
294 | });
295 |
296 | });
297 |
298 | Main.WORK_COUNT_DOWN.barProgressProperty().addListener((observable, oldValue, newValue) -> {
299 | Platform.runLater(() -> {
300 | progressBar.setProgress((Double) newValue);
301 | taskProgressbar.showOtherProgress((Double) newValue, TaskBarProgressbar.TaskBarProgressbarType.NORMAL);
302 | });
303 |
304 | });
305 |
306 | Main.WORK_COUNT_DOWN.textProgressProperty().addListener((observable, oldValue, newValue) -> {
307 | Platform.runLater(() -> {
308 | progressText.setText(newValue);
309 | });
310 | });
311 | Main.WORK_COUNT_DOWN.finishedProperty().addListener((observable, oldValue, newValue) -> {
312 | boolean finished = newValue;
313 | if (finished) {
314 | handleWorkFinished();
315 | }
316 | });
317 | }
318 |
319 |
320 | //--------------------------------------- Method
321 |
322 | private void developmentMode(Boolean newValue) {
323 | if (newValue) {
324 | Main.WORK_COUNT_DOWN.setDuration(Main.DEVELOPMENT_DURATION);
325 | Main.RESPITE_COUNT_DOWN.setDuration(Main.DEVELOPMENT_DURATION);
326 | } else {
327 | Main.WORK_COUNT_DOWN.setDuration(Main.DEFAULT_WORK_DURATION);
328 | Main.RESPITE_COUNT_DOWN.setDuration(Main.DEFAULT_RESPITE_DURATION);
329 | }
330 | }
331 |
332 |
333 | private void addTaskNameAfterFinished(String taskName) {
334 |
335 | if ((taskName == null)||(taskName.isEmpty())) {
336 | Alert alert = new OnTopAlert(Alert.AlertType.WARNING, "确定要提交一个空任务吗?", ButtonType.NO, ButtonType.YES);
337 | ButtonType selectButton = alert.showAndWait().get();
338 | if (selectButton.equals(ButtonType.NO)) {
339 | //Platform.runLater 在这里是必不可少的
340 | Platform.runLater(() -> {
341 | main.startFinishDialogAndWait();
342 | });
343 | return;
344 | } else {
345 | }
346 | }
347 |
348 | TomatoTask tomatoTask = new TomatoTask(taskName,
349 | Main.WORK_COUNT_DOWN);
350 | main.getStackedPanes().addItems(tomatoTask);
351 | Main.RESPITE_COUNT_DOWN.start();
352 | Main.RESPITE_DURATION_MP3_PLAYER.repeatPlayInNewThread();
353 |
354 | }
355 |
356 | @Override
357 | public void setMainAndInit(Main main) {
358 | super.setMainAndInit(main);
359 | initHeadText();
360 | headTextBind();
361 | setWorkCountDownListener();
362 | setRespiteCountDownListener();
363 | sizeBind();
364 | initCountDownText();
365 | setSettingListenerAndSetDuration();
366 | setStackedPanes();
367 | taskProgressbar = new TaskBarProgressbar(main.getPrimaryStage());
368 |
369 | }
370 |
371 |
372 | private void setStackedPanes() {
373 |
374 | main.getStackedPanes().setPrefHeight(240.0);
375 |
376 | AnchorPane.setTopAnchor(main.getStackedPanes(), STACKED_PANE_MARGIN_TOP);
377 |
378 | AnchorPane.setBottomAnchor(main.getStackedPanes(), STACKED_PANE_MARGIN);
379 | AnchorPane.setLeftAnchor(main.getStackedPanes(), STACKED_PANE_MARGIN);
380 | AnchorPane.setRightAnchor(main.getStackedPanes(), STACKED_PANE_MARGIN);
381 |
382 |
383 | Node temp = anchorPane.getChildren().get(FIRST_INDEX);
384 | anchorPane.getChildren().set(FIRST_INDEX, main.getStackedPanes());
385 | anchorPane.getChildren().add(temp);
386 | }
387 |
388 |
389 | private void updateHeadText() {
390 | headText.setText("今日已完成 " + todayTaskAmount + " 个番茄");
391 | }
392 |
393 | public void initHeadText() {
394 | todayTaskAmount = getCertainDayTaskAmount(Main.getTomatoTasksMap(), LocalDate.now());
395 | updateHeadText();
396 | }
397 |
398 | private void headTextBind() {
399 | main.getStackedPanes().titledPaneItemsChangeProperty().addListener((observable, oldValue, newValue) -> {
400 | initHeadText();
401 | });
402 | }
403 |
404 | private int getCertainDayTaskAmount(ObservableMap> map, LocalDate date) {
405 | ObservableList CertainDayTaskList = map.get(date);
406 | if (CertainDayTaskList == null)
407 | return 0;
408 | else
409 | return CertainDayTaskList.size();
410 | }
411 |
412 |
413 | private void initCountDownText() {
414 | progressText.setText("");
415 | }
416 |
417 | public void showRedoBarAndSleep() {
418 | showRedoBarAndSleepThread = new Thread(() -> {
419 | showRedoBar();
420 | try {
421 | Thread.sleep(REDO_DELETE_BAR_SHOW_MILLIS);
422 | } catch (InterruptedException e) {
423 | } finally {
424 | hideRedoBar();
425 | }
426 | },"showRedoBar");
427 | showRedoBarAndSleepThread.start();
428 | }
429 |
430 | private void hideRedoBar() {
431 | Platform.runLater(() -> {
432 | buttonFlowPaneBackground.setVisible(false);
433 | buttonFlowPane.setVisible(false);
434 | });
435 | }
436 |
437 | public void closeRedoBar() {
438 | if (showRedoBarAndSleepThread != null) {
439 | showRedoBarAndSleepThread.interrupt();
440 | }
441 |
442 | }
443 |
444 | private void showRedoBar() {
445 | Platform.runLater(() -> {
446 | buttonFlowPaneBackground.setVisible(true);
447 | buttonFlowPane.setVisible(true);
448 | });
449 |
450 | }
451 | }
452 |
--------------------------------------------------------------------------------
/src/app/view/PlusDialog.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/app/view/PlusDialogController.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 | import app.Main;
4 | import app.util.Mp3Player;
5 | import app.util.ResGetter;
6 | import javafx.beans.property.SimpleStringProperty;
7 | import javafx.fxml.FXML;
8 | import javafx.scene.control.Button;
9 | import javafx.scene.control.Label;
10 | import javafx.scene.control.TextField;
11 | import javafx.scene.layout.AnchorPane;
12 |
13 | import java.io.File;
14 |
15 | public class PlusDialogController extends Controller{
16 |
17 | @FXML
18 | private AnchorPane anchorPane;
19 | @FXML
20 | private Label label;
21 | @FXML
22 | private Button okButton;
23 |
24 |
25 | private Mp3Player addTasksMp3Player = new Mp3Player(new File(ResGetter.getResFile(),"sound/add_tasks_ahead_of_time.mp3"));
26 | private String tempFieldText;
27 |
28 |
29 | @FXML
30 | private void handleOkButton() {
31 | handleTextField();
32 | }
33 |
34 |
35 | public Label getLabel() {
36 | return label;
37 | }
38 |
39 | public void setLabel(Label label) {
40 | this.label = label;
41 | }
42 |
43 | public Button getOkButton() {
44 | return okButton;
45 | }
46 |
47 | public void setOkButton(Button okButton) {
48 | this.okButton = okButton;
49 | }
50 |
51 | public TextField getTextField() {
52 | return textField;
53 | }
54 |
55 | public void setTextField(TextField textField) {
56 | this.textField = textField;
57 | }
58 |
59 | @FXML
60 | private TextField textField;
61 |
62 | private SimpleStringProperty inputString = new SimpleStringProperty(null);
63 |
64 | public String getInputString() {
65 | return inputString.get();
66 | }
67 |
68 | public SimpleStringProperty inputStringProperty() {
69 | return inputString;
70 | }
71 |
72 | public void setInputString(String inputString) {
73 | this.inputString.set(inputString);
74 | }
75 |
76 | @FXML
77 | private void handleTextField() {
78 | main.getPlusDialogStage().close();
79 | inputString.set(textField.getCharacters().toString());
80 | main.getFinishDialogController().getTextField().setText(inputString.getValue());
81 | if ((getTextField().getText() != "")&&(!getTextField().getText().equals(getTempFieldText()))) {
82 | addTasksMp3Player.playInNewThread();
83 | }
84 | saveTempFieldText();
85 | }
86 |
87 | @Override
88 | public void setMainAndInit(Main main) {
89 | super.setMainAndInit(main);
90 | }
91 |
92 | private void saveTempFieldText() {
93 | tempFieldText = getTextField().getText();
94 | }
95 |
96 | public String getTempFieldText() {
97 | return tempFieldText;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/app/view/RootLayout.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/app/view/RootLayoutController.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 | import app.Main;
4 | import javafx.fxml.FXML;
5 | import javafx.scene.control.*;
6 | import javafx.scene.layout.*;
7 |
8 | public class RootLayoutController extends Controller{
9 |
10 | @FXML
11 | private BorderPane rootLayout;
12 | @FXML
13 | private MenuBar MenuBar;
14 | @FXML
15 | private Menu fileMenu;
16 | @FXML
17 | private MenuItem settingsMenuItem;
18 |
19 |
20 | @FXML
21 | private void handleSettingsMenuItem() {
22 | main.startSettingDialogAndWait();
23 | }
24 |
25 | @Override
26 | public void setMainAndInit(Main main) {
27 | super.setMainAndInit(main);
28 | rootLayout.setCenter(main.getMainLayout());
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/app/view/SettingDialog.fxml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/src/app/view/SettingDialogController.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 | import app.Main;
4 | import javafx.fxml.FXML;
5 | import javafx.scene.control.CheckBox;
6 | import javafx.scene.control.Tab;
7 | import javafx.scene.control.TabPane;
8 | import javafx.scene.layout.AnchorPane;
9 |
10 | public class SettingDialogController extends Controller {
11 |
12 |
13 | @FXML
14 | private TabPane tabPane;
15 |
16 | @FXML
17 | private Tab genericTab;
18 |
19 | @FXML
20 | private AnchorPane genericAnchorPane;
21 |
22 | @FXML
23 | private CheckBox threeSecondMode;
24 |
25 | @FXML
26 | private void throwException() throws Exception {
27 | throw new RuntimeException("Exception Throw Test!");
28 | }
29 |
30 | @Override
31 | public void setMainAndInit(Main main) {
32 | super.setMainAndInit(main);
33 | loadPropertyFromFile();
34 | addPropertyListener();
35 | }
36 |
37 | private void loadPropertyFromFile() {
38 | boolean developmentMode;
39 | developmentMode = Boolean.parseBoolean(Main.PROPERTIES_MANAGER.getProperty("developmentMode", "false"));
40 | threeSecondMode.setSelected(developmentMode);
41 | }
42 |
43 | private void addPropertyListener() {
44 | threeSecondMode.selectedProperty().addListener((observable, oldValue, newValue) -> {
45 | Main.PROPERTIES_MANAGER.setProperty("developmentMode", newValue ? "true" : "false");
46 | });
47 | }
48 |
49 |
50 | public TabPane getTabPane() {
51 | return tabPane;
52 | }
53 |
54 | public void setTabPane(TabPane tabPane) {
55 | this.tabPane = tabPane;
56 | }
57 |
58 | public Tab getGenericTab() {
59 | return genericTab;
60 | }
61 |
62 | public void setGenericTab(Tab genericTab) {
63 | this.genericTab = genericTab;
64 | }
65 |
66 | public AnchorPane getGenericAnchorPane() {
67 | return genericAnchorPane;
68 | }
69 |
70 | public void setGenericAnchorPane(AnchorPane genericAnchorPane) {
71 | this.genericAnchorPane = genericAnchorPane;
72 | }
73 |
74 | public CheckBox getThreeSecondModeBox() {
75 | return threeSecondMode;
76 | }
77 |
78 | public void setThreeSecondModeBox(CheckBox ThreeSecondModeBox) {
79 | this.threeSecondMode = ThreeSecondModeBox;
80 | }
81 |
82 | @FXML
83 | private void handleThreeSecondModeBox() {
84 | boolean result = threeSecondMode.isSelected();
85 | }
86 |
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/app/view/StackedPanesController.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 | import app.Main;
4 | import app.control.mytomato.StackedPanes;
5 | import app.model.TomatoTask;
6 | import javafx.collections.MapChangeListener;
7 | import javafx.collections.ObservableList;
8 | import javafx.scene.input.KeyCode;
9 | import javafx.scene.input.KeyEvent;
10 |
11 | import java.time.LocalDate;
12 |
13 | public class StackedPanesController extends Controller {
14 |
15 |
16 | //--------------------------------------- Field
17 |
18 | private StackedPanes stackedPanes;
19 |
20 | //--------------------------------------- Getter Setter
21 | //--------------------------------------- Method
22 |
23 | public StackedPanesController() {
24 |
25 | }
26 |
27 | @Override
28 | public void setMainAndInit(Main main) {
29 | super.setMainAndInit(main);
30 | stackedPanes.setItemsMap(Main.getTomatoTasksMap());
31 | writeWhenItemsChange();
32 | stackedPanes.setStackedPanesController(this);
33 | }
34 |
35 | private void writeWhenItemsChange() {
36 | stackedPanes.getItemsMap().addListener(new MapChangeListener>() {
37 | @Override
38 | public void onChanged(Change extends LocalDate, ? extends ObservableList> change) {
39 | main.getTomatoTaskDataMapJson().write();
40 | }
41 | });
42 | stackedPanes.titledPaneItemsChangeProperty().addListener((observable, oldChange, newChange) -> {
43 | main.getTomatoTaskDataMapJson().write();
44 | });
45 | }
46 |
47 | public StackedPanes createScrollPane() {
48 | stackedPanes = new StackedPanes();
49 | return stackedPanes;
50 | }
51 |
52 |
53 | }
--------------------------------------------------------------------------------
/src/app/view/Test.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/app/view/TestController.java:
--------------------------------------------------------------------------------
1 | package app.view;
2 |
3 | public class TestController {
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/view/Trash_67981_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Andy-AO/MyTomato/04a157c8a4ce00f899a4e6ff263072942e6e9e7c/src/app/view/Trash_67981_512x512.png
--------------------------------------------------------------------------------
/src/app/view/stylesheet.css:
--------------------------------------------------------------------------------
1 |
2 | .v-box {
3 | -fx-padding: 10;
4 | -fx-spacing: 10;
5 | }
6 |
7 | .grid-pane {
8 | -fx-hgap: 10;
9 | -fx-vgap: 10;
10 | -fx-background-color: rgb(250, 250, 250);
11 | -fx-alignment: top-left;
12 | -fx-pref-height: 0;
13 | -fx-padding: 1 1 1 15;
14 | }
15 | .title-pane {
16 | -fx-pref-height: 0;
17 |
18 | }
19 |
20 | .delete-button{
21 | -fx-border-color: transparent;
22 | -fx-border-width: 0;
23 | -fx-background-radius: 0;
24 | -fx-background-color: transparent;
25 | -fx-background-image: url(../view/Trash_67981_512x512.png);
26 | -fx-background-size: 16px 16px;
27 | -fx-background-repeat: no-repeat;
28 | -fx-background-position: center;
29 | -fx-pref-width: 25;
30 | -fx-pref-height: 25;
31 | }
32 |
33 |
34 | .edit-button{
35 | -fx-border-color: transparent;
36 | -fx-border-width: 0;
37 | -fx-background-radius: 0;
38 | -fx-background-color: transparent;
39 | -fx-background-image: url(../view/Edit_512x512.png);
40 | -fx-background-size: 16px 16px;
41 | -fx-background-repeat: no-repeat;
42 | -fx-background-position: center;
43 | -fx-pref-width: 25;
44 | -fx-pref-height: 25;
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/src/sample/Parent.java:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import app.util.GL;
4 | import javafx.fxml.FXML;
5 | import javafx.scene.control.Button;
6 | import javafx.scene.control.Label;
7 |
8 | public class Parent {
9 | public static class Child {
10 |
11 | @FXML
12 | private Label label;
13 |
14 | @FXML
15 | private Button okButton;
16 |
17 |
18 | @FXML
19 | private void initialize() {
20 | System.out.println("initialize " + getClass().getName());
21 | System.out.println("label = " + label);
22 | okButton.setText("OKOK");
23 | }
24 |
25 | @FXML
26 | private void handleOkButton(){
27 | GL.logger.debug("handleOkButton");
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/sample/Sample.java:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import javafx.application.Application;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.Scene;
6 | import javafx.scene.layout.AnchorPane;
7 | import javafx.stage.Stage;
8 |
9 | public class Sample extends Application {
10 |
11 | public void start(Stage stage) throws Exception {
12 | FXMLLoader loader = new FXMLLoader();
13 | loader.setLocation(getClass().getResource("/sample/test.fxml"));
14 |
15 | AnchorPane root = loader.load();
16 | Scene scene = new Scene(root, 400, 200);
17 |
18 | stage.setScene(scene);
19 | stage.show();
20 | }
21 |
22 | public static void main(String[] args) {
23 | launch(args);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/sample/test.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/test/Demo.java:
--------------------------------------------------------------------------------
1 |
2 | package test;
3 |
4 | import app.util.TaskBarProgressbar;
5 | import javafx.application.Application;
6 | import javafx.scene.Scene;
7 | import javafx.scene.control.Button;
8 | import javafx.scene.control.RadioButton;
9 | import javafx.scene.control.Slider;
10 | import javafx.scene.control.ToggleGroup;
11 | import javafx.scene.layout.StackPane;
12 | import javafx.scene.layout.VBox;
13 | import javafx.stage.Stage;
14 |
15 |
16 | public class Demo extends Application{
17 | public static void main(String[] args) {
18 | launch(args);
19 | }
20 |
21 | private TaskBarProgressbar.TaskBarProgressbarType actualSelectedType;
22 |
23 | @Override
24 | public void start(Stage primaryStage) throws Exception {
25 |
26 | TaskBarProgressbar taskProgressbar = new TaskBarProgressbar(primaryStage);
27 |
28 | Button btn = new Button("Stop");
29 | btn.setOnAction((event) -> {
30 | taskProgressbar.stopProgress();
31 | });
32 |
33 | primaryStage.setScene(new Scene(new StackPane(btn)));
34 | primaryStage.show();
35 |
36 | taskProgressbar.showIndeterminateProgress();
37 |
38 |
39 | Stage stage = new Stage();
40 |
41 | TaskBarProgressbar taskbarProgressbar2 = new TaskBarProgressbar(stage);
42 |
43 | Slider slider = new Slider(0, 100, 0);
44 |
45 | RadioButton paused = new RadioButton("Paused");
46 | RadioButton normal = new RadioButton("Normal");
47 | RadioButton error = new RadioButton("Error");
48 |
49 | paused.selectedProperty().addListener((observable, oldValue, newValue) -> {
50 | if (newValue)
51 | actualSelectedType = TaskBarProgressbar.TaskBarProgressbarType.PAUSED;
52 | });
53 |
54 | normal.selectedProperty().addListener((observable, oldValue, newValue) -> {
55 | if (newValue)
56 | actualSelectedType = TaskBarProgressbar.TaskBarProgressbarType.NORMAL;
57 | });
58 |
59 | error.selectedProperty().addListener((observable, oldValue, newValue) -> {
60 | if (newValue)
61 | actualSelectedType = TaskBarProgressbar.TaskBarProgressbarType.ERROR;
62 | });
63 |
64 | normal.setSelected(true);
65 |
66 | ToggleGroup tg = new ToggleGroup();
67 | tg.getToggles().addAll(paused, error, normal);
68 |
69 | VBox vBox = new VBox(slider, paused, normal, error);
70 |
71 | slider.valueProperty().addListener((observable, oldValue, newValue) -> {
72 | taskbarProgressbar2.showOtherProgress((long)slider.getValue(), (long) slider.getMax(), actualSelectedType);
73 | });
74 |
75 | stage.setScene(new Scene(vBox));
76 | stage.show();
77 |
78 |
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/JavaStringFormat.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | public class JavaStringFormat {
4 | public static void main(String[] args) {
5 | String minuteFormat = "Text'%s' could not be parsed:valid values is 0-59.";
6 | String s = String.format(minuteFormat, minuteFormat);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/test/LocalTimeTest.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import java.time.LocalTime;
4 |
5 | public class LocalTimeTest {
6 | public static void main(String[] args) {
7 | LocalTime localTime = LocalTime.now();
8 |
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/test/OldPlay.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import app.Main;
4 | import javafx.scene.media.Media;
5 | import javafx.scene.media.MediaPlayer;
6 |
7 | import java.io.File;
8 |
9 | public class OldPlay {
10 |
11 | public static void main(String[] args) {
12 | Main.WORK_DURATION_MP3_PLAYER.repeatPlayInNewThread();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/test/PathGetMain.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URL;
6 |
7 |
8 | public class PathGetMain {
9 |
10 | public static File getResFile() throws Exception {
11 | File resFile = new File("res");
12 | boolean resDirIsInWorkDir = resFile.exists() & resFile.isDirectory();
13 | if(resDirIsInWorkDir)
14 | return resFile;
15 | else{
16 | String path = PathGetMain.class.getResource("/").getFile();
17 | resFile = new File(path);
18 | boolean resDirIsInJarDir = resFile.exists() & resFile.isDirectory();
19 | if (resDirIsInJarDir) {
20 | return resFile;
21 | } else {
22 | return null;
23 | // throw new IOException("res file is not found !");
24 | }
25 | }
26 |
27 | }
28 |
29 | public static void main(String[] args) {
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/test/newPlay.java:
--------------------------------------------------------------------------------
1 | package test;
2 | import javafx.scene.media.Media;
3 | import javafx.scene.media.MediaPlayer;
4 |
5 | import java.io.File;
6 | public class newPlay {
7 | public static void main(String[] args) {
8 | String musicFile = "D:\\GitRepository\\Java\\MyTomato\\res\\sound\\bgm_Ticking.mp3"; // For example
9 | Media sound = new Media(new File(musicFile).toURI().toString());
10 | MediaPlayer mediaPlayer = new MediaPlayer(sound);
11 | mediaPlayer.play();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------