├── settings.gradle
├── doc
├── config.png
└── usage.gif
├── .gitignore
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── src
├── main
│ ├── resources
│ │ ├── pictures
│ │ │ ├── 1.jpg
│ │ │ ├── 10.jpg
│ │ │ ├── 2.jpg
│ │ │ ├── 3.jpg
│ │ │ ├── 4.jpg
│ │ │ ├── 5.jpg
│ │ │ ├── 6.jpg
│ │ │ ├── 7.jpg
│ │ │ ├── 8.jpg
│ │ │ └── 9.jpg
│ │ ├── i18n
│ │ │ ├── templates.properties
│ │ │ └── templates_zh_CN.properties
│ │ └── META-INF
│ │ │ └── plugin.xml
│ └── java
│ │ └── cn
│ │ └── fantasticmao
│ │ └── ycy
│ │ └── intellij
│ │ └── plugin
│ │ ├── remind
│ │ ├── ReminderComponent.java
│ │ ├── package-info.java
│ │ ├── ReminderComponentImpl.java
│ │ ├── PictureManager.java
│ │ ├── OpenPictureAction.java
│ │ ├── OpenPictureConsumer.java
│ │ ├── ReminderTask.java
│ │ └── ReminderStrategy.java
│ │ ├── GlobalConfig.java
│ │ └── config
│ │ ├── package-info.java
│ │ ├── ConfigService.java
│ │ ├── DefaultConfig.java
│ │ ├── ConfigServiceImpl.java
│ │ ├── PluginSettingConfig.java
│ │ ├── I18nBundle.java
│ │ ├── PluginSettingPage.java
│ │ ├── PluginSettingForm.form
│ │ ├── ConfigState.java
│ │ ├── PluginSettingForm.java
│ │ └── PluginSettingPictureUrlTable.java
└── test
│ └── java
│ └── cn
│ └── fantasticmao
│ └── ycy
│ └── intellij
│ └── plugin
│ ├── remind
│ ├── OpenPictureConsumerTest.java
│ └── PictureManagerTest.java
│ └── config
│ ├── ConfigServiceTest.java
│ └── I18nBundleTest.java
├── .editorconfig
├── bin
└── javadoc.sh
├── README_ZH.md
├── CHANGELOG.md
├── README.md
├── LICENSE
├── gradlew.bat
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'ycy-intellij-plugin'
--------------------------------------------------------------------------------
/doc/config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/doc/config.png
--------------------------------------------------------------------------------
/doc/usage.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/doc/usage.gif
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .gradle/
3 |
4 | build/
5 | out/
6 | javadoc/
7 |
8 | *.iml
9 | *.ipr
10 | *.iws
11 |
12 | .DS_Store
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/src/main/resources/pictures/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/1.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/10.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/2.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/3.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/4.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/5.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/6.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/7.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/8.jpg
--------------------------------------------------------------------------------
/src/main/resources/pictures/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fantasticmao/ycy-intellij-plugin/HEAD/src/main/resources/pictures/9.jpg
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | max_line_length = 100
10 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/remind/ReminderComponent.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | /**
4 | * 定时提醒组件
5 | *
6 | *
依赖于 Intellij Platform 组件实现
7 | *
8 | * @author fantasticmao
9 | * @version 1.0
10 | * @since 2019-04-03
11 | */
12 | public interface ReminderComponent {
13 | }
--------------------------------------------------------------------------------
/bin/javadoc.sh:
--------------------------------------------------------------------------------
1 | #/bin/bash
2 | # 请在 ycy-intellij-plugin 项目的根目录下执行脚本
3 |
4 | JAVADOC_HOME='./javadoc'
5 |
6 | if [ -d $JAVADOC_HOME ]; then
7 | rm -rf $JAVADOC_HOME
8 | echo "rm -rf $JAVADOC_HOME"
9 | fi
10 |
11 | javadoc -sourcepath './src/main/java' -d './javadoc' -charset 'utf-8' -private -author -version -subpackages 'cn.fantasticmao.ycy'
12 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/remind/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 定时提醒相关功能
3 | *
4 | * @see cn.fantasticmao.ycy.intellij.plugin.remind.ReminderComponent 定时提醒组件
5 | * @see cn.fantasticmao.ycy.intellij.plugin.remind.ReminderTask 定时提醒任务
6 | * @see cn.fantasticmao.ycy.intellij.plugin.remind.ReminderStrategy 打开图片的策略模式
7 | * @see cn.fantasticmao.ycy.intellij.plugin.remind.PictureManager 图片管理器
8 | * @see cn.fantasticmao.ycy.intellij.plugin.remind.OpenPictureAction 打开图片的动作事件
9 | * @see cn.fantasticmao.ycy.intellij.plugin.remind.OpenPictureConsumer 打开图片的具体实现
10 | */
11 | package cn.fantasticmao.ycy.intellij.plugin.remind;
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/GlobalConfig.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin;
2 |
3 | import cn.fantasticmao.ycy.intellij.plugin.config.I18nBundle;
4 |
5 | /**
6 | * 全局配置接口
7 | *
8 | * @author fantasticmao
9 | * @since 2019-04-05
10 | */
11 | public interface GlobalConfig {
12 |
13 | /**
14 | * 插件标识
15 | *
16 | * 与 {@code /META-INF/plugin.xml} 中 {@code } 内容一致
17 | */
18 | String PLUGIN_ID = "cn.fantasticmao.ycy.intellij.plugin";
19 |
20 | /**
21 | * 插件名称
22 | */
23 | String PLUGIN_NAME = I18nBundle.message(I18nBundle.Key.PLUGIN_NAME);
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/cn/fantasticmao/ycy/intellij/plugin/remind/OpenPictureConsumerTest.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | import com.intellij.ide.DataManager;
4 | import com.intellij.testFramework.fixtures.BasePlatformTestCase;
5 |
6 | /**
7 | * OpenPictureConsumerTest
8 | *
9 | * @author fantasticmao
10 | * @since 2019-04-17
11 | */
12 | public class OpenPictureConsumerTest extends BasePlatformTestCase {
13 |
14 | public void testAccept() {
15 | DataManager.getInstance().getDataContextFromFocusAsync()
16 | .onSuccess((dataContext -> new OpenPictureConsumer().accept(dataContext)));
17 | }
18 | }
--------------------------------------------------------------------------------
/src/test/java/cn/fantasticmao/ycy/intellij/plugin/remind/PictureManagerTest.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | import com.intellij.testFramework.fixtures.BasePlatformTestCase;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * PictureManagerTest
9 | *
10 | * @author fantasticmao
11 | * @since 2019-04-17
12 | */
13 | public class PictureManagerTest extends BasePlatformTestCase {
14 |
15 | public void testGetDefaultPictureList() {
16 | List pictureList = PictureManager.getDefaultPictureList();
17 | assertNotEmpty(pictureList);
18 | assertEquals(10, pictureList.size());
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/src/test/java/cn/fantasticmao/ycy/intellij/plugin/config/ConfigServiceTest.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import com.intellij.testFramework.fixtures.BasePlatformTestCase;
4 |
5 | /**
6 | * ConfigServiceTest
7 | *
8 | * @author fantasticmao
9 | * @since 2021-02-06
10 | */
11 | public class ConfigServiceTest extends BasePlatformTestCase {
12 |
13 | public void testGetState() {
14 | ConfigState state = ConfigService.getInstance().getState();
15 | assertNotNull(state);
16 | Integer expected = DefaultConfig.REMIND_MODE;
17 | assertEquals(expected, state.getRemindMode());
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/test/java/cn/fantasticmao/ycy/intellij/plugin/config/I18nBundleTest.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import com.intellij.testFramework.fixtures.BasePlatformTestCase;
4 |
5 | /**
6 | * I18nBundleTest
7 | *
8 | * @author fantasticmao
9 | * @since 2021-02-06
10 | */
11 | public class I18nBundleTest extends BasePlatformTestCase {
12 |
13 | public void testMessage() {
14 | String pluginName = I18nBundle.message(I18nBundle.Key.PLUGIN_NAME);
15 | assertNotNull(pluginName);
16 | String expected = "Programmer Motivator: Chaoyue Yang";
17 | assertEquals(expected, pluginName);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 插件配置相关功能
3 | *
4 | * @see cn.fantasticmao.ycy.intellij.plugin.config.PluginSettingPage 插件设置页面
5 | * @see cn.fantasticmao.ycy.intellij.plugin.config.PluginSettingForm 插件设置页面的表单
6 | * @see cn.fantasticmao.ycy.intellij.plugin.config.PluginSettingPictureUrlTable 插件设置页面的表格
7 | * @see cn.fantasticmao.ycy.intellij.plugin.config.PluginSettingConfig 插件设置页面的配置参数
8 | * @see cn.fantasticmao.ycy.intellij.plugin.config.ConfigService 配置参数持久化服务
9 | * @see cn.fantasticmao.ycy.intellij.plugin.config.ConfigState 配置状态类
10 | * @see cn.fantasticmao.ycy.intellij.plugin.config.DefaultConfig 默认配置接口
11 | */
12 | package cn.fantasticmao.ycy.intellij.plugin.config;
--------------------------------------------------------------------------------
/README_ZH.md:
--------------------------------------------------------------------------------
1 | # 超越鼓励师
2 |
3 | [](https://plugins.jetbrains.com/plugin/12204-programmer-motivator-chaoyue-yang/)
4 | [](https://github.com/fantasticmao/ycy-intellij-plugin/blob/master/LICENSE)
5 |
6 | > [Visual Studio Code 版本](https://github.com/formulahendry/vscode-ycy)
7 |
8 | README [English](README.md) | [中文](README_ZH.md)
9 |
10 | 写代码不再孤单,有杨超越与你同在!
11 |
12 | 在 IntelliJ IDEA 中连续编写代码一小时,会有杨超越提醒你该休息啦~
13 |
14 | ## 在线演示
15 |
16 | 
17 |
18 | 
19 |
20 | ## 特性列表
21 |
22 | - [x] 定时提醒
23 | - [x] 可配置提醒方式
24 | - [x] 可配置提醒图片
25 | - [x] 可配置提醒间隔
26 | - [x] 可配置提醒文案
27 | - [x] 可配置多张图片
28 | - [x] 支持国际化和本地化
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/remind/ReminderComponentImpl.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 | import com.intellij.openapi.diagnostic.Logger;
5 |
6 | /**
7 | * {@link ReminderComponent} 实现类
8 | *
9 | * @author fantasticmao
10 | * @version 1.0
11 | * @since 2019-04-03
12 | */
13 | public class ReminderComponentImpl implements ReminderComponent {
14 | private static final Logger LOG = Logger.getInstance(ReminderComponentImpl.class);
15 |
16 | public ReminderComponentImpl() {
17 | ApplicationManager.getApplication().invokeLater(() -> {
18 | LOG.info("=== Programmer Motivator: Chaoyue Yang(超越鼓励师) ===");
19 |
20 | ReminderTask.init();
21 | LOG.info("start scheduled reminder task");
22 | });
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.8 - 2022-12-18
4 |
5 | - Adapt to IntelliJ Platform 2022.3
6 | - Upgrade to Gradle Wrapper 7.6
7 |
8 | ## 1.7 - 2021-10-17
9 |
10 | - Adapt to IntelliJ Platform 2020.3
11 | - Upgrade to Gradle Wrapper 7.2
12 |
13 | ## 1.6 - 2021-02-06
14 |
15 | - Support internationalization and localization
16 | - Support plugin disability
17 |
18 | ## 1.5 - 2020-05-07
19 |
20 | - Bugfix [#27](https://github.com/fantasticmao/ycy-intellij-plugin/issues/27)
21 |
22 | ## 1.4 - 2019-05-24
23 |
24 | - Bugfix [#20](https://github.com/fantasticmao/ycy-intellij-plugin/pull/20)
25 |
26 | ## 1.3 - 2019-05-15
27 |
28 | - Configurable multiple pictures
29 |
30 | ## 1.2 - 2019-04-19
31 |
32 | - Configurable reminder mode
33 | - Configurable reminder picture
34 | - Configurable reminder duration
35 | - Configurable reminder content
36 | - Bugfix [#9](https://github.com/fantasticmao/ycy-intellij-plugin/issues/9)
37 |
38 | ## 1.1 - 2019-04-09
39 |
40 | - Bugfix [#4](https://github.com/fantasticmao/ycy-intellij-plugin/issues/4)
41 |
42 | ## 1.0 - 2019-04-05
43 |
44 | - Initial release
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Programmer Motivator: Chaoyue Yang
2 |
3 | [](https://plugins.jetbrains.com/plugin/12204-programmer-motivator-chaoyue-yang/)
4 | [](https://github.com/fantasticmao/ycy-intellij-plugin/blob/master/LICENSE)
5 |
6 | > [Visual Studio Code Version](https://github.com/formulahendry/vscode-ycy)
7 |
8 | README [English](README.md) | [中文](README_ZH.md)
9 |
10 | Coding won't be lonely anymore with Chaoyue Yang's accompany!
11 |
12 | Chaoyue Yang will remind you to have a rest when you have been working for an hour on IntelliJ IDEA~
13 |
14 | ## Demos
15 |
16 | 
17 |
18 | 
19 |
20 | ## Features
21 |
22 | - [x] Scheduled reminder
23 | - [x] Configurable reminder mode
24 | - [x] Configurable reminder picture
25 | - [x] Configurable reminder duration
26 | - [x] Configurable reminder content
27 | - [x] Configurable multiple pictures
28 | - [x] Support internationalization and localization
--------------------------------------------------------------------------------
/src/main/resources/i18n/templates.properties:
--------------------------------------------------------------------------------
1 | # Plugin
2 | plugin.name=Programmer Motivator: Chaoyue Yang
3 | # ConfigPage - Keys
4 | config.label.remind.mode.options=Remind Mode:
5 | config.label.picture.url.list=Picture URLs:
6 | config.label.duration.in.minutes=Duration in Minutes:
7 | config.label.notify.content.title=Notify Content - Title:
8 | config.label.notify.content.body=Notify Content - Body:
9 | config.label.notify.content.action=Notify Content - Action:
10 | config.label.disabled=Disabled:
11 | # ConfigPage - Values
12 | config.value.remind.mode.direct=Show Pictures
13 | config.value.remind.mode.indirect=Notification -> Show Pictures
14 | config.value.notify.content.title=Chaoyue
15 | config.value.notify.content.body=Sweetie, you have been working for a long time, it is time to have a rest now~
16 | config.value.notify.content.action=Let's chill out
17 | # ConfigPage - Table
18 | config.table.column.0=Index
19 | config.table.column.1=Picture URL
20 | # Picture Chooser
21 | picture.chooser.title=Select Pictures
22 | picture.chooser.error.format=Please choose a picture that is a format of: {0}
23 | # Symbol
24 | symbol.delimiter=,\u0020
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 fantasticmao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/ConfigService.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 | import com.intellij.openapi.components.PersistentStateComponent;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | /**
8 | * 配置参数持久化服务
9 | *
10 | * 依赖于 Intellij Platform 的 {@link PersistentStateComponent} 组件实现
11 | *
12 | * @author fantasticmao
13 | * @version 1.2
14 | * @see SDK DevGuide
15 | * @since 2019-04-14
16 | */
17 | public interface ConfigService extends PersistentStateComponent {
18 |
19 | /**
20 | * 由 Intellij Platform 保证的单例模式
21 | */
22 | static ConfigService getInstance() {
23 | return ApplicationManager.getApplication().getService(ConfigService.class);
24 | }
25 |
26 | /**
27 | * 获取插件配置
28 | *
29 | * @return {@link ConfigState}
30 | */
31 | @NotNull
32 | ConfigState getState();
33 |
34 | /**
35 | * 修改插件配置
36 | *
37 | * @param state 将被保存的新配置对象
38 | */
39 | void setState(@NotNull ConfigState state);
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/resources/i18n/templates_zh_CN.properties:
--------------------------------------------------------------------------------
1 | # Plugin
2 | plugin.name=\u8d85\u8d8a\u9f13\u52b1\u5e08
3 | # ConfigPage - Keys
4 | config.label.remind.mode.options=\u63d0\u9192\u65b9\u5f0f\uff1a
5 | config.label.picture.url.list=\u56fe\u7247 URL\uff1a
6 | config.label.duration.in.minutes=\u95f4\u9694\u65f6\u95f4\uff08\u5206\u949f\uff09\uff1a
7 | config.label.notify.content.title=\u901a\u77e5\u6587\u6848 - \u6807\u9898\uff1a
8 | config.label.notify.content.body=\u901a\u77e5\u6587\u6848 - \u5185\u5bb9\uff1a
9 | config.label.notify.content.action=\u901a\u77e5\u6587\u6848 - \u6309\u94ae\uff1a
10 | config.label.disabled=\u7981\u7528\uff1a
11 | # ConfigPage - Values
12 | config.value.remind.mode.direct=\u6253\u5f00\u56fe\u7247
13 | config.value.remind.mode.indirect=\u6d88\u606f\u901a\u77e5 -> \u6253\u5f00\u56fe\u7247
14 | config.value.notify.content.title=\u8d85\u8d8a\u59b9\u59b9
15 | config.value.notify.content.body=\u5c0f\u54e5\u54e5\uff0c\u4ee3\u7801\u5199\u597d\u4e45\u4e86\uff0c\u8be5\u4f11\u606f\u4e00\u4e0b\u5566\uff5e
16 | config.value.notify.content.action=\u6765\u653e\u677e\u4e00\u4e0b
17 | # ConfigPage - Table
18 | config.table.column.0=\u5e8f\u53f7
19 | config.table.column.1=\u56fe\u7247 URL
20 | # Picture Chooser
21 | picture.chooser.title=\u9009\u62e9\u56fe\u7247
22 | picture.chooser.error.format=\u8bf7\u786e\u8ba4\u9009\u62e9\u7684\u56fe\u7247\u683c\u5f0f\u662f\uff1a{0}
23 | # Symbol
24 | symbol.delimiter=\u3001
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/DefaultConfig.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import cn.fantasticmao.ycy.intellij.plugin.remind.PictureManager;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | /**
10 | * 默认配置接口
11 | *
12 | * @author fantasticmao
13 | * @version 1.0
14 | * @since 2019-04-14
15 | */
16 | public interface DefaultConfig {
17 |
18 | /**
19 | * 默认提醒方式
20 | */
21 | Integer REMIND_MODE = ConfigState.RemindModeEnum.INDIRECT.index;
22 |
23 | /**
24 | * 默认提醒图片列表
25 | */
26 | // Immutable
27 | List REMIND_PICTURE_LIST = Collections.unmodifiableList(PictureManager.getDefaultPictureList());
28 |
29 | /**
30 | * 默认提醒间隔时间
31 | */
32 | Integer DURATION_IN_MINUTES = (int) TimeUnit.HOURS.toMinutes(1);
33 |
34 | /**
35 | * 默认通知文案的标题
36 | */
37 | String NOTIFY_TITLE = I18nBundle.message(I18nBundle.Key.CONFIG_VALUE_NOTIFY_CONTENT_TITLE);
38 |
39 | /**
40 | * 默认通知文案的内容
41 | */
42 | String NOTIFY_BODY = I18nBundle.message(I18nBundle.Key.CONFIG_VALUE_NOTIFY_CONTENT_BODY);
43 |
44 | /**
45 | * 默认通知文案的按钮
46 | */
47 | String NOTIFY_ACTION = I18nBundle.message(I18nBundle.Key.CONFIG_VALUE_NOTIFY_CONTENT_ACTION);
48 |
49 | /**
50 | * 默认禁用状态
51 | */
52 | Boolean DISABLED = Boolean.FALSE;
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/remind/PictureManager.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | import com.intellij.openapi.vfs.VfsUtil;
4 |
5 | import java.net.URL;
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | /**
11 | * 图片管理器
12 | *
13 | * @author fantasticmao
14 | * @version 1.0
15 | * @since 2019-04-05
16 | */
17 | public class PictureManager {
18 | private static final PictureManager INSTANCE = new PictureManager();
19 |
20 | /**
21 | * 默认图片列表
22 | */
23 | private final List defaultPictureUrlList;
24 |
25 | private PictureManager() {
26 | List pictureUrlList = new ArrayList<>(10);
27 | for (int i = 1; i <= 10; i++) {
28 | // 从插件 jar 中获取默认图片列表,
29 | // 默认图片地址是 ${pluginPath}/ycy-intellij-plugin.jar!/pictures/1.jpg
30 | URL pictureUrl = getClass().getResource("/pictures/" + i + ".jpg");
31 | pictureUrlList.add(pictureUrl);
32 | }
33 | this.defaultPictureUrlList = Collections.unmodifiableList(pictureUrlList);
34 | }
35 |
36 | /**
37 | * 获取默认图片列表
38 | *
39 | * @return {@link java.lang.String}
40 | * @see com.intellij.openapi.vfs.VfsUtil#convertFromUrl(URL)
41 | * @see com.intellij.openapi.vfs.VirtualFileManager#findFileByUrl(String)
42 | */
43 | public static List getDefaultPictureList() {
44 | return INSTANCE.defaultPictureUrlList.stream()
45 | .map(VfsUtil::convertFromUrl)
46 | .toList();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/ConfigServiceImpl.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import com.intellij.openapi.components.State;
4 | import com.intellij.openapi.components.Storage;
5 | import com.intellij.util.xmlb.XmlSerializerUtil;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | /**
9 | * {@link ConfigService} 实现类
10 | *
11 | * @author fantasticmao
12 | * @version 1.2
13 | * @see SDK DevGuide
14 | * @since 2019-04-14
15 | */
16 | @State(name = "ycyReminder", storages = @Storage("ycyReminder.xml"))
17 | public class ConfigServiceImpl implements ConfigService {
18 | private ConfigState configState;
19 |
20 | public ConfigServiceImpl() {
21 | this.configState = new ConfigState();
22 | }
23 |
24 | /**
25 | * {@inheritDoc}
26 | *
27 | * 若用户是第一次开启插件时,则使用默认配置对象 {@code new ConfigState()},
28 | * 否则使用从 {@code ycyReminder.xml} 配置文件中解析生成的配置对象
29 | */
30 | @NotNull
31 | @Override
32 | public ConfigState getState() {
33 | return this.configState;
34 | }
35 |
36 | /**
37 | * {@inheritDoc}
38 | *
39 | * 调用此方法,会将配置数据持久化至 {@code ycyReminder.xml} 配置文件中
40 | */
41 | @Override
42 | public void setState(@NotNull ConfigState state) {
43 | this.configState = state;
44 | }
45 |
46 | /**
47 | * {@inheritDoc}
48 | */
49 | @Override
50 | public void loadState(@NotNull ConfigState state) {
51 | XmlSerializerUtil.copyBean(state, this.configState);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/remind/OpenPictureAction.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | import com.intellij.ide.DataManager;
4 | import com.intellij.notification.Notification;
5 | import com.intellij.openapi.actionSystem.AnAction;
6 | import com.intellij.openapi.actionSystem.AnActionEvent;
7 | import com.intellij.openapi.diagnostic.Logger;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | /**
11 | * 打开图片的动作事件
12 | *
13 | * @author fantasticmao
14 | * @version 1.0
15 | * @since 2019-04-04
16 | */
17 | class OpenPictureAction extends AnAction {
18 | private static final Logger LOG = Logger.getInstance(OpenPictureAction.class);
19 |
20 | private final Notification notification;
21 |
22 | /**
23 | * 构造方法
24 | *
25 | * @param text 通知中可点击的按钮的文案
26 | * @param notification 通知对象,在点击按钮之后需要调用 {@link Notification#expire()}
27 | */
28 | OpenPictureAction(String text, Notification notification) {
29 | super(text);
30 | this.notification = notification;
31 | }
32 |
33 | /**
34 | * {@inheritDoc}
35 | */
36 | @Override
37 | public void actionPerformed(@NotNull AnActionEvent event) {
38 | /*
39 | * at version 1.2 fix a bug: 2017.1 版本无法打开图片问题
40 | * see https://github.com/fantasticmao/ycy-intellij-plugin/issues/9
41 | */
42 | DataManager.getInstance().getDataContextFromFocusAsync()
43 | .onSuccess(dataContext -> new OpenPictureConsumer().accept(dataContext))
44 | .onError(LOG::error);
45 |
46 | // 使打开图片按钮失效,避免重复点击
47 | notification.expire();
48 | LOG.info("notification action has been expired");
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/PluginSettingConfig.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import com.intellij.openapi.fileChooser.FileChooserDescriptor;
4 | import com.intellij.openapi.vfs.VirtualFile;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | import java.util.Arrays;
8 | import java.util.List;
9 |
10 | /**
11 | * 插件设置页面的配置参数
12 | *
13 | * @author fantasticmao
14 | * @version 1.2
15 | * @since 2019-04-18
16 | */
17 | public interface PluginSettingConfig {
18 |
19 | /**
20 | * 支持的图片格式
21 | */
22 | List PICTURE_EXTENSION_LIST = Arrays.asList("jpg", "jpeg", "png", "bmp", "gif");
23 |
24 | /**
25 | * 图片选择器的描述
26 | */
27 | FileChooserDescriptor PICTURE_FILE_CHOOSER_DESCRIPTOR = new PictureChooserDescriptor();
28 |
29 | class PictureChooserDescriptor extends FileChooserDescriptor {
30 |
31 | public PictureChooserDescriptor() {
32 | super(true, false, false, false, false, true);
33 | super.withFileFilter(file ->
34 | file.getExtension() != null && PICTURE_EXTENSION_LIST.contains(file.getExtension().toLowerCase())
35 | ).withTitle(I18nBundle.message(I18nBundle.Key.PICTURE_CHOOSER_TITLE));
36 | }
37 |
38 | @Override
39 | public void validateSelectedFiles(VirtualFile @NotNull [] files) throws Exception {
40 | super.validateSelectedFiles(files);
41 | for (VirtualFile file : files) {
42 | if (file.getExtension() == null || !PICTURE_EXTENSION_LIST.contains(file.getExtension().toLowerCase())) {
43 | String delimiter = I18nBundle.message(I18nBundle.Key.SYMBOL_DELIMITER);
44 | String message = I18nBundle.message(I18nBundle.Key.PICTURE_CHOOSER_ERROR_FORMAT,
45 | String.join(delimiter, PICTURE_EXTENSION_LIST));
46 | throw new IllegalArgumentException(message);
47 | }
48 | }
49 | }
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | cn.fantasticmao.ycy.intellij.plugin
3 | Programmer Motivator: Chaoyue Yang
4 | 1.8
5 |
6 |
7 |
8 |
9 | fantasticmao
10 |
11 |
13 | Chaoyue Yang will remind you to have a rest when you have been working for an hour on IntelliJ IDEA~
14 |
15 | 写代码不再孤单,有杨超越与你同在!
16 | 在 IntelliJ IDEA 中连续编写代码一小时,会有杨超越提醒你该休息啦~
17 |
18 | GitHub: https://github.com/fantasticmao/ycy-intellij-plugin
19 | ]]>
20 |
21 |
23 | Adapt to IntelliJ Platform 2022.3
24 | Upgrade to Gradle Wrapper 7.6
25 |
26 | ]]>
27 |
28 | com.intellij.modules.platform
29 |
30 |
31 |
32 |
33 | cn.fantasticmao.ycy.intellij.plugin.remind.ReminderComponent
34 |
35 |
36 |
37 | cn.fantasticmao.ycy.intellij.plugin.remind.ReminderComponentImpl
38 |
39 |
40 |
41 |
42 |
43 |
45 |
46 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/I18nBundle.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import com.intellij.AbstractBundle;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | /**
7 | * 国际化服务
8 | *
9 | * 依赖于 {@link com.intellij.AbstractBundle} 组件实现
10 | *
11 | * @author fantasticmao
12 | * @version 1.6
13 | * @since 2021-02-01
14 | */
15 | public class I18nBundle extends AbstractBundle {
16 | private static final I18nBundle INSTANCE = new I18nBundle();
17 |
18 | private I18nBundle() {
19 | super("i18n.templates");
20 | }
21 |
22 | /**
23 | * 获取国际化值
24 | *
25 | * @param key {@code template.properties} 中的键
26 | * @param params {@code template.properties} 中的值占位符
27 | * @return 国际化值
28 | * @see Key
29 | */
30 | public static String message(@NotNull Key key, @NotNull Object... params) {
31 | return INSTANCE.getMessage(key.value, params);
32 | }
33 |
34 | public enum Key {
35 | // 插件
36 | PLUGIN_NAME("plugin.name"),
37 |
38 | // 配置界面 - 表单键
39 | CONFIG_LABEL_REMIND_MODE_OPTIONS("config.label.remind.mode.options"),
40 | CONFIG_LABEL_PICTURE_URL_LIST("config.label.picture.url.list"),
41 | CONFIG_LABEL_DURATION_IN_MINUTES("config.label.duration.in.minutes"),
42 | CONFIG_LABEL_NOTIFY_CONTENT_TITLE("config.label.notify.content.title"),
43 | CONFIG_LABEL_NOTIFY_CONTENT_BODY("config.label.notify.content.body"),
44 | CONFIG_LABEL_NOTIFY_CONTENT_ACTION("config.label.notify.content.action"),
45 | CONFIG_LABEL_DISABLED("config.label.disabled"),
46 |
47 | // 配置界面 - 表单值
48 | CONFIG_VALUE_REMIND_MODE_DIRECT("config.value.remind.mode.direct"),
49 | CONFIG_VALUE_REMIND_MODE_INDIRECT("config.value.remind.mode.indirect"),
50 | CONFIG_VALUE_NOTIFY_CONTENT_TITLE("config.value.notify.content.title"),
51 | CONFIG_VALUE_NOTIFY_CONTENT_BODY("config.value.notify.content.body"),
52 | CONFIG_VALUE_NOTIFY_CONTENT_ACTION("config.value.notify.content.action"),
53 |
54 | // 配置界面 - 表格
55 | CONFIG_TABLE_COLUMN_0("config.table.column.0"),
56 | CONFIG_TABLE_COLUMN_1("config.table.column.1"),
57 |
58 | // 图片选择器
59 | PICTURE_CHOOSER_TITLE("picture.chooser.title"),
60 | PICTURE_CHOOSER_ERROR_FORMAT("picture.chooser.error.format"),
61 |
62 | // 符号
63 | SYMBOL_DELIMITER("symbol.delimiter");
64 |
65 | final String value;
66 |
67 | Key(String value) {
68 | this.value = value;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/remind/OpenPictureConsumer.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | import cn.fantasticmao.ycy.intellij.plugin.config.ConfigService;
4 | import cn.fantasticmao.ycy.intellij.plugin.config.ConfigState;
5 | import com.intellij.openapi.actionSystem.DataContext;
6 | import com.intellij.openapi.actionSystem.PlatformDataKeys;
7 | import com.intellij.openapi.diagnostic.Logger;
8 | import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
9 | import com.intellij.openapi.fileEditor.impl.EditorWindow;
10 | import com.intellij.openapi.project.Project;
11 | import com.intellij.openapi.vfs.VirtualFile;
12 | import com.intellij.openapi.vfs.VirtualFileManager;
13 |
14 | import javax.swing.*;
15 | import java.util.function.Consumer;
16 |
17 | /**
18 | * 打开图片的具体实现
19 | *
20 | * @author fantasticmao
21 | * @version 1.2
22 | * @since 2019-04-17
23 | */
24 | public class OpenPictureConsumer implements Consumer {
25 | private static final Logger LOG = Logger.getInstance(OpenPictureConsumer.class);
26 |
27 | public OpenPictureConsumer() {
28 | }
29 |
30 | @Override
31 | public void accept(DataContext dataContext) {
32 | // 1. 获取 IDEA 正在使用的 Project
33 | Project currentProject = dataContext.getData(PlatformDataKeys.PROJECT);
34 | if (currentProject == null) {
35 | LOG.warn("currentProject cannot be null");
36 | return;
37 | }
38 |
39 | // 2. 获取即将用于展示的图片
40 | ConfigState configState = ConfigService.getInstance().getState();
41 | String pictureUrl = configState.getRandomRemindPicture();
42 | VirtualFile picture = VirtualFileManager.getInstance().findFileByUrl(pictureUrl);
43 | if (picture == null) {
44 | LOG.error("find picture by URL error: " + pictureUrl);
45 | return;
46 | }
47 |
48 | // 3. 获取当前 Project 中,正在使用的 EditorWindow
49 | FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(currentProject);
50 | EditorWindow currentWindow = fileEditorManager.getCurrentWindow();
51 | if (currentWindow == null || currentWindow.getTabCount() == 0) {
52 | // 3.1 如果没有打开 EditorWindow 或者 EditorWindow 打开的 tab 为零,那就直接打开图片
53 | fileEditorManager.openFile(picture, true);
54 | } else {
55 | // 4 获取下一个 EditorWindow
56 | EditorWindow nextWindow = fileEditorManager.getNextWindow(currentWindow);
57 | if (nextWindow == currentWindow) {
58 | // 4.1 如果下一个 EditorWindow 还是它自己,表示 IDEA 只打开了一个 EditorWindow
59 | // 4.1 那则需要创建一个垂直分屏,再打开图片
60 | currentWindow.split(SwingConstants.VERTICAL, true, picture, true);
61 | } else {
62 | // 4.2 在下一个 EditorWindow 打开图片
63 | fileEditorManager.openFileWithProviders(picture, true, nextWindow);
64 | }
65 | }
66 | LOG.info("the picture has been shown");
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/remind/ReminderTask.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | import cn.fantasticmao.ycy.intellij.plugin.config.ConfigService;
4 | import cn.fantasticmao.ycy.intellij.plugin.config.ConfigState;
5 | import com.intellij.concurrency.JobScheduler;
6 | import com.intellij.openapi.application.ApplicationManager;
7 | import com.intellij.openapi.diagnostic.Logger;
8 |
9 | import java.util.concurrent.ScheduledFuture;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | /**
13 | * 定时提醒任务
14 | *
15 | * @author fantasticmao
16 | * @version 1.2
17 | * @since 2019-04-16
18 | */
19 | public class ReminderTask {
20 | private static final Logger LOG = Logger.getInstance(ReminderTask.class);
21 | private static final ThreadLocal> SCHEDULED_FUTURE_CONTEXT = new ThreadLocal<>();
22 |
23 | /**
24 | * 开启定时提醒任务
25 | */
26 | public static void init() {
27 | restart();
28 | }
29 |
30 | /**
31 | * 重新开启定时提醒任务
32 | */
33 | public static void restart() {
34 | destroy();
35 |
36 | ConfigState configState = ConfigService.getInstance().getState();
37 | ScheduledFuture> scheduledFuture = JobScheduler.getScheduler().scheduleWithFixedDelay(new Reminder(),
38 | configState.getDurationInMinutes(), configState.getDurationInMinutes(), TimeUnit.MINUTES);
39 | // 保存 ScheduledFuture 引用至 ThreadLocal 上下文中,用于后续注销定时任务
40 | SCHEDULED_FUTURE_CONTEXT.set(scheduledFuture);
41 | }
42 |
43 | /**
44 | * 注销定时提醒任务
45 | */
46 | private static void destroy() {
47 | ScheduledFuture> existScheduledFuture = SCHEDULED_FUTURE_CONTEXT.get();
48 | if (existScheduledFuture != null) {
49 | existScheduledFuture.cancel(true);
50 | SCHEDULED_FUTURE_CONTEXT.remove();
51 | } else {
52 | LOG.warn("reminder task has not been started");
53 | }
54 | }
55 |
56 | /**
57 | * 实现定时提醒任务逻辑的线程
58 | *
59 | * @version 1.0
60 | */
61 | static class Reminder implements Runnable {
62 |
63 | @Override
64 | public void run() {
65 | ConfigState configState = ConfigService.getInstance().getState();
66 | if (configState.getDisabled()) {
67 | LOG.warn("reminder task has been disabled");
68 | return;
69 | }
70 |
71 | ConfigState.RemindModeEnum remindMode = ConfigState.RemindModeEnum.valueOf(configState.getRemindMode());
72 | ReminderStrategy reminderStrategy = ReminderStrategy.getRemindStrategy(remindMode);
73 | /*
74 | * 2019-04-18 fix a bug: Access is allowed from event dispatch thread only. according to:
75 | * https://intellij-support.jetbrains.com/hc/en-us/community/posts/206124399-Error-Write-access-is-allowed-from-event-dispatch-thread-only
76 | */
77 | ApplicationManager.getApplication().invokeLater(reminderStrategy::remind);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/remind/ReminderStrategy.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.remind;
2 |
3 | import cn.fantasticmao.ycy.intellij.plugin.config.ConfigService;
4 | import cn.fantasticmao.ycy.intellij.plugin.config.ConfigState;
5 | import com.intellij.ide.DataManager;
6 | import com.intellij.notification.Notification;
7 | import com.intellij.notification.NotificationGroupManager;
8 | import com.intellij.notification.NotificationType;
9 | import com.intellij.notification.Notifications;
10 | import com.intellij.openapi.diagnostic.Logger;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | /**
14 | * 打开图片的策略模式
15 | *
16 | * @author fantasticmao
17 | * @version 1.2
18 | * @since 2019-04-17
19 | */
20 | @FunctionalInterface
21 | public interface ReminderStrategy {
22 | Logger LOG = Logger.getInstance(ReminderStrategy.class);
23 | ReminderStrategy INSTANCE_REMINDER_DIRECT = new ReminderDirect();
24 | ReminderStrategy INSTANCE_REMINDER_INDIRECT = new ReminderIndirect();
25 |
26 | /**
27 | * 策略模式的工厂方法
28 | *
29 | * @see ConfigState.RemindModeEnum
30 | */
31 | @NotNull
32 | static ReminderStrategy getRemindStrategy(ConfigState.RemindModeEnum mode) {
33 | return switch (mode) {
34 | case DIRECT -> INSTANCE_REMINDER_DIRECT;
35 | case INDIRECT -> INSTANCE_REMINDER_INDIRECT;
36 | };
37 | }
38 |
39 | /**
40 | * 使用不同策略打开图片
41 | */
42 | void remind();
43 |
44 | /**
45 | * 直接打开图片
46 | *
47 | * @see ConfigState.RemindModeEnum#DIRECT
48 | */
49 | class ReminderDirect implements ReminderStrategy {
50 |
51 | /**
52 | * {@inheritDoc}
53 | */
54 | @Override
55 | public void remind() {
56 | DataManager.getInstance().getDataContextFromFocusAsync()
57 | .onSuccess(dataContext -> new OpenPictureConsumer().accept(dataContext))
58 | .onError(LOG::error);
59 | }
60 | }
61 |
62 | /**
63 | * 间接打开图片
64 | *
65 | * @see ConfigState.RemindModeEnum#INDIRECT
66 | */
67 | class ReminderIndirect implements ReminderStrategy {
68 | private static final Logger LOG = Logger.getInstance(ReminderIndirect.class);
69 |
70 | /**
71 | * {@inheritDoc}
72 | */
73 | @Override
74 | public void remind() {
75 | ConfigState configState = ConfigService.getInstance().getState();
76 |
77 | /*
78 | * at version 1.7 fix a bug: 2020.3 版本 NotificationGroup. 过时问题
79 | * see https://github.com/fantasticmao/ycy-intellij-plugin/issues/32
80 | * see https://plugins.jetbrains.com/docs/intellij/notifications.html#top-level-notifications-balloons
81 | */
82 | final String groupId = "Plugins Programmer Motivator: Chaoyue Yang";
83 | Notification notification = NotificationGroupManager.getInstance().getNotificationGroup(groupId)
84 | .createNotification(configState.getNotifyTitle(), configState.getNotifyBody(),
85 | NotificationType.INFORMATION);
86 | if (!configState.getRemindPictures().isEmpty()) {
87 | OpenPictureAction openPictureAction = new OpenPictureAction(configState.getNotifyAction(), notification);
88 | notification.addAction(openPictureAction);
89 | }
90 | Notifications.Bus.notify(notification);
91 | LOG.info("notify an info message");
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/PluginSettingPage.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import cn.fantasticmao.ycy.intellij.plugin.GlobalConfig;
4 | import cn.fantasticmao.ycy.intellij.plugin.remind.ReminderTask;
5 | import com.intellij.openapi.diagnostic.Logger;
6 | import com.intellij.openapi.options.SearchableConfigurable;
7 | import org.jetbrains.annotations.Nls;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import javax.swing.*;
12 |
13 | /**
14 | * 插件设置页面
15 | *
16 | * @author fantasticmao
17 | * @version 1.2
18 | * @see 一篇很不错的关于开发 Intellij Plugin 配置页面的博客
19 | * @since 2019-04-12
20 | */
21 | public class PluginSettingPage implements SearchableConfigurable {
22 | private static final Logger LOG = Logger.getInstance(PluginSettingPage.class);
23 | /**
24 | * 插件设置页面的表单
25 | */
26 | private PluginSettingForm form;
27 |
28 | /**
29 | * {@inheritDoc}
30 | */
31 | @NotNull
32 | @Override
33 | public String getId() {
34 | return GlobalConfig.PLUGIN_ID;
35 | }
36 |
37 | /**
38 | * {@inheritDoc}
39 | */
40 | @Nls(capitalization = Nls.Capitalization.Title)
41 | @Override
42 | public String getDisplayName() {
43 | return GlobalConfig.PLUGIN_NAME;
44 | }
45 |
46 | /**
47 | * {@inheritDoc}
48 | */
49 | @Nullable
50 | @Override
51 | public String getHelpTopic() {
52 | return null;
53 | }
54 |
55 | /**
56 | * IDEA 初始化设置页面时,会调用此方法
57 | *
58 | * @return 由 {@code UI Designer} 生成的 {@link PluginSettingForm} 页面
59 | */
60 | @Nullable
61 | @Override
62 | public JComponent createComponent() {
63 | if (this.form == null) {
64 | this.form = new PluginSettingForm();
65 | }
66 | return this.form.getPluginSettingPanel();
67 | }
68 |
69 | /**
70 | * IDEA 初始化设置页面时,判断 "Apply" 按钮是否为可用
71 | *
72 | * @return true 是;false 否
73 | */
74 | @Override
75 | public boolean isModified() {
76 | return this.form != null;
77 | }
78 |
79 | /**
80 | * 用户点击 "Apply" 按钮或 "OK" 按钮之后,会调用此方法
81 | */
82 | @Override
83 | public void apply() {
84 | if (this.form == null) return;
85 |
86 | ConfigState configState = ConfigService.getInstance().getState();
87 | configState.setRemindMode(this.form.getRemindModeOption());
88 | configState.setRemindPictures(this.form.getPictureUrlList());
89 | configState.setDurationInMinutes(this.form.getDurationInMinutes());
90 | configState.setNotifyTitle(this.form.getNotifyTitle());
91 | configState.setNotifyBody(this.form.getNotifyBody());
92 | configState.setNotifyAction(this.form.getNotifyAction());
93 | configState.setDisabled(this.form.getDisabled());
94 | ConfigService.getInstance().setState(configState);
95 | LOG.info("apply and save user settings");
96 |
97 | ReminderTask.restart();
98 | LOG.info("restart scheduled reminder task");
99 | }
100 |
101 | /**
102 | * IDEA 初始化设置页面或者用户点击 "Reset" 按钮之后,会调用此方法
103 | */
104 | @Override
105 | public void reset() {
106 | if (form == null) return;
107 |
108 | ConfigState configState = ConfigService.getInstance().getState();
109 | this.form.setRemindModeOption(configState.getRemindMode());
110 | this.form.setPictureUrlList(configState.getRemindPictures());
111 | this.form.setDurationInMinutes(configState.getDurationInMinutes());
112 | this.form.setNotifyTitle(configState.getNotifyTitle());
113 | this.form.setNotifyBody(configState.getNotifyBody());
114 | this.form.setNotifyAction(configState.getNotifyAction());
115 | this.form.setDisabled(configState.getDisabled());
116 | LOG.info("reset user settings");
117 | }
118 |
119 | /**
120 | * IDEA 销毁设置页面后,会调用此方法
121 | */
122 | @Override
123 | public void disposeUIResources() {
124 | this.form = null;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/PluginSettingForm.form:
--------------------------------------------------------------------------------
1 |
2 |
132 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/ConfigState.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import cn.fantasticmao.ycy.intellij.plugin.GlobalConfig;
4 | import com.intellij.ide.util.PropertiesComponent;
5 | import com.intellij.util.xmlb.annotations.OptionTag;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 | import java.util.Objects;
10 | import java.util.Random;
11 | import java.util.stream.Stream;
12 |
13 | /**
14 | * 配置状态类
15 | *
16 | * @author fantasticmao
17 | * @version 1.2
18 | * @see SDK DevGuide
19 | * @since 2019-04-14
20 | */
21 | public class ConfigState {
22 | @OptionTag
23 | private Integer remindMode;
24 | @OptionTag
25 | private List remindPictures;
26 | @OptionTag
27 | private Integer durationInMinutes;
28 | @OptionTag
29 | private String notifyTitle;
30 | @OptionTag
31 | private String notifyBody;
32 | @OptionTag
33 | private String notifyAction;
34 | @OptionTag
35 | private Boolean disabled;
36 |
37 | /**
38 | * 提醒方式枚举类
39 | *
40 | * @see cn.fantasticmao.ycy.intellij.plugin.remind.ReminderStrategy
41 | */
42 | public enum RemindModeEnum {
43 | /**
44 | * 直接打开图片
45 | */
46 | DIRECT(0, I18nBundle.message(I18nBundle.Key.CONFIG_VALUE_REMIND_MODE_DIRECT)),
47 | /**
48 | * 间接打开图片
49 | */
50 | INDIRECT(1, I18nBundle.message(I18nBundle.Key.CONFIG_VALUE_REMIND_MODE_INDIRECT));
51 |
52 | public final int index;
53 | public final String description;
54 |
55 | RemindModeEnum(int index, String description) {
56 | this.index = index;
57 | this.description = description;
58 | }
59 |
60 | public static RemindModeEnum valueOf(int index) {
61 | return Stream.of(RemindModeEnum.values())
62 | .filter(remindMode -> index == remindMode.index)
63 | .findFirst()
64 | .orElseThrow(NullPointerException::new);
65 | }
66 | }
67 |
68 | /**
69 | * 默认配置对象
70 | *
71 | * 在 {@link ConfigService#setState(ConfigState)} 时,新配置对象会与默认配置对象作比较,
72 | * IDEA 会保存有差异的字段至 {@link ConfigServiceImpl} 指定的 {@code ycyReminder.xml} 配置文件中
73 | *
74 | * @see ConfigService#getState()
75 | * @see ConfigService#setState(ConfigState)
76 | */
77 | public ConfigState() {
78 | // 第一次开启插件时,应该使用默认配置
79 | this.remindMode = DefaultConfig.REMIND_MODE;
80 | this.remindPictures = new ArrayList<>(DefaultConfig.REMIND_PICTURE_LIST);
81 | this.durationInMinutes = DefaultConfig.DURATION_IN_MINUTES;
82 | this.notifyTitle = DefaultConfig.NOTIFY_TITLE;
83 | this.notifyBody = DefaultConfig.NOTIFY_BODY;
84 | this.notifyAction = DefaultConfig.NOTIFY_ACTION;
85 | this.disabled = DefaultConfig.DISABLED;
86 | }
87 |
88 | @Override
89 | public boolean equals(Object o) {
90 | if (this == o) return true;
91 | if (o == null || getClass() != o.getClass()) return false;
92 | ConfigState that = (ConfigState) o;
93 | return Objects.equals(remindMode, that.remindMode) &&
94 | Objects.equals(remindPictures, that.remindPictures) &&
95 | Objects.equals(durationInMinutes, that.durationInMinutes) &&
96 | Objects.equals(notifyTitle, that.notifyTitle) &&
97 | Objects.equals(notifyBody, that.notifyBody) &&
98 | Objects.equals(notifyAction, that.notifyAction) &&
99 | Objects.equals(disabled, that.disabled);
100 | }
101 |
102 | @Override
103 | public int hashCode() {
104 | return Objects.hash(remindMode, remindPictures, durationInMinutes,
105 | notifyTitle, notifyBody, notifyAction, disabled);
106 | }
107 |
108 | /**
109 | * 随机获取待展示的图片
110 | */
111 | public String getRandomRemindPicture() {
112 | if (this.remindPictures == null || this.remindPictures.size() == 0) {
113 | throw new IllegalArgumentException("picture list cannot be empty");
114 | }
115 |
116 | final String pictureIndexCacheKey = GlobalConfig.PLUGIN_ID + "_showedPictureIndex";
117 | final int pictureIndex;
118 | if (this.remindPictures.size() > 1) {
119 | // 1. 获取上次展示的图片 index
120 | final String prevPictureIndexStr = PropertiesComponent.getInstance().getValue(pictureIndexCacheKey);
121 |
122 | // 2. 生成下次展示的图片 index
123 | if (prevPictureIndexStr != null) {
124 | // 2.1 若上次展示的图片 index 存在,则生成下次展示的图片 index 时,需要避免与上次展示的重复
125 | final int prevPictureIndex = Integer.parseInt(prevPictureIndexStr);
126 | for (; ; ) { // 使用 for(; ;) 而不是 while(true),让代码看起来更酷
127 | int nextPictureIndex = new Random().nextInt(this.remindPictures.size());
128 | if (nextPictureIndex != prevPictureIndex) {
129 | pictureIndex = nextPictureIndex;
130 | break;
131 | }
132 | }
133 | } else {
134 | // 2.2 若上次展示的图片 index 不存在,则直接随机生成下次展示的图片 index
135 | pictureIndex = new Random().nextInt(this.remindPictures.size());
136 | }
137 | } else {
138 | pictureIndex = 0;
139 | }
140 | // 保存这次展示的图片 index
141 | PropertiesComponent.getInstance().setValue(pictureIndexCacheKey, String.valueOf(pictureIndex));
142 | return this.remindPictures.get(pictureIndex);
143 | }
144 |
145 | // getter and setter
146 |
147 | public Integer getRemindMode() {
148 | return remindMode;
149 | }
150 |
151 | public void setRemindMode(Integer remindMode) {
152 | this.remindMode = remindMode;
153 | }
154 |
155 | public List getRemindPictures() {
156 | return remindPictures;
157 | }
158 |
159 | public void setRemindPictures(List remindPictures) {
160 | this.remindPictures = remindPictures;
161 | }
162 |
163 | public Integer getDurationInMinutes() {
164 | return durationInMinutes;
165 | }
166 |
167 | public void setDurationInMinutes(Integer durationInMinutes) {
168 | this.durationInMinutes = durationInMinutes;
169 | }
170 |
171 | public String getNotifyTitle() {
172 | return notifyTitle;
173 | }
174 |
175 | public void setNotifyTitle(String notifyTitle) {
176 | this.notifyTitle = notifyTitle;
177 | }
178 |
179 | public String getNotifyBody() {
180 | return notifyBody;
181 | }
182 |
183 | public void setNotifyBody(String notifyBody) {
184 | this.notifyBody = notifyBody;
185 | }
186 |
187 | public String getNotifyAction() {
188 | return notifyAction;
189 | }
190 |
191 | public void setNotifyAction(String notifyAction) {
192 | this.notifyAction = notifyAction;
193 | }
194 |
195 | public Boolean getDisabled() {
196 | return disabled;
197 | }
198 |
199 | public void setDisabled(Boolean disabled) {
200 | this.disabled = disabled;
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/PluginSettingForm.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import com.intellij.icons.AllIcons;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import com.intellij.openapi.ui.ComboBox;
7 | import com.intellij.ui.ToolbarDecorator;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import javax.swing.*;
11 | import java.util.List;
12 |
13 | /**
14 | * 插件设置页面的表单
15 | *
16 | * 表单实例化由插件 {@code UI Designer} 根据 {@code PluginSettingForm.form} 配置文件生成
17 | *
18 | * @author fantasticmao
19 | * @version 1.2
20 | * @see PluginSettingPage#createComponent()
21 | * @since 2019-04-13
22 | */
23 | public class PluginSettingForm {
24 | private JPanel pluginSettingPanel;
25 |
26 | private JLabel remindModeLabel;
27 | private JComboBox remindModeOptions;
28 |
29 | private JLabel pictureUrlLabel;
30 | private PluginSettingPictureUrlTable pictureUrlTable;
31 | private JPanel pictureUrlPanel;
32 |
33 | private JLabel durationInMinutesLabel;
34 | private JTextField durationInMinutesField;
35 |
36 | private JLabel notifyTitleLabel;
37 | private JTextField notifyTitleField;
38 |
39 | private JLabel notifyBodyLabel;
40 | private JTextField notifyBodyField;
41 |
42 | private JLabel notifyActionLabel;
43 | private JTextField notifyActionField;
44 |
45 | private JLabel disabledLabel;
46 | private JCheckBox disabledField;
47 |
48 | public JPanel getPluginSettingPanel() {
49 | return this.pluginSettingPanel;
50 | }
51 |
52 | private void createUIComponents() {
53 | // create custom UI components
54 | final ConfigState configState = ConfigService.getInstance().getState();
55 |
56 | String remindModeLabelText = I18nBundle.message(I18nBundle.Key.CONFIG_LABEL_REMIND_MODE_OPTIONS);
57 | this.remindModeLabel = new JLabel(remindModeLabelText);
58 | this.remindModeOptions = new ComboBox<>();
59 | for (ConfigState.RemindModeEnum remindMode : ConfigState.RemindModeEnum.values()) {
60 | this.remindModeOptions.addItem(remindMode.description);
61 | }
62 |
63 | String pictureUrlLabelText = I18nBundle.message(I18nBundle.Key.CONFIG_LABEL_PICTURE_URL_LIST);
64 | this.pictureUrlLabel = new JLabel(pictureUrlLabelText);
65 | List remindPictures = configState.getRemindPictures();
66 | this.pictureUrlTable = new PluginSettingPictureUrlTable(remindPictures);
67 | this.pictureUrlPanel = ToolbarDecorator.createDecorator(pictureUrlTable)
68 | /*
69 | * at version 1.5 fix a bug: 2020.1 版本 AllIcons.Actions.Reset_to_default 过时问题
70 | * see https://github.com/fantasticmao/ycy-intellij-plugin/issues/27
71 | */
72 | .addExtraAction(new AnAction(() -> "Reset", AllIcons.Actions.Rollback) {
73 | @Override
74 | public void actionPerformed(@NotNull AnActionEvent e) {
75 | pictureUrlTable.resetTableList();
76 | }
77 |
78 | @Override
79 | public boolean isDumbAware() {
80 | return true; // 使用「后台更新」模式
81 | }
82 | })
83 | .createPanel();
84 |
85 | String durationInMinutesLabelText = I18nBundle.message(I18nBundle.Key.CONFIG_LABEL_DURATION_IN_MINUTES);
86 | this.durationInMinutesLabel = new JLabel(durationInMinutesLabelText);
87 |
88 | String notifyTitleLabelText = I18nBundle.message(I18nBundle.Key.CONFIG_LABEL_NOTIFY_CONTENT_TITLE);
89 | this.notifyTitleLabel = new JLabel(notifyTitleLabelText);
90 |
91 | String notifyBodyLabelText = I18nBundle.message(I18nBundle.Key.CONFIG_LABEL_NOTIFY_CONTENT_BODY);
92 | this.notifyBodyLabel = new JLabel(notifyBodyLabelText);
93 |
94 | String notifyActionLabelText = I18nBundle.message(I18nBundle.Key.CONFIG_LABEL_NOTIFY_CONTENT_ACTION);
95 | this.notifyActionLabel = new JLabel(notifyActionLabelText);
96 |
97 | String disabledLabelText = I18nBundle.message(I18nBundle.Key.CONFIG_LABEL_DISABLED);
98 | this.disabledLabel = new JLabel(disabledLabelText);
99 | }
100 |
101 | /**
102 | * 获取提醒方式
103 | *
104 | * @see cn.fantasticmao.ycy.intellij.plugin.config.ConfigState.RemindModeEnum
105 | */
106 | public int getRemindModeOption() {
107 | return this.remindModeOptions.getSelectedIndex();
108 | }
109 |
110 | /**
111 | * 设置提醒方式
112 | *
113 | * optionIndex 参数值:
114 | *
115 | * - 0. 消息通知 -> 打开图片
116 | * - 1. 打开图片
117 | *
118 | *
119 | * @param optionIndex 0 或 1
120 | */
121 | public void setRemindModeOption(int optionIndex) {
122 | optionIndex = Math.max(optionIndex, 0);
123 | optionIndex = Math.min(optionIndex, 1);
124 | this.remindModeOptions.setSelectedIndex(optionIndex);
125 | }
126 |
127 | /**
128 | * 获取提醒图片列表
129 | */
130 | public List getPictureUrlList() {
131 | return this.pictureUrlTable.getTableList();
132 | }
133 |
134 | /**
135 | * 设置提醒图片列表
136 | */
137 | public void setPictureUrlList(List pictureList) {
138 | this.pictureUrlTable.setTableList(pictureList);
139 | }
140 |
141 | /**
142 | * 获取提醒间隔时间,单位分钟
143 | */
144 | public int getDurationInMinutes() {
145 | try {
146 | return Integer.parseInt(this.durationInMinutesField.getText());
147 | } catch (NumberFormatException e) {
148 | return DefaultConfig.DURATION_IN_MINUTES;
149 | }
150 | }
151 |
152 | /**
153 | * 设置提醒间隔时间,单位分钟
154 | */
155 | public void setDurationInMinutes(int durationInMinutes) {
156 | this.durationInMinutesField.setText(String.valueOf(durationInMinutes));
157 | }
158 |
159 | /**
160 | * 获取通知文案的标题
161 | */
162 | public String getNotifyTitle() {
163 | return this.notifyTitleField.getText();
164 | }
165 |
166 | /**
167 | * 设置通知文案的标题
168 | */
169 | public void setNotifyTitle(String notifyTitle) {
170 | this.notifyTitleField.setText(notifyTitle);
171 | }
172 |
173 | /**
174 | * 获取通知文案的内容
175 | */
176 | public String getNotifyBody() {
177 | return this.notifyBodyField.getText();
178 | }
179 |
180 | /**
181 | * 设置通知文案的内容
182 | */
183 | public void setNotifyBody(String notifyContent) {
184 | this.notifyBodyField.setText(notifyContent);
185 | }
186 |
187 | /**
188 | * 获取通知文案的按钮
189 | */
190 | public String getNotifyAction() {
191 | return this.notifyActionField.getText();
192 | }
193 |
194 | /**
195 | * 设置通知文案的按钮
196 | */
197 | public void setNotifyAction(String notifyAction) {
198 | this.notifyActionField.setText(notifyAction);
199 | }
200 |
201 | /**
202 | * 获取禁用状态
203 | */
204 | public Boolean getDisabled() {
205 | return this.disabledField.isSelected();
206 | }
207 |
208 | /**
209 | * 设置禁用状态
210 | */
211 | public void setDisabled(Boolean disabled) {
212 | this.disabledField.setSelected(disabled);
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/src/main/java/cn/fantasticmao/ycy/intellij/plugin/config/PluginSettingPictureUrlTable.java:
--------------------------------------------------------------------------------
1 | package cn.fantasticmao.ycy.intellij.plugin.config;
2 |
3 | import com.intellij.openapi.diagnostic.Logger;
4 | import com.intellij.openapi.fileChooser.FileChooser;
5 | import com.intellij.openapi.fileChooser.FileChooserDescriptor;
6 | import com.intellij.openapi.vfs.VirtualFile;
7 | import com.intellij.ui.table.JBTable;
8 | import com.intellij.util.ui.EditableModel;
9 |
10 | import javax.swing.*;
11 | import javax.swing.table.AbstractTableModel;
12 | import javax.swing.table.TableColumn;
13 | import javax.swing.table.TableColumnModel;
14 | import java.util.ArrayList;
15 | import java.util.Collections;
16 | import java.util.List;
17 |
18 | /**
19 | * 插件设置页面的表格
20 | *
21 | * @author fantasticmao
22 | * @version 1.3
23 | * @since 2019-05-05
24 | */
25 | public class PluginSettingPictureUrlTable extends JBTable {
26 | private static final Logger LOG = Logger.getInstance(PluginSettingPictureUrlTable.class);
27 |
28 | /**
29 | * 序号列
30 | */
31 | private static final int ORDER_COLUMN = 0;
32 |
33 | /**
34 | * 图片 URL 列
35 | */
36 | private static final int URL_COLUMN = 1;
37 |
38 | public PluginSettingPictureUrlTable(List defaultPictureUrlList) {
39 | super(new ModelAdapter(defaultPictureUrlList));
40 | super.setStriped(true);
41 |
42 | final TableColumnModel columnModel = getColumnModel();
43 | final TableColumn orderColumn = columnModel.getColumn(ORDER_COLUMN);
44 | orderColumn.setPreferredWidth(20);
45 |
46 | final TableColumn urlColumn = columnModel.getColumn(URL_COLUMN);
47 | urlColumn.setPreferredWidth(280);
48 | }
49 |
50 | /**
51 | * 获取表格的 {@link javax.swing.table.TableModel}
52 | *
53 | * 实现 {@link com.intellij.util.ui.EditableModel} 表示表格是可编辑的
54 | *
55 | * @see com.intellij.ui.ToolbarDecorator#createDecorator(JTable)
56 | */
57 | @Override
58 | public ModelAdapter getModel() {
59 | return (ModelAdapter) super.getModel();
60 | }
61 |
62 | /**
63 | * 重置表格数据
64 | */
65 | public void resetTableList() {
66 | this.getModel().setRowsData(DefaultConfig.REMIND_PICTURE_LIST);
67 | LOG.info("reset picture url list to default");
68 | }
69 |
70 | /**
71 | * 获取表格数据
72 | */
73 | public List getTableList() {
74 | List pictureList = this.getModel().getRowsData();
75 | LOG.info("get picture url list: " + pictureList);
76 | return pictureList;
77 | }
78 |
79 | /**
80 | * 设置表格数据
81 | */
82 | public void setTableList(List pictureList) {
83 | this.getModel().setRowsData(pictureList);
84 | LOG.info("set picture url list to: " + pictureList);
85 | }
86 |
87 | /**
88 | * 插件设置页面的表格的 {@link javax.swing.table.TableModel}
89 | *
90 | * 实现 {@link com.intellij.util.ui.EditableModel} 表示表格是可编辑的
91 | */
92 | private static class ModelAdapter extends AbstractTableModel implements EditableModel {
93 | private static final Logger LOG = Logger.getInstance(ModelAdapter.class);
94 |
95 | /**
96 | * 图片 URL 列表数据
97 | */
98 | private final List pictureUrlList;
99 |
100 | public ModelAdapter(List defaultPictureUrlList) {
101 | // 复制表格数据(使用深拷贝模式),避免修改默认图片配置
102 | this.pictureUrlList = new ArrayList<>(defaultPictureUrlList.size());
103 | this.pictureUrlList.addAll(defaultPictureUrlList);
104 | }
105 |
106 | /**
107 | * 获取总行数
108 | */
109 | @Override
110 | public int getRowCount() {
111 | return pictureUrlList.size();
112 | }
113 |
114 | /**
115 | * 获取总列数
116 | */
117 | @Override
118 | public int getColumnCount() {
119 | return 2;
120 | }
121 |
122 | /**
123 | * 根据列号,获取默认列名
124 | */
125 | @Override
126 | public String getColumnName(int column) {
127 | return column == ORDER_COLUMN
128 | ? I18nBundle.message(I18nBundle.Key.CONFIG_TABLE_COLUMN_0)
129 | : I18nBundle.message(I18nBundle.Key.CONFIG_TABLE_COLUMN_1);
130 | }
131 |
132 | /**
133 | * 设置单元格的 {@link java.lang.Class}
134 | */
135 | @Override
136 | public Class> getColumnClass(int columnIndex) {
137 | return String.class;
138 | }
139 |
140 | /**
141 | * 是否可以编辑单元格内容
142 | */
143 | @Override
144 | public boolean isCellEditable(int rowIndex, int columnIndex) {
145 | return false;
146 | }
147 |
148 | /**
149 | * 根据单元格坐标,获取单元格内容
150 | */
151 | @Override
152 | public Object getValueAt(int rowIndex, int columnIndex) {
153 | return columnIndex == ORDER_COLUMN ? String.valueOf(rowIndex) : pictureUrlList.get(rowIndex);
154 | }
155 |
156 | /**
157 | * 添加表格行
158 | */
159 | @Override
160 | public void addRow() {
161 | FileChooserDescriptor descriptor = PluginSettingConfig.PICTURE_FILE_CHOOSER_DESCRIPTOR;
162 | FileChooser.chooseFiles(descriptor, null, null, fileList -> {
163 | List chosenPictureUrlList = fileList.stream()
164 | .map(VirtualFile::getUrl)
165 | .filter(pictureUrl -> !pictureUrlList.contains(pictureUrl))
166 | .toList();
167 | if (chosenPictureUrlList.size() != 0) {
168 | pictureUrlList.addAll(chosenPictureUrlList);
169 | LOG.info("add rows: " + chosenPictureUrlList);
170 | super.fireTableRowsInserted(pictureUrlList.size() - 1 - chosenPictureUrlList.size(), pictureUrlList.size() - 1);
171 | } else {
172 | LOG.info("choose no files");
173 | }
174 | });
175 | }
176 |
177 | /**
178 | * 交换表格行
179 | */
180 | @Override
181 | public void exchangeRows(int oldIndex, int newIndex) {
182 | final String oldPictureUrl = pictureUrlList.get(oldIndex);
183 | final String newPictureUrl = pictureUrlList.get(newIndex);
184 | pictureUrlList.set(oldIndex, newPictureUrl);
185 | pictureUrlList.set(newIndex, oldPictureUrl);
186 | LOG.info(String.format("exchange rows index: %d -> %d", oldIndex, newIndex));
187 | super.fireTableRowsUpdated(Math.min(oldIndex, newIndex), Math.max(oldIndex, newIndex));
188 | }
189 |
190 | /**
191 | * 是否可以交换表格行
192 | */
193 | @Override
194 | public boolean canExchangeRows(int oldIndex, int newIndex) {
195 | return true;
196 | }
197 |
198 | /**
199 | * 删除表格行
200 | */
201 | @Override
202 | public void removeRow(int idx) {
203 | pictureUrlList.remove(idx);
204 | LOG.info("remove row index: " + idx);
205 | super.fireTableRowsDeleted(idx, idx);
206 | }
207 |
208 | /**
209 | * 获取表格行数据
210 | */
211 | public List getRowsData() {
212 | List rows = Collections.unmodifiableList(new ArrayList<>(pictureUrlList));
213 | LOG.info("get rows data: " + rows);
214 | return rows;
215 | }
216 |
217 | /**
218 | * 设置表格行数据
219 | */
220 | public void setRowsData(List list) {
221 | pictureUrlList.clear();
222 | pictureUrlList.addAll(list);
223 | LOG.info("set rows data: " + list);
224 | super.fireTableDataChanged();
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 | # Collect all arguments for the java command;
201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
202 | # shell script including quotes and variable substitutions, so put them in
203 | # double quotes to make sure that they get re-expanded; and
204 | # * put everything else in single quotes, so that it's not re-expanded.
205 |
206 | set -- \
207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
208 | -classpath "$CLASSPATH" \
209 | org.gradle.wrapper.GradleWrapperMain \
210 | "$@"
211 |
212 | # Stop when "xargs" is not available.
213 | if ! command -v xargs >/dev/null 2>&1
214 | then
215 | die "xargs is not available"
216 | fi
217 |
218 | # Use "xargs" to parse quoted args.
219 | #
220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
221 | #
222 | # In Bash we could simply go:
223 | #
224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
225 | # set -- "${ARGS[@]}" "$@"
226 | #
227 | # but POSIX shell has neither arrays nor command substitution, so instead we
228 | # post-process each arg (as a line of input to sed) to backslash-escape any
229 | # character that might be a shell metacharacter, then use eval to reverse
230 | # that process (while maintaining the separation between arguments), and wrap
231 | # the whole thing up as a single "set" statement.
232 | #
233 | # This will of course break if any of these variables contains a newline or
234 | # an unmatched quote.
235 | #
236 |
237 | eval "set -- $(
238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
239 | xargs -n1 |
240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
241 | tr '\n' ' '
242 | )" '"$@"'
243 |
244 | exec "$JAVACMD" "$@"
245 |
--------------------------------------------------------------------------------