├── 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 | [![jetbrains plugin](https://img.shields.io/jetbrains/plugin/v/cn.fantasticmao.ycy.intellij.plugin)](https://plugins.jetbrains.com/plugin/12204-programmer-motivator-chaoyue-yang/) 4 | [![License](https://img.shields.io/github/license/fantasticmao/ycy-intellij-plugin)](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 | ![usage](doc/usage.gif) 17 | 18 | ![config](doc/config.png) 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 | [![jetbrains plugin](https://img.shields.io/jetbrains/plugin/v/cn.fantasticmao.ycy.intellij.plugin)](https://plugins.jetbrains.com/plugin/12204-programmer-motivator-chaoyue-yang/) 4 | [![License](https://img.shields.io/github/license/fantasticmao/ycy-intellij-plugin)](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 | ![usage](doc/usage.gif) 17 | 18 | ![config](doc/config.png) 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 |
    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 | 127 | 128 | 129 | 130 | 131 |
    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 | --------------------------------------------------------------------------------