├── .github └── workflows │ └── retype-action.yml ├── .gitignore ├── .idea ├── gradle.xml ├── misc.xml ├── uiDesigner.xml ├── vcs.xml └── workspace.xml ├── .run └── Run IDE with Plugin.run.xml ├── AUTHORS ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── retype.yml ├── settings.gradle.kts ├── site ├── about.md ├── changelog.md ├── data.md ├── developer.md ├── docs │ ├── actions │ │ ├── AddLabelAction.html │ │ ├── AddLabelActionGroup.html │ │ ├── ConfigAction.html │ │ ├── PauseResumeTrackingAction.html │ │ ├── StartStopTrackingAction.html │ │ ├── package-summary.html │ │ └── package-tree.html │ ├── allclasses-index.html │ ├── allpackages-index.html │ ├── api │ │ ├── RealtimeDataImpl.html │ │ ├── package-summary.html │ │ └── package-tree.html │ ├── components │ │ ├── AlertDialog.html │ │ ├── ConfigDialog.html │ │ ├── package-summary.html │ │ └── package-tree.html │ ├── element-list │ ├── entity │ │ ├── Config.html │ │ ├── package-summary.html │ │ └── package-tree.html │ ├── help-doc.html │ ├── index-files │ │ ├── index-1.html │ │ ├── index-10.html │ │ ├── index-11.html │ │ ├── index-12.html │ │ ├── index-13.html │ │ ├── index-14.html │ │ ├── index-15.html │ │ ├── index-16.html │ │ ├── index-17.html │ │ ├── index-18.html │ │ ├── index-19.html │ │ ├── index-2.html │ │ ├── index-3.html │ │ ├── index-4.html │ │ ├── index-5.html │ │ ├── index-6.html │ │ ├── index-7.html │ │ ├── index-8.html │ │ └── index-9.html │ ├── index.html │ ├── jquery-ui.overrides.css │ ├── legal │ │ ├── COPYRIGHT │ │ └── LICENSE │ ├── member-search-index.js │ ├── module-search-index.js │ ├── overview-summary.html │ ├── overview-tree.html │ ├── package-search-index.js │ ├── resources │ │ ├── glass.png │ │ └── x.png │ ├── script-dir │ │ ├── jquery-3.6.1.min.js │ │ ├── jquery-ui.min.css │ │ └── jquery-ui.min.js │ ├── script.js │ ├── search.js │ ├── serialized-form.html │ ├── stylesheet.css │ ├── tag-search-index.js │ ├── trackers │ │ ├── EyeTracker.html │ │ ├── IDETracker.html │ │ ├── ScreenRecorder.html │ │ ├── package-summary.html │ │ └── package-tree.html │ ├── type-search-index.js │ └── utils │ │ ├── AvailabilityChecker.html │ │ ├── OSDetector.html │ │ ├── RelativePathGetter.html │ │ ├── XMLWriter.html │ │ ├── package-summary.html │ │ └── package-tree.html ├── faq.md ├── favicon.png ├── google3c26aad8347fc661.html ├── license.md ├── rename.py ├── static │ ├── add-label.png │ ├── ast.png │ ├── config.png │ ├── eye-data.png │ ├── eye-tracker.png │ ├── ide-data.png │ ├── ide-tracker.png │ ├── logo.png │ ├── logo.svg │ ├── overview.png │ ├── paper.pdf │ ├── range.png │ ├── toolbar.png │ └── visualization.png ├── usage.md └── welcome.md └── src └── main ├── java ├── actions │ ├── AddLabelAction.java │ ├── AddLabelActionGroup.java │ ├── ConfigAction.java │ ├── PauseResumeTrackingAction.java │ └── StartStopTrackingAction.java ├── api │ └── RealtimeDataImpl.java ├── components │ ├── AlertDialog.java │ └── ConfigDialog.java ├── entity │ └── Config.java ├── trackers │ ├── EyeTracker.java │ ├── IDETracker.java │ └── ScreenRecorder.java └── utils │ ├── AvailabilityChecker.java │ ├── OSDetector.java │ ├── RelativePathGetter.java │ └── XMLWriter.java └── resources └── META-INF ├── plugin.xml └── pluginIcon.svg /.github/workflows/retype-action.yml: -------------------------------------------------------------------------------- 1 | name: Publish Retype website to GitHub Pages 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | publish: 10 | name: Publish to retype branch 11 | 12 | runs-on: ubuntu-latest 13 | 14 | permissions: 15 | contents: write 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - uses: retypeapp/action-build@latest 21 | 22 | - uses: retypeapp/action-github-pages@latest 23 | with: 24 | update-branch: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.run/Run IDE with Plugin.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | false 23 | 24 | 25 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | CodeGRITS is developed by a team of HCI and SE researchers at the University of Notre Dame and Vanderbilt University. 2 | 3 | Ningzhi Tang (Equal Contribution) 4 | Junwen An (Equal Contribution) 5 | Meng Chen 6 | Aakash Bansa 7 | Yu Huang 8 | Collin McMillan 9 | Toby Jia-Jun Li -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 SaNDwich Lab 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeGRITS 2 | 3 | [![Website](https://img.shields.io/badge/Website-Visit-brightgreen)](https://codegrits.github.io/CodeGRITS/) [![Javadoc](https://img.shields.io/badge/Javadoc-Docs-blue)](https://codegrits.github.io/CodeGRITS/docs/index.html) [![Paper PDF](https://img.shields.io/badge/Paper-PDF-olive)](https://codegrits.github.io/CodeGRITS/static/paper.pdf) [![Demo Video](https://img.shields.io/badge/Demo-Video-orange)](https://www.youtube.com/watch?v=d-YsJfW2NMI) [![Archived](https://img.shields.io/badge/Archived-SWH-blueviolet)](https://archive.softwareheritage.org/swh:1:dir:32d91426f07dd0f4b36ba05bc708b5a25ad06dd3) [![MIT License](https://img.shields.io/badge/License-MIT-green)](https://github.com/codegrits/CodeGRITS/blob/main/LICENSE) 4 | 5 | [CodeGRITS](https://codegrits.github.io/CodeGRITS/) stands for **G**aze **R**ecording & **I**DE **T**racking **S**ystem. It's a plugin developed by the [SaNDwich Lab](https://toby.li/) and is specially designed for empirical software engineering researchers. CodeGRITS is built on top of [IntelliJ Platform SDK](https://plugins.jetbrains.com/docs/intellij/welcome.html), with wide compatibility with the entire family of [JetBrains IDEs](https://www.jetbrains.com/) and [Tobii eye-tracking devices](https://www.tobii.com/), to track developers’ IDE interactions and eye gaze data. 6 | 7 |

8 | CodeGRITS Overview 9 |

10 | 11 | The [website](https://codegrits.github.io/CodeGRITS/)'s source code is stored in the `./site` folder of this repository and deployed via GitHub Pages. The [JavaDoc](https://codegrits.github.io/CodeGRITS/docs/index.html) documentation is located in the `./site/docs` folder of this repository. They are archived together with the source code in the Software Heritage for long-term preservation. 12 | 13 | The data collected by CodeGRITS can be used by empirical SE researchers to understand the behaviors of developers, especially those related to eye gaze. CodeGRITS also provides a [real-time data API](developer.md) for future plugin developers and researchers to design context-aware programming support tools. 14 | 15 | We provide an example project [DataStreamReceiver](https://github.com/codegrits/DataStreamReceiver) that builds on top of the real-time data API. It is designed to receive the IDE and eye tracking data and directly visualize them in two separate windows. 16 | 17 | ## Key Features 18 | 19 | - :mag: **IDE Tracking**: CodeGRITS tracks developers’ IDE interactions, including mouse clicks, keyboard inputs, etc. 20 | - :eye: **Eye Tracking**: CodeGRITS tracks developers’ eye gaze data from [Tobii eye-tracking devices](https://www.tobii.com/), and maps them to corresponding source code elements. 21 | - :computer: **Screen Recording**: CodeGRITS simultaneously records developers’ screen for visualizing their behaviors. 22 | - 🔨 **Research Toolkit**: CodeGRITS provides a set of extra features for empirical SE researchers, including dynamic configuration, activity labeling, real-time data API, etc. 23 | - 🗃️ **Data Export**: CodeGRITS exports data in XML format for further data analysis. See [Data Format](data.md) for more details. 24 | 25 | ### Cross-platform and Multilingual Support 26 | 27 | - [x] CodeGRITS provides cross-platform support for Windows, macOS, and Linux, and is expected to be compatible with the entire family of JetBrains IDEs, including IntelliJ IDEA, PyCharm, WebStorm, etc. 28 | - [x] CodeGRITS could extract the abstract syntax tree (AST) structure of eye gazes on multiple programming languages, as long as the IDE supports them, including Java, Python, C/C++, JavaScript, etc. 29 | 30 | CodeGRITS has been primarily developed and tested on Windows and Linux, with only partial testing on macOS. There may be some unnoticed bugs on macOS. We have created a [`mac` branch](https://github.com/codegrits/CodeGRITS/tree/mac) for this and have fixed some known issues. If you encounter additional issues on macOS, please feel free to report them to us. 31 | 32 | ## Usage Guide 33 | 34 | Please see the [CodeGRITS website](https://codegrits.github.io/CodeGRITS/) for more details. The Javadoc documentation is available [here](https://codegrits.github.io/CodeGRITS/docs/index.html). 35 | 36 | ## Citation 37 | 38 | The paper of CodeGRITS has been accepted by [ICSE 2024 Demonstrations Track](https://conf.researchr.org/track/icse-2024/icse-2024-demonstrations). The PDF version is available [here](https://codegrits.github.io/CodeGRITS/static/paper.pdf). The [video demonstration](https://www.youtube.com/watch?v=d-YsJfW2NMI) is available on YouTube. 39 | 40 | Please cite the following if you use CodeGRITS in your research. 41 | 42 | ```bibtex 43 | @inproceedings{tang2024codegrits, 44 | title={CodeGRITS: A Research Toolkit for Developer Behavior and Eye Tracking in IDE}, 45 | author={Tang, Ningzhi and An, Junwen and Chen, Meng and Bansal, Aakash and Huang, Yu and McMillan, Collin and Li, Toby Jia-Jun}, 46 | booktitle={46th International Conference on Software Engineering Companion (ICSE-Companion '24)}, 47 | year={2024}, 48 | organization={ACM} 49 | } 50 | ``` 51 | 52 | ## Contact us 53 | 54 | Please feel free to contact Ningzhi Tang at ntang@nd.edu or Junwen An at jan2@nd.edu/feasiblechart@gmail.com if you have any questions or suggestions. 55 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | // id("org.jetbrains.kotlin.jvm") version "1.8.21" 4 | id("org.jetbrains.intellij") version "1.13.3" 5 | } 6 | 7 | group = "com.nd" 8 | version = "0.2.0" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | // https://mvnrepository.com/artifact/com.opencsv/opencsv 16 | implementation("com.opencsv:opencsv:5.7.1") 17 | // implementation("org.bytedeco:javacv-platform:1.5.9") // NOTE: This is too large 18 | implementation("org.bytedeco:javacv:1.5.9") 19 | implementation("org.bytedeco:ffmpeg:6.0-1.5.9") 20 | implementation("org.bytedeco:ffmpeg-platform:6.0-1.5.9") 21 | } 22 | 23 | // Configure Gradle IntelliJ Plugin 24 | // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html 25 | intellij { 26 | version.set("2023.1.4") 27 | 28 | type.set("IC") // IntelliJ Community Edition 29 | // type.set("IU") // IntelliJ Ultimate Edition 30 | plugins.set(listOf("com.intellij.java")) 31 | 32 | // type.set("PC") // PyCharm Community Edition 33 | // type.set("PY") // PyCharm Professional Edition 34 | // plugins.set(listOf("PythonCore")) 35 | 36 | // type.set("CL") // CLion 37 | 38 | // type.set("PS") // PhpStorm 39 | 40 | // Fixme: Android Studio 41 | // type.set("IC") 42 | // plugins.set(listOf("android")) 43 | } 44 | 45 | tasks { 46 | // Set the JVM compatibility versions 47 | withType { 48 | sourceCompatibility = "17" 49 | targetCompatibility = "17" 50 | } 51 | // withType { 52 | // kotlinOptions.jvmTarget = "17" 53 | // } 54 | 55 | patchPluginXml { 56 | sinceBuild.set("222") // 2022.2 NOTE Java 17 is now required 57 | untilBuild.set("233.*") 58 | } 59 | 60 | signPlugin { 61 | certificateChain.set(System.getenv("CERTIFICATE_CHAIN")) 62 | privateKey.set(System.getenv("PRIVATE_KEY")) 63 | password.set(System.getenv("PRIVATE_KEY_PASSWORD")) 64 | } 65 | 66 | publishPlugin { 67 | token.set(System.getenv("PUBLISH_TOKEN")) 68 | } 69 | 70 | val createOpenApiSourceJar by registering(Jar::class) { 71 | from(sourceSets.main.get().java) { 72 | include("**/api/**/*.java") 73 | } 74 | destinationDirectory.set(layout.buildDirectory.dir("libs")) 75 | archiveClassifier.set("src") 76 | } 77 | 78 | buildPlugin { 79 | dependsOn(createOpenApiSourceJar) 80 | from(createOpenApiSourceJar) { into("lib/src") } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.stdlib.default.dependency=false 2 | # TODO temporary workaround for Kotlin 1.8.20+ (https://jb.gg/intellij-platform-kotlin-oom) 3 | kotlin.incremental.useClasspathSnapshot=false 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /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 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /retype.yml: -------------------------------------------------------------------------------- 1 | input: ./site 2 | output: .retype 3 | url: https://codegrits.github.io/CodeGRITS/ 4 | 5 | branding: 6 | title: CodeGRITS 7 | logo: static/logo.png 8 | label: v0.2.0 9 | 10 | links: 11 | - text: GitHub 12 | link: https://github.com/codegrits/CodeGRITS 13 | icon: mark-github 14 | target: blank 15 | - text: Paper 16 | link: static/paper.pdf 17 | icon: file 18 | target: blank 19 | - text: Demo 20 | link: https://www.youtube.com/watch?v=d-YsJfW2NMI 21 | icon: video 22 | target: blank 23 | - text: Issues 24 | link: https://github.com/codegrits/CodeGRITS/issues 25 | icon: comment-discussion 26 | target: blank 27 | 28 | footer: 29 | copyright: © Copyright {{ year }} – [CodeGRITS](https://codegrits.github.io/CodeGRITS/) – All rights reserved 30 | links: 31 | - text: About 32 | link: ./about.md 33 | icon: info 34 | 35 | - text: License 36 | link: ./license.md 37 | icon: shield-check 38 | 39 | include: 40 | - "**/docs/**" -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "CodeGRITS" -------------------------------------------------------------------------------- /site/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: info 3 | order: 95 4 | --- 5 | 6 | # About 7 | 8 | [CodeGRITS](https://codegrits.github.io/CodeGRITS/) is professionally built and supported by a small dedicated team of 9 | HCI and SE researchers from [SaNDwich Lab](https://toby.li/) at the [University of Notre Dame](https://www.nd.edu/) 10 | and [Vanderbilt University](https://www.vanderbilt.edu/). 11 | We are committed to keeping CodeGRITS open source and making it beneficial for the community. 12 | 13 | The authors of CodeGRITS are [Ningzhi Tang](https://nztang.com/) (equal contribution), 14 | [Junwen An](https://wanteatfruit.github.io/) (equal contribution), 15 | [Meng Chen](https://casardo-chen.github.io/), [Aakash Bansal](https://aakashba.github.io/), 16 | [Yu Huang](https://yuhuang-lab.github.io/), [Collin McMillan](https://sdf.org/~cmc/), 17 | and [Toby Jia-Jun Li](https://toby.li/). Technical support questions, bug reports, and feature requests are best started 18 | as a [GitHub Issue](https://github.com/codegrits/CodeGRITS/issues). 19 | 20 | We prepared a video demonstration of CodeGRITS for ICSE 2024. 21 | 22 | [!embed](https://www.youtube.com/embed/d-YsJfW2NMI) 23 | 24 | ## Credits 25 | 26 | CodeGRITS would not be possible without the following commercial and open-source projects: 27 | 28 | - [IntelliJ Platform SDK](https://plugins.jetbrains.com/docs/intellij/welcome.html): CodeGRITS is built on top of the 29 | IntelliJ Platform. 30 | - [Tobii Pro SDK](https://www.tobii.com/products/software/applications-and-developer-kits/tobii-pro-sdk/): CodeGRITS 31 | uses the Tobii Pro SDK to collect eye gaze data from Tobii eye-tracking devices. 32 | - [ReType](https://retype.com/): CodeGRITS documentation is built with Retype. 33 | - [JavaCV](http://bytedeco.org/): CodeGRITS uses JavaCV to implement the screen recorder. 34 | 35 | Special thanks to the [iTrace](https://www.i-trace.org/) team. iTrace is a similar tool to CodeGRITS for collecting 36 | developers' eye gaze data in several IDEs, including Eclipse, Visual Studio, and Atom. We have learned a lot from their 37 | work and referred to part of their code. Different from iTrace, CodeGRITS is built for JetBrains IDEs and provides a 38 | set of extra functionalities (e.g., IDE tracking, screen recording, etc.) for empirical SE researchers. 39 | 40 | ## License 41 | 42 | CodeGRITS is licensed under the MIT License. See the [LICENSE](license.md) for details. 43 | 44 | ## Contact us 45 | 46 | Please feel free to contact us anytime if you have any questions or suggestions. 47 | 48 | - Email the developers [Ningzhi Tang](https://nztang.com/) at ntang@nd.edu 49 | or [Junwen An](https://wanteatfruit.github.io/) at jan2@nd.edu. 50 | - Start a [GitHub Issue](https://github.com/codegrits/CodeGRITS/issues). -------------------------------------------------------------------------------- /site/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: log 3 | order: 60 4 | --- 5 | 6 | # Changelog 7 | 8 | !!! 9 | Encountered a bug or have a suggestion? Please create a [GitHub Issue](https://github.com/codegrits/CodeGRITS/issues). 10 | !!! 11 | 12 | ## v0.2.0 [!badge text="LATEST" variant="info"] 13 | 14 | Released on 2023-12-28. 15 | 16 | +++ New :icon-shield-check: 17 | 18 | - [x] Improve the implementation of Screen Recorder from [JCodec](http://jcodec.org/index.html) 19 | to [JavaCV](http://bytedeco.org/). 20 | - [x] Initial pre-mature release for CodeGRITS. 21 | +++ 22 | 23 | +++ Improved :icon-thumbsup: 24 | - [x] Fix the color distortion and the missing cursor issues of the screen recorder. 25 | - [x] Fix several issues about the naming. 26 | - [x] Add the logo of CodeGRITS. 27 | +++ 28 | 29 | --- 30 | 31 | ## v0.1.0 32 | 33 | Released on 2023-11-30. 34 | 35 | +++ New :icon-shield-check: 36 | 37 | - [x] Release for ICSE 2024 Demo review. 38 | +++ 39 | 40 | --- 41 | 42 | -------------------------------------------------------------------------------- /site/developer.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Developer Guide 3 | icon: gear 4 | order: 70 5 | --- 6 | 7 | # Developer Guide 8 | 9 | ## Further Development 10 | 11 | Please refer to [IntelliJ Platform SDK](https://plugins.jetbrains.com/docs/intellij/welcome.html) for more details. 12 | We also provide the [JavaDoc](docs/index.html) of the source code. 13 | Feel free to contact us if you need any help. 14 | 15 | ### Accommodating New IDEs 16 | 17 | See [Build from Source](usage.md#build-from-source). 18 | 19 | ### Accommodating New Eye Trackers 20 | 21 | If you want to integrate other eye-tracking devices except for Tobii eye-tracking devices, you need to reimplement all 22 | Python scripts in the source code to get the right eye-tracking device information and eye gaze data using your eye 23 | tracker API. 24 | 25 | {.compact} 26 | | Location | Method | 27 | |:------------------------------------------:|:------------------------:| 28 | | `/trackers/EyeTracker.java` | `setPythonScriptTobii()` | 29 | | `/utils/AvailabilityChecker.java` | `checkPythonEnvironment(String pythonInterpreter)` | 30 | | `/utils/AvailabilityChecker.java` | `checkEyeTracker(String pythonInterpreter)` | 31 | | `/utils/AvailabilityChecker.java` | `getEyeTrackerName(String pythonInterpreter)` | 32 | | `/utils/AvailabilityChecker.java` | `getFrequencies(String pythonInterpreter)` | 33 | 34 | ## Real-time Data API 35 | 36 | ### Overview 37 | 38 | We provide a real-time data API for future JetBrains plugin developers and researchers to get real-time data from 39 | IDE tracker and eye tracker separately. The API is based on the [IDE Tracker](#ide-tracker) 40 | and [Eye Tracker](#eye-tracker). 41 | 42 | !!! Example Project 43 | We provide an example project [DataStreamReceiver](https://github.com/codegrits/DataStreamReceiver) 44 | that builds on top of the real-time data API. It is designed to receive the IDE and eye tracking data and directly 45 | visualize them in two separate windows. You could refer to its source code to learn how to use the API. 46 | !!! 47 | 48 | ### Configuration 49 | 50 | Before using the API, you first need to build CodeGRITS from source 51 | (See [Build from Source](usage.md#build-from-source)). Then, find the folder `./build/idea-sandbox/plugins/CodeGRITS` 52 | in the CodeGRITS project, which is the dependency of the API. You need to add it to the `intellij` section 53 | in `build.gradle.kts` file of your plugin project. 54 | 55 | ```groovy 56 | intellij { 57 | // the dependency path is like ./build/idea-sandbox/plugins/CodeGRITS 58 | plugins.set(file("path-to-CodeGRITS-dependency")) 59 | } 60 | ``` 61 | 62 | You also need to add the following to `./src/main/resources/META-INF/plugin.xml`. 63 | 64 | ```xml 65 | com.nd.codegrits 66 | ``` 67 | 68 | ### Quick Start 69 | 70 | To use the API, simply call the `getInstance()` method to get the instance of the IDE Tracker or Eye Tracker. Then, set 71 | the `isRealTimeDataTransmitting` to `true` to enable real-time data transmitting. After that, set 72 | the `ideTrackerDataHandler` or `eyeTrackerDataHandler` to handle the real-time data. Finally, call the `startTracking()` 73 | method to start tracking. 74 | 75 | ```java 76 | IDETracker ideTracker = IDETracker.getInstance(); 77 | ideTracker.setIsRealTimeDataTransmitting(true); 78 | ideTracker.setIdeTrackerDataHandler(element -> { 79 | String formattedStr = "Event: " + element.getAttribute("id"); 80 | System.out.println(formattedStr); 81 | }); 82 | ideTracker.startTracking(currentProject); 83 | ``` 84 | 85 | !!! 86 | `Element` object is an XML element that is imported from `org.w3c.dom.Element` package. 87 | !!! 88 | 89 | ### IDE Tracker 90 | 91 | - `IDETracker.getInstance()` 92 | - `setIsRealTimeDataTransmitting(boolean isRealTimeDataTransmitting)` 93 | - `setIdeTrackerDataHandler(Consumer ideTrackerDataHandler)` 94 | - `startTracking(Project project)` 95 | 96 | ### Eye Tracker 97 | 98 | - `EyeTracker.getInstance()` 99 | - `setIsRealTimeDataTransmitting(boolean isRealTimeDataTransmitting)` 100 | - `setEyeTrackerDataHandler(Consumer eyeTrackerDataHandler)` 101 | - `startTracking(Project project)` -------------------------------------------------------------------------------- /site/docs/actions/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | actions 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 56 |
57 |
58 |
59 |

Package actions

60 |
61 |
62 |
package actions
63 |
64 |
    65 |
  • 66 |
    67 |
    Classes
    68 |
    69 |
    Class
    70 |
    Description
    71 | 72 |
    73 |
    This class is the action for adding labels.
    74 |
    75 | 76 |
    77 |
    This class is the action group for adding labels.
    78 |
    79 | 80 |
    81 |
    This class is the action for configuring the application.
    82 |
    83 | 84 |
    85 |
    This class is the action for pausing/resuming tracking.
    86 |
    87 | 88 |
    89 |
    This class is the action for starting/stopping tracking.
    90 |
    91 |
    92 |
    93 |
  • 94 |
95 |
96 |
97 |
98 |
99 | 100 | 101 | -------------------------------------------------------------------------------- /site/docs/actions/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | actions Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Hierarchy For Package actions

52 | Package Hierarchies: 53 | 56 |
57 |
58 |

Class Hierarchy

59 |
    60 |
  • java.lang.Object 61 |
      62 |
    • com.intellij.openapi.actionSystem.AnAction (implements com.intellij.openapi.actionSystem.ActionUpdateThreadAware, com.intellij.openapi.project.PossiblyDumbAware) 63 | 78 |
    • 79 |
    80 |
  • 81 |
82 |
83 |
84 |
85 |
86 | 87 | 88 | -------------------------------------------------------------------------------- /site/docs/allpackages-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | All Packages 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

All Packages

52 |
53 |
Package Summary
54 |
55 |
Package
56 |
Description
57 | 58 |
 
59 | 60 |
 
61 | 62 |
 
63 | 64 |
 
65 | 66 |
 
67 | 68 |
 
69 |
70 |
71 |
72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /site/docs/api/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | api 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 56 |
57 |
58 |
59 |

Package api

60 |
61 |
62 |
package api
63 |
64 |
    65 |
  • 66 |
    67 |
    Classes
    68 |
    69 |
    Class
    70 |
    Description
    71 | 72 |
    73 |
    This class provides the API for getting real-time data from the IDE and eye tracker.
    74 |
    75 |
    76 |
    77 |
  • 78 |
79 |
80 |
81 |
82 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /site/docs/api/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | api Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Hierarchy For Package api

52 | Package Hierarchies: 53 | 56 |
57 |
58 |

Class Hierarchy

59 | 66 |
67 |
68 |
69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /site/docs/components/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | components 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 56 |
57 |
58 |
59 |

Package components

60 |
61 |
62 |
package components
63 |
64 |
    65 |
  • 66 |
    67 |
    Classes
    68 |
    69 |
    Class
    70 |
    Description
    71 | 72 |
    73 |
    This class is used to create a dialog to show alert message.
    74 |
    75 | 76 |
    77 |
    This class is used to create the configuration dialog.
    78 |
    79 |
    80 |
    81 |
  • 82 |
83 |
84 |
85 |
86 |
87 | 88 | 89 | -------------------------------------------------------------------------------- /site/docs/components/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | components Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Hierarchy For Package components

52 | Package Hierarchies: 53 | 56 |
57 |
58 |

Class Hierarchy

59 |
    60 |
  • java.lang.Object 61 |
      62 |
    • com.intellij.openapi.ui.DialogWrapper 63 | 67 |
    • 68 |
    69 |
  • 70 |
71 |
72 |
73 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /site/docs/element-list: -------------------------------------------------------------------------------- 1 | actions 2 | api 3 | components 4 | entity 5 | trackers 6 | utils 7 | -------------------------------------------------------------------------------- /site/docs/entity/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entity 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 56 |
57 |
58 |
59 |

Package entity

60 |
61 |
62 |
package entity
63 |
64 |
    65 |
  • 66 |
    67 |
    Classes
    68 |
    69 |
    Class
    70 |
    Description
    71 | 72 |
    73 |
    This class is used to store the configuration of the application.
    74 |
    75 |
    76 |
    77 |
  • 78 |
79 |
80 |
81 |
82 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /site/docs/entity/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entity Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Hierarchy For Package entity

52 | Package Hierarchies: 53 | 56 |
57 |
58 |

Class Hierarchy

59 | 66 |
67 |
68 |
69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /site/docs/index-files/index-10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | M-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

M

55 |
56 |
mouses - Variable in class trackers.IDETracker
57 |
 
58 |
59 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
60 |
61 |
62 | 63 | 64 | -------------------------------------------------------------------------------- /site/docs/index-files/index-11.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | O-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

O

55 |
56 |
OS - Static variable in class utils.OSDetector
57 |
58 |
The operating system name.
59 |
60 |
OSDetector - Class in utils
61 |
62 |
This class is used to detect the operating system.
63 |
64 |
OSDetector() - Constructor for class utils.OSDetector
65 |
 
66 |
67 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
68 |
69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /site/docs/index-files/index-15.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | T-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

T

55 |
56 |
timeList - Variable in class trackers.ScreenRecorder
57 |
 
58 |
timer - Variable in class trackers.IDETracker
59 |
60 |
This variable is the timer for tracking the document changes.
61 |
62 |
timerTask - Variable in class trackers.IDETracker
63 |
64 |
This variable is the timer task for tracking the document changes.
65 |
66 |
toString() - Method in class entity.Config
67 |
 
68 |
track() - Method in class trackers.EyeTracker
69 |
70 |
This method builds the Python process and redirects the output to the pythonOutputThread to process.
71 |
72 |
trackers - package trackers
73 |
 
74 |
typings - Variable in class trackers.IDETracker
75 |
 
76 |
77 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
78 |
79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /site/docs/index-files/index-16.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | U-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

U

55 |
56 |
update(AnActionEvent) - Method in class actions.AddLabelAction
57 |
 
58 |
update(AnActionEvent) - Method in class actions.AddLabelActionGroup
59 |
60 |
This method is called when the action is performed.
61 |
62 |
update(AnActionEvent) - Method in class actions.ConfigAction
63 |
 
64 |
update(AnActionEvent) - Method in class actions.PauseResumeTrackingAction
65 |
66 |
Update the text of the action button.
67 |
68 |
update(AnActionEvent) - Method in class actions.StartStopTrackingAction
69 |
70 |
Update the text of the action button.
71 |
72 |
updateActionGroup() - Method in class components.ConfigDialog
73 |
74 |
Update the AddLabelAction group when the configuration is changed.
75 |
76 |
utils - package utils
77 |
 
78 |
79 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
80 |
81 |
82 | 83 | 84 | -------------------------------------------------------------------------------- /site/docs/index-files/index-17.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | V-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

V

55 |
56 |
visibleArea - Variable in class trackers.EyeTracker
57 |
 
58 |
visibleAreaListener - Variable in class trackers.EyeTracker
59 |
60 |
The listener for the visible area used for filtering the eye tracking data.
61 |
62 |
visibleAreaListener - Variable in class trackers.IDETracker
63 |
64 |
This variable is the visible area listener for the IDE tracker.
65 |
66 |
visibleAreas - Variable in class trackers.IDETracker
67 |
 
68 |
69 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
70 |
71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /site/docs/index-files/index-18.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | W-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

W

55 |
56 |
writeToXML(Document, String) - Static method in class utils.XMLWriter
57 |
58 |
Write the formatted XML document to the XML file.
59 |
60 |
61 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
62 |
63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /site/docs/index-files/index-19.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | X-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

X

55 |
56 |
XMLWriter - Class in utils
57 |
58 |
This class is used to write the XML document to the XML file.
59 |
60 |
XMLWriter() - Constructor for class utils.XMLWriter
61 |
 
62 |
63 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
64 |
65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /site/docs/index-files/index-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | D-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

D

55 |
56 |
dataInputStream - Variable in class api.RealtimeDataImpl
57 |
 
58 |
dataOutputPath - Variable in class entity.Config
59 |
 
60 |
dataOutputPath - Variable in class trackers.EyeTracker
61 |
 
62 |
dataOutputPath - Variable in class trackers.IDETracker
63 |
 
64 |
dataOutputPath - Variable in class trackers.ScreenRecorder
65 |
 
66 |
dataOutputTextField - Static variable in class components.ConfigDialog
67 |
 
68 |
defaultLabelsLoaded - Variable in class actions.AddLabelActionGroup
69 |
 
70 |
description - Variable in class actions.AddLabelAction
71 |
 
72 |
deviceCombo - Variable in class components.ConfigDialog
73 |
 
74 |
deviceIndex - Variable in class trackers.EyeTracker
75 |
 
76 |
dispose() - Method in class trackers.EyeTracker
77 |
 
78 |
dispose() - Method in class trackers.IDETracker
79 |
 
80 |
documentListener - Variable in class trackers.IDETracker
81 |
82 |
This variable is the document listener for the IDE tracker.
83 |
84 |
doOKAction() - Method in class components.ConfigDialog
85 |
86 |
Save the configuration when the OK button is clicked.
87 |
88 |
89 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
90 |
91 |
92 | 93 | 94 | -------------------------------------------------------------------------------- /site/docs/index-files/index-5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | F-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

F

55 |
56 |
filePath - Variable in class trackers.EyeTracker
57 |
 
58 |
files - Variable in class trackers.IDETracker
59 |
 
60 |
frameNumber - Variable in class trackers.ScreenRecorder
61 |
62 |
This variable indicates the current frame number.
63 |
64 |
frameRate - Variable in class trackers.ScreenRecorder
65 |
66 |
This variable indicates the frame rate of the screen recorder.
67 |
68 |
freqCombo - Variable in class components.ConfigDialog
69 |
 
70 |
71 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
72 |
73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /site/docs/index-files/index-7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | H-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

H

55 |
56 |
handleElement(Element) - Method in class trackers.EyeTracker
57 |
58 |
This method handles the element.
59 |
60 |
handleElement(Element) - Method in class trackers.IDETracker
61 |
62 |
This method handles the XML element for real-time data transmission.
63 |
64 |
65 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
66 |
67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /site/docs/index-files/index-9.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | L-Index 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Index

52 |
53 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form 54 |

L

55 |
56 |
label - Variable in class components.AlertDialog
57 |
58 |
The label of the alert message.
59 |
60 |
labelAreas - Static variable in class components.ConfigDialog
61 |
 
62 |
labels - Variable in class entity.Config
63 |
 
64 |
lastElement - Variable in class trackers.EyeTracker
65 |
 
66 |
lastSelectionInfo - Variable in class trackers.IDETracker
67 |
 
68 |
loadConfig() - Method in class components.ConfigDialog
69 |
70 |
Load the configuration from the config.json file to the configuration dialog using the Config class.
71 |
72 |
loadFromJson() - Method in class entity.Config
73 |
74 |
Load the configuration from the JSON file.
75 |
76 |
77 | A C D E F G H I L M O P R S T U V W X 
All Classes and Interfaces|All Packages|Serialized Form
78 |
79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /site/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Overview 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |
Packages
52 |
53 |
Package
54 |
Description
55 | 56 |
 
57 | 58 |
 
59 | 60 |
 
61 | 62 |
 
63 | 64 |
 
65 | 66 |
 
67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /site/docs/jquery-ui.overrides.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | .ui-state-active, 27 | .ui-widget-content .ui-state-active, 28 | .ui-widget-header .ui-state-active, 29 | a.ui-button:active, 30 | .ui-button:active, 31 | .ui-button.ui-state-active:hover { 32 | /* Overrides the color of selection used in jQuery UI */ 33 | background: #F8981D; 34 | border: 1px solid #F8981D; 35 | } 36 | -------------------------------------------------------------------------------- /site/docs/legal/COPYRIGHT: -------------------------------------------------------------------------------- 1 | Please see ..\java.base\COPYRIGHT 2 | -------------------------------------------------------------------------------- /site/docs/legal/LICENSE: -------------------------------------------------------------------------------- 1 | Please see ..\java.base\LICENSE 2 | -------------------------------------------------------------------------------- /site/docs/module-search-index.js: -------------------------------------------------------------------------------- 1 | moduleSearchIndex = [];updateSearchResults(); -------------------------------------------------------------------------------- /site/docs/overview-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Generated Documentation (Untitled) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 |
20 | 23 |

index.html

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /site/docs/overview-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Hierarchy For All Packages

52 | Package Hierarchies: 53 | 61 |
62 |
63 |

Class Hierarchy

64 | 102 |
103 |
104 |
105 |
106 | 107 | 108 | -------------------------------------------------------------------------------- /site/docs/package-search-index.js: -------------------------------------------------------------------------------- 1 | packageSearchIndex = [{"l":"actions"},{"l":"All Packages","u":"allpackages-index.html"},{"l":"api"},{"l":"components"},{"l":"entity"},{"l":"trackers"},{"l":"utils"}];updateSearchResults(); -------------------------------------------------------------------------------- /site/docs/resources/glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/docs/resources/glass.png -------------------------------------------------------------------------------- /site/docs/resources/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/docs/resources/x.png -------------------------------------------------------------------------------- /site/docs/script-dir/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.13.2 - 2023-02-27 2 | * http://jqueryui.com 3 | * Includes: core.css, autocomplete.css, menu.css 4 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;-ms-filter:"alpha(opacity=0)"}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0} -------------------------------------------------------------------------------- /site/docs/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | var moduleSearchIndex; 27 | var packageSearchIndex; 28 | var typeSearchIndex; 29 | var memberSearchIndex; 30 | var tagSearchIndex; 31 | function loadScripts(doc, tag) { 32 | createElem(doc, tag, 'search.js'); 33 | 34 | createElem(doc, tag, 'module-search-index.js'); 35 | createElem(doc, tag, 'package-search-index.js'); 36 | createElem(doc, tag, 'type-search-index.js'); 37 | createElem(doc, tag, 'member-search-index.js'); 38 | createElem(doc, tag, 'tag-search-index.js'); 39 | } 40 | 41 | function createElem(doc, tag, path) { 42 | var script = doc.createElement(tag); 43 | var scriptElement = doc.getElementsByTagName(tag)[0]; 44 | script.src = pathtoroot + path; 45 | scriptElement.parentNode.insertBefore(script, scriptElement); 46 | } 47 | 48 | function show(tableId, selected, columns) { 49 | if (tableId !== selected) { 50 | document.querySelectorAll('div.' + tableId + ':not(.' + selected + ')') 51 | .forEach(function(elem) { 52 | elem.style.display = 'none'; 53 | }); 54 | } 55 | document.querySelectorAll('div.' + selected) 56 | .forEach(function(elem, index) { 57 | elem.style.display = ''; 58 | var isEvenRow = index % (columns * 2) < columns; 59 | elem.classList.remove(isEvenRow ? oddRowColor : evenRowColor); 60 | elem.classList.add(isEvenRow ? evenRowColor : oddRowColor); 61 | }); 62 | updateTabs(tableId, selected); 63 | } 64 | 65 | function updateTabs(tableId, selected) { 66 | document.querySelector('div#' + tableId +' .summary-table') 67 | .setAttribute('aria-labelledby', selected); 68 | document.querySelectorAll('button[id^="' + tableId + '"]') 69 | .forEach(function(tab, index) { 70 | if (selected === tab.id || (tableId === selected && index === 0)) { 71 | tab.className = activeTableTab; 72 | tab.setAttribute('aria-selected', true); 73 | tab.setAttribute('tabindex',0); 74 | } else { 75 | tab.className = tableTab; 76 | tab.setAttribute('aria-selected', false); 77 | tab.setAttribute('tabindex',-1); 78 | } 79 | }); 80 | } 81 | 82 | function switchTab(e) { 83 | var selected = document.querySelector('[aria-selected=true]'); 84 | if (selected) { 85 | if ((e.keyCode === 37 || e.keyCode === 38) && selected.previousSibling) { 86 | // left or up arrow key pressed: move focus to previous tab 87 | selected.previousSibling.click(); 88 | selected.previousSibling.focus(); 89 | e.preventDefault(); 90 | } else if ((e.keyCode === 39 || e.keyCode === 40) && selected.nextSibling) { 91 | // right or down arrow key pressed: move focus to next tab 92 | selected.nextSibling.click(); 93 | selected.nextSibling.focus(); 94 | e.preventDefault(); 95 | } 96 | } 97 | } 98 | 99 | var updateSearchResults = function() {}; 100 | 101 | function indexFilesLoaded() { 102 | return moduleSearchIndex 103 | && packageSearchIndex 104 | && typeSearchIndex 105 | && memberSearchIndex 106 | && tagSearchIndex; 107 | } 108 | 109 | // Workaround for scroll position not being included in browser history (8249133) 110 | document.addEventListener("DOMContentLoaded", function(e) { 111 | var contentDiv = document.querySelector("div.flex-content"); 112 | window.addEventListener("popstate", function(e) { 113 | if (e.state !== null) { 114 | contentDiv.scrollTop = e.state; 115 | } 116 | }); 117 | window.addEventListener("hashchange", function(e) { 118 | history.replaceState(contentDiv.scrollTop, document.title); 119 | }); 120 | contentDiv.addEventListener("scroll", function(e) { 121 | var timeoutID; 122 | if (!timeoutID) { 123 | timeoutID = setTimeout(function() { 124 | history.replaceState(contentDiv.scrollTop, document.title); 125 | timeoutID = null; 126 | }, 100); 127 | } 128 | }); 129 | if (!location.hash) { 130 | history.replaceState(contentDiv.scrollTop, document.title); 131 | } 132 | }); 133 | -------------------------------------------------------------------------------- /site/docs/serialized-form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Serialized Form 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Serialized Form

52 |
53 |
    54 |
  • 55 |
    56 |

    Package entity

    57 |
      58 |
    • 59 |
      60 |

      Class entity.Config

      61 |
      class Config extends Object implements Serializable
      62 |
        63 |
      • 64 |
        65 |

        Serialized Fields

        66 |
          67 |
        • 68 |
          checkBoxes
          69 |
          List<Boolean> checkBoxes
          70 |
        • 71 |
        • 72 |
          dataOutputPath
          73 |
          String dataOutputPath
          74 |
        • 75 |
        • 76 |
          eyeTrackerDevice
          77 |
          Integer eyeTrackerDevice
          78 |
        • 79 |
        • 80 |
          labels
          81 |
          List<String> labels
          82 |
        • 83 |
        • 84 |
          pythonInterpreter
          85 |
          String pythonInterpreter
          86 |
        • 87 |
        • 88 |
          sampleFreq
          89 |
          Double sampleFreq
          90 |
        • 91 |
        92 |
        93 |
      • 94 |
      95 |
      96 |
    • 97 |
    98 |
    99 |
  • 100 |
101 |
102 |
103 |
104 | 105 | 106 | -------------------------------------------------------------------------------- /site/docs/tag-search-index.js: -------------------------------------------------------------------------------- 1 | tagSearchIndex = [{"l":"Serialized Form","h":"","u":"serialized-form.html"}];updateSearchResults(); -------------------------------------------------------------------------------- /site/docs/trackers/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | trackers 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 56 |
57 |
58 |
59 |

Package trackers

60 |
61 |
62 |
package trackers
63 |
64 |
    65 |
  • 66 |
    67 |
    Classes
    68 |
    69 |
    Class
    70 |
    Description
    71 | 72 |
    73 |
    This class is the eye tracker.
    74 |
    75 | 76 |
    77 |
    This class is the IDE tracker.
    78 |
    79 | 80 |
    81 |
    This class is the screen recorder.
    82 |
    83 |
    84 |
    85 |
  • 86 |
87 |
88 |
89 |
90 |
91 | 92 | 93 | -------------------------------------------------------------------------------- /site/docs/trackers/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | trackers Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Hierarchy For Package trackers

52 | Package Hierarchies: 53 | 56 |
57 |
58 |

Class Hierarchy

59 |
    60 |
  • java.lang.Object 61 |
      62 |
    • trackers.EyeTracker (implements com.intellij.openapi.Disposable)
    • 63 |
    • trackers.IDETracker (implements com.intellij.openapi.Disposable)
    • 64 |
    • trackers.ScreenRecorder
    • 65 |
    66 |
  • 67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /site/docs/type-search-index.js: -------------------------------------------------------------------------------- 1 | typeSearchIndex = [{"p":"actions","l":"AddLabelAction"},{"p":"actions","l":"AddLabelActionGroup"},{"p":"components","l":"AlertDialog"},{"l":"All Classes and Interfaces","u":"allclasses-index.html"},{"p":"utils","l":"AvailabilityChecker"},{"p":"entity","l":"Config"},{"p":"actions","l":"ConfigAction"},{"p":"components","l":"ConfigDialog"},{"p":"trackers","l":"EyeTracker"},{"p":"trackers","l":"IDETracker"},{"p":"utils","l":"OSDetector"},{"p":"actions","l":"PauseResumeTrackingAction"},{"p":"api","l":"RealtimeDataImpl"},{"p":"utils","l":"RelativePathGetter"},{"p":"trackers","l":"ScreenRecorder"},{"p":"actions","l":"StartStopTrackingAction"},{"p":"utils","l":"XMLWriter"}];updateSearchResults(); -------------------------------------------------------------------------------- /site/docs/utils/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | utils 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 56 |
57 |
58 |
59 |

Package utils

60 |
61 |
62 |
package utils
63 |
64 |
    65 |
  • 66 |
    67 |
    Classes
    68 |
    69 |
    Class
    70 |
    Description
    71 | 72 |
    73 |
    This class is used to check the availability of the python environment and the eye-tracking device, and to get the eye tracker name and the available frequencies.
    74 |
    75 | 76 |
    77 |
    This class is used to detect the operating system.
    78 |
    79 | 80 |
    81 |
    This class is used to get the relative path of a file compared to the project path.
    82 |
    83 | 84 |
    85 |
    This class is used to write the XML document to the XML file.
    86 |
    87 |
    88 |
    89 |
  • 90 |
91 |
92 |
93 |
94 |
95 | 96 | 97 | -------------------------------------------------------------------------------- /site/docs/utils/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | utils Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 24 |
25 | 48 |
49 |
50 |
51 |

Hierarchy For Package utils

52 | Package Hierarchies: 53 | 56 |
57 |
58 |

Class Hierarchy

59 | 69 |
70 |
71 |
72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /site/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: FAQ 3 | icon: question 4 | order: 0 5 | --- 6 | 7 | # Frequently Asked Questions (FAQ) 8 | 9 | #### Q1. What is the difference between CodeGRITS and iTrace? 10 | 11 | [iTrace](https://www.i-trace.org/) is a similar tool to CodeGRITS for collecting developers' eye gaze data in several 12 | IDEs, including Eclipse, Visual Studio, and Atom. However, CodeGRITS is built for JetBrains IDEs, which have increased 13 | popularity in the industry and academia. 14 | 15 | CodeGRITS also provides a set of extra functionalities, notably IDE tracking and screen recording, for empirical SE 16 | researchers. See [Trackers](usage.md#trackers) for more details. 17 | 18 | #### Q2. Can I use CodeGRITS without an eye-tracking device? 19 | 20 | Yes. CodeGRITS provides mouse simulation as a substitute for eye gaze. You could also uncheck the `Eye Tracking` option 21 | in the configuration window to disable the eye tracker to only use the IDE tracker and screen recorder. 22 | 23 | #### Q3. How to integrate other eye-tracking devices with CodeGRITS? 24 | 25 | See [Accommodating New Eye Trackers](developer.md#accommodating-new-eye-trackers). 26 | 27 | #### Q4. How to use CodeGRITS in other JetBrains IDEs? 28 | 29 | See [Accommodating New IDEs](developer.md#accommodating-new-ides). 30 | 31 | #### Q5. How efficient is the processing of raw eye gaze data? 32 | 33 | In CodeGRITS, the efficiency of the processing of gaze data is negligible. For each gaze, we calculate the average time 34 | from the timestamp in the raw data to the timestamp after all processing is complete. This processing primarily involves 35 | location mapping and upward traversal of the AST. The average delay is 4.32 ms, equating to delays of approximately 36 | 12.98% for 30Hz, 25.96% for 60Hz, and 51.92% for 120Hz eye gaze data. With such 37 | a high sampling frequency, meaningful changes in the content of the code editor's page are extremely rare within this 38 | short time frame, which ensures the accuracy of gaze processing. Moreover, compared 39 | to [iTrace](https://www.i-trace.org/), our method of receiving data from the 40 | eye-tracking device is more efficient, ensuring that all sampled gazes are mapped without any loss. 41 | 42 | #### Q6. How much storage space does CodeGRITS require? 43 | 44 | Generally speaking, a high sample frequency generates a large amount of gaze data. To conserve storage space, we 45 | only perform upward traversal of the AST of the first gaze and record the hierarchy structure, and mark the rest 46 | as `same`. This approach significantly reduces storage space. In a previous debugging study, we set the eye-tracking 47 | device's sample frequency to 60Hz, and during the 20-minute experiment, the eye-tracking data amounted to only about 48 | 40MB. 49 | 50 | #### Q7. Which eye gazes can be analyzed, and can gazes on the UI be understood? 51 | 52 | In CodeGRITS, we only analyze gazes within the code editor. For example, in the following figure, only gazes within the 53 | red rectangle are mapped to the source code tokens and performed the upward traversal of the AST. For gazes outside the 54 | code editor, such as those on the file explorer, menubar, tool window, console, etc., we only record their raw 55 | information and add a remark `Fail | Out of Text Editor`. CodeGRITS cannot understand the semantics of gazes on the UI. 56 | 57 |
58 |

59 |
60 | 61 | However, if certain UI elements within the editor are triggered, such as the list for auto-completion or the inline 62 | definition display, CodeGRITS mistakenly interprets the gaze as being directed at the code content below the UI (when it 63 | should actually be on the UI). We have not yet found an appropriate method to resolve this issue, which may lead to 64 | inaccuracies in some gaze analyses. -------------------------------------------------------------------------------- /site/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/favicon.png -------------------------------------------------------------------------------- /site/google3c26aad8347fc661.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google3c26aad8347fc661.html -------------------------------------------------------------------------------- /site/license.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | --- 4 | 5 | MIT License 6 | 7 | Copyright (c) 2023 SaNDwich Lab 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. -------------------------------------------------------------------------------- /site/rename.py: -------------------------------------------------------------------------------- 1 | """ 2 | Rename all the `ClassName.html` to `classname.html` in the /docs folder. 3 | This is to make sure that the links in the JavaDoc is useful after the 4 | processing by Retype. 5 | """ 6 | import os 7 | 8 | 9 | def get_candidate_list(path): 10 | candidate_list = [] 11 | for root, dirs, files in os.walk(path): 12 | for file in files: 13 | if file.endswith('.java'): 14 | candidate_list.append(file.replace('.java', '.html')) 15 | return candidate_list 16 | 17 | 18 | def rename_text(file_path, candidate_list): 19 | with open(file_path, 'r') as f: 20 | content = f.read() 21 | for candidate in candidate_list: 22 | content = content.replace(candidate, candidate.lower()) 23 | with open(file_path, 'w') as f: 24 | f.write(content) 25 | 26 | 27 | if __name__ == '__main__': 28 | path_java = '../src/main/java' 29 | path_docs = './docs' 30 | candidates = get_candidate_list(path_java) 31 | for root, dirs, files in os.walk(path_docs): 32 | for file in files: 33 | if file.endswith('.html'): 34 | rename_text(os.path.join(root, file), candidates) 35 | if file in candidates: 36 | os.rename(os.path.join(root, file), os.path.join(root, file.lower())) 37 | -------------------------------------------------------------------------------- /site/static/add-label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/add-label.png -------------------------------------------------------------------------------- /site/static/ast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/ast.png -------------------------------------------------------------------------------- /site/static/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/config.png -------------------------------------------------------------------------------- /site/static/eye-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/eye-data.png -------------------------------------------------------------------------------- /site/static/eye-tracker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/eye-tracker.png -------------------------------------------------------------------------------- /site/static/ide-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/ide-data.png -------------------------------------------------------------------------------- /site/static/ide-tracker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/ide-tracker.png -------------------------------------------------------------------------------- /site/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/logo.png -------------------------------------------------------------------------------- /site/static/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/overview.png -------------------------------------------------------------------------------- /site/static/paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/paper.pdf -------------------------------------------------------------------------------- /site/static/range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/range.png -------------------------------------------------------------------------------- /site/static/toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/toolbar.png -------------------------------------------------------------------------------- /site/static/visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegrits/CodeGRITS/dda784fc9a31693f3ca0df900443ade7da5baadc/site/static/visualization.png -------------------------------------------------------------------------------- /site/welcome.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Welcome 3 | icon: home 4 | order: 100 5 | --- 6 | 7 | # Welcome to CodeGRITS 8 | 9 | !!! :zap: NEWS! :zap: 10 | We would present CodeGRITS at 11 | [ICSE 2024 Demo Track](https://conf.researchr.org/track/icse-2024/icse-2024-demonstrations?#program). 12 | Welcome to join us and discuss with us about it! 13 | !!! 14 | 15 | CodeGRITS stands for **G**aze **R**ecording & **I**DE **T**racking **S**ystem. It's a plugin developed by 16 | the [SaNDwich Lab](https://toby.li/) and is specially designed for empirical software engineering researchers. 17 | CodeGRITS is built on top 18 | of [IntelliJ Platform SDK](https://plugins.jetbrains.com/docs/intellij/welcome.html), with wide compatibility with the 19 | entire family of JetBrains IDEs and [Tobii eye-tracking devices](https://www.tobii.com/), 20 | to track developers’ IDE interactions and eye gaze data. The source code is available 21 | on [GitHub](https://github.com/codegrits/CodeGRITS) with [Javadoc](docs/index.html) documentation. 22 | 23 |
24 |

25 |
26 | 27 | The data collected by CodeGRITS can be used by empirical SE researchers to understand the behaviors of developers, 28 | especially those related to eye gaze. CodeGRITS also provides a [real-time data API](developer.md) 29 | for future plugin developers and researchers to design context-aware programming support tools. 30 | 31 | !!! 32 | CodeGRITS is still in its developmental stage as a research tool. Our goal is to make it mature enough and beneficial 33 | for the community, particularly for those involved in empirical software engineering and eye tracking research. We 34 | encourage the community to contribute through [GitHub Issue](https://github.com/codegrits/CodeGRITS/issues) for any 35 | suggestions or issues, aiding in its improvement. 36 | 37 | For any inquiries, please email us at ntang@nd.edu or jan2@nd.edu. If you're interested in 38 | using CodeGRITS in your research, don't hesitate to email us for setup support. We are delighted to provide 39 | tailored assistance based on your specific OS and JetBrains IDE environment. 40 | !!! 41 | 42 | ### Cross-platform and Multilingual Support 43 | 44 | - [x] CodeGRITS provides cross-platform support for Windows, macOS, 45 | and Linux, and is expected to be compatible with the entire family of JetBrains IDEs, including IntelliJ IDEA, 46 | PyCharm, WebStorm, etc. 47 | - [x] CodeGRITS could extract the abstract syntax tree (AST) structure of eye gazes on multiple 48 | programming languages, as long as the IDE supports them, including Java, Python, C/C++, JavaScript, etc. 49 | 50 | !!!warning 🚨 macOS Support 🚨 51 | CodeGRITS has been primarily developed and tested on Windows and Linux, with only partial testing on macOS. 52 | There may be some unnoticed bugs on macOS. We have created a 53 | [`mac` branch](https://github.com/codegrits/CodeGRITS/tree/mac) for this and have fixed some known issues. 54 | If you encounter additional issues on macOS, please feel free to report them to us. 55 | !!! 56 | 57 | ## Key Features 58 | 59 | - :mag: **IDE Tracking**: CodeGRITS tracks developers’ IDE interactions, including mouse clicks, keyboard inputs, etc. 60 | - :eye: **Eye Tracking**: CodeGRITS tracks developers’ eye gaze data 61 | from [Tobii eye-tracking devices](https://www.tobii.com/), and maps them to corresponding source code elements. 62 | - :computer: **Screen Recording**: CodeGRITS simultaneously records developers’ screen for visualizing their behaviors. 63 | - 🔨 **Research Toolkit**: CodeGRITS provides a set of extra features for empirical SE 64 | researchers, including dynamic configuration, activity labeling, real-time data API, etc. 65 | - 🗃️ **Data Export**: CodeGRITS exports data in XML format for further data analysis. See [Data Format](data.md) 66 | for more details. 67 | 68 |
69 | 70 |
71 | 72 | ## Citation 73 | 74 | The paper of CodeGRITS has been accepted 75 | by [ICSE 2024 Demonstrations Track](https://conf.researchr.org/track/icse-2024/icse-2024-demonstrations). 76 | The PDF version is available [here](static/paper.pdf). 77 | Please cite the following if you use CodeGRITS in your research. 78 | 79 | ```bibtex 80 | @inproceedings{tang2024codegrits, 81 | title={CodeGRITS: A Research Toolkit for Developer Behavior and Eye Tracking in IDE}, 82 | author={Tang, Ningzhi and An, Junwen and Chen, Meng and Bansal, Aakash and Huang, Yu and McMillan, Collin and Li, Toby Jia-Jun}, 83 | booktitle={46th International Conference on Software Engineering Companion (ICSE-Companion '24)}, 84 | year={2024}, 85 | organization={ACM} 86 | } 87 | ``` -------------------------------------------------------------------------------- /src/main/java/actions/AddLabelAction.java: -------------------------------------------------------------------------------- 1 | package actions; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.notification.NotificationType; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * This class is the action for adding labels. 11 | */ 12 | public class AddLabelAction extends AnAction { 13 | 14 | private String description; 15 | private static boolean isEnabled = false; 16 | 17 | @Override 18 | public void update(@NotNull AnActionEvent e) { 19 | e.getPresentation().setText(description); 20 | e.getPresentation().setEnabled(isEnabled); 21 | } 22 | 23 | /** 24 | * This method is called when the action is performed. It will show a notification to indicate that the label is successfully added. 25 | * 26 | * @param e The action event. 27 | */ 28 | @Override 29 | public void actionPerformed(@NotNull AnActionEvent e) { 30 | Notification notification = new Notification("CodeGRITS Notification Group", "Add label", 31 | "Successfully add label \"" + description + "\"!", NotificationType.INFORMATION); 32 | notification.notify(e.getProject()); 33 | } 34 | 35 | public void setDescription(String description) { 36 | this.description = description; 37 | } 38 | 39 | public static void setIsEnabled(boolean isEnabled) { 40 | AddLabelAction.isEnabled = isEnabled; 41 | } 42 | 43 | @Override 44 | public @NotNull String getTemplateText() { 45 | return description; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/actions/AddLabelActionGroup.java: -------------------------------------------------------------------------------- 1 | package actions; 2 | 3 | import com.intellij.openapi.actionSystem.*; 4 | import entity.Config; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * This class is the action group for adding labels. 11 | */ 12 | public class AddLabelActionGroup extends DefaultActionGroup { 13 | 14 | private static boolean isEnabled = true; 15 | private boolean defaultLabelsLoaded = false; 16 | 17 | /** 18 | * This method is called when the action is performed. It will register all the {@link AddLabelAction}s from the configuration file. 19 | * 20 | * @param e The action event. 21 | */ 22 | @Override 23 | public void update(@NotNull AnActionEvent e) { 24 | Config config = new Config(); 25 | if (!defaultLabelsLoaded && config.configExists()) { 26 | config.loadFromJson(); 27 | ActionManager actionManager = ActionManager.getInstance(); 28 | DefaultActionGroup actionGroup = (DefaultActionGroup) actionManager.getAction("CodeGRITS.AddLabelActionGroup"); 29 | actionGroup.removeAll(); 30 | List labels = config.getLabels(); 31 | for (String label : labels) { 32 | AddLabelAction addLabelAction = new AddLabelAction(); 33 | addLabelAction.setDescription(label); 34 | actionManager.registerAction("CodeGRITS.AddLabel.[" + label + "]", addLabelAction); 35 | actionGroup.add(addLabelAction); 36 | } 37 | defaultLabelsLoaded = true; 38 | } 39 | } 40 | 41 | public static void setIsEnabled(boolean isEnabled) { 42 | AddLabelActionGroup.isEnabled = isEnabled; 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/actions/ConfigAction.java: -------------------------------------------------------------------------------- 1 | package actions; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.project.Project; 6 | import components.ConfigDialog; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * This class is the action for configuring the application. 13 | */ 14 | public class ConfigAction extends AnAction { 15 | 16 | private static boolean isEnabled = true; 17 | 18 | @Override 19 | public void update(@NotNull AnActionEvent e) { 20 | e.getPresentation().setEnabled(isEnabled); 21 | } 22 | 23 | /** 24 | * This method is called when the action is performed. It will show the configuration dialog. 25 | * 26 | * @param e The action event. 27 | */ 28 | @Override 29 | public void actionPerformed(@NotNull AnActionEvent e) { 30 | Project project = e.getProject(); 31 | ConfigDialog configDialog; 32 | try { 33 | configDialog = new ConfigDialog(project); 34 | } catch (IOException | InterruptedException ex) { 35 | throw new RuntimeException(ex); 36 | } 37 | configDialog.show(); 38 | } 39 | 40 | public static void setIsEnabled(boolean isEnabled) { 41 | ConfigAction.isEnabled = isEnabled; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/actions/PauseResumeTrackingAction.java: -------------------------------------------------------------------------------- 1 | package actions; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import org.jetbrains.annotations.NotNull; 6 | import trackers.ScreenRecorder; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * This class is the action for pausing/resuming tracking. 12 | */ 13 | public class PauseResumeTrackingAction extends AnAction { 14 | private final ScreenRecorder screenRecorder = ScreenRecorder.getInstance(); 15 | 16 | /** 17 | * Update the text of the action button. If the tracking is not started, the button is disabled. 18 | * 19 | * @param e The action event. 20 | */ 21 | @Override 22 | public void update(@NotNull AnActionEvent e) { 23 | if (StartStopTrackingAction.isTracking()) { 24 | e.getPresentation().setEnabled(true); 25 | if (StartStopTrackingAction.isPaused()) { 26 | e.getPresentation().setText("Resume Tracking"); 27 | } else { 28 | e.getPresentation().setText("Pause Tracking"); 29 | } 30 | } else { 31 | e.getPresentation().setText("Pause Tracking"); 32 | e.getPresentation().setEnabled(false); 33 | } 34 | } 35 | 36 | /** 37 | * This method is called when the action is performed. It will pause/resume tracking. 38 | * 39 | * @param e The action event. 40 | */ 41 | @Override 42 | public void actionPerformed(@NotNull AnActionEvent e) { 43 | if (StartStopTrackingAction.isPaused()) { 44 | screenRecorder.resumeRecording(); 45 | StartStopTrackingAction.resumeTracking(); 46 | AddLabelAction.setIsEnabled(true); 47 | ConfigAction.setIsEnabled(false); 48 | 49 | } else { 50 | StartStopTrackingAction.pauseTracking(); 51 | ConfigAction.setIsEnabled(false); 52 | AddLabelAction.setIsEnabled(false); 53 | try { 54 | screenRecorder.pauseRecording(); 55 | } catch (IOException ex) { 56 | throw new RuntimeException(ex); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/actions/StartStopTrackingAction.java: -------------------------------------------------------------------------------- 1 | package actions; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.notification.NotificationType; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import components.ConfigDialog; 8 | import entity.Config; 9 | import org.jetbrains.annotations.NotNull; 10 | import trackers.EyeTracker; 11 | import trackers.IDETracker; 12 | import trackers.ScreenRecorder; 13 | import utils.AvailabilityChecker; 14 | 15 | import javax.swing.*; 16 | import javax.xml.parsers.ParserConfigurationException; 17 | import javax.xml.transform.TransformerException; 18 | import java.io.IOException; 19 | import java.util.Objects; 20 | 21 | /** 22 | * This class is the action for starting/stopping tracking. 23 | */ 24 | public class StartStopTrackingAction extends AnAction { 25 | 26 | /** 27 | * This variable indicates whether the tracking is started. 28 | */ 29 | private static boolean isTracking = false; 30 | /** 31 | * This variable is the IDE tracker. 32 | */ 33 | private static IDETracker iDETracker; 34 | /** 35 | * This variable is the eye tracker. 36 | */ 37 | private static EyeTracker eyeTracker; 38 | /** 39 | * This variable is the screen recorder. 40 | */ 41 | private final ScreenRecorder screenRecorder = ScreenRecorder.getInstance(); 42 | /** 43 | * This variable is the configuration. 44 | */ 45 | Config config = new Config(); 46 | 47 | /** 48 | * Update the text of the action button. 49 | * 50 | * @param e The action event. 51 | */ 52 | @Override 53 | public void update(@NotNull AnActionEvent e) { 54 | e.getPresentation().setText(isTracking ? "Stop Tracking" : "Start Tracking"); 55 | } 56 | 57 | /** 58 | * This method is called when the action is performed. It will start/stop tracking. 59 | * 60 | * @param e The action event. 61 | */ 62 | @Override 63 | public void actionPerformed(@NotNull AnActionEvent e) { 64 | if (config.configExists()) { 65 | config.loadFromJson(); 66 | } else { 67 | Notification notification = new Notification("CodeGRITS Notification Group", "Configuration", 68 | "Please configure the plugin first.", NotificationType.WARNING); 69 | notification.notify(e.getProject()); 70 | return; 71 | } 72 | try { 73 | if (!isTracking) { 74 | if (config.getCheckBoxes().get(1)) { 75 | if (!AvailabilityChecker.checkPythonEnvironment(config.getPythonInterpreter())) { 76 | JOptionPane.showMessageDialog(null, "Python interpreter not found. Please configure the plugin first."); 77 | return; 78 | } 79 | if (config.getEyeTrackerDevice() != 0 && !AvailabilityChecker.checkEyeTracker(config.getPythonInterpreter())) { 80 | JOptionPane.showMessageDialog(null, "Eye tracker not found. Please configure the mouse simulation first."); 81 | return; 82 | } 83 | } 84 | 85 | isTracking = true; 86 | ConfigAction.setIsEnabled(false); 87 | AddLabelActionGroup.setIsEnabled(true); 88 | String projectPath = e.getProject() != null ? e.getProject().getBasePath() : ""; 89 | String realDataOutputPath = Objects.equals(config.getDataOutputPath(), ConfigDialog.selectDataOutputPlaceHolder) 90 | ? projectPath : config.getDataOutputPath(); 91 | realDataOutputPath += "/" + System.currentTimeMillis() + "/"; 92 | 93 | if (config.getCheckBoxes().get(2)) { 94 | screenRecorder.setDataOutputPath(realDataOutputPath); 95 | screenRecorder.startRecording(); 96 | } 97 | 98 | iDETracker = IDETracker.getInstance(); 99 | iDETracker.setProjectPath(projectPath); 100 | iDETracker.setDataOutputPath(realDataOutputPath); 101 | iDETracker.startTracking(e.getProject()); 102 | 103 | if (config.getCheckBoxes().get(1)) { 104 | eyeTracker = new EyeTracker(); 105 | eyeTracker.setProjectPath(projectPath); 106 | eyeTracker.setDataOutputPath(realDataOutputPath); 107 | eyeTracker.setPythonInterpreter(config.getPythonInterpreter()); 108 | eyeTracker.setSampleFrequency(config.getSampleFreq()); 109 | eyeTracker.setDeviceIndex(config.getEyeTrackerDevice()); 110 | eyeTracker.setPythonScriptTobii(); 111 | eyeTracker.setPythonScriptMouse(); 112 | eyeTracker.startTracking(e.getProject()); 113 | } 114 | AddLabelAction.setIsEnabled(true); 115 | 116 | } else { 117 | isTracking = false; 118 | iDETracker.stopTracking(); 119 | AddLabelAction.setIsEnabled(false); 120 | ConfigAction.setIsEnabled(true); 121 | if (config.getCheckBoxes().get(1) && eyeTracker != null) { 122 | eyeTracker.stopTracking(); 123 | } 124 | if (config.getCheckBoxes().get(2)) { 125 | screenRecorder.stopRecording(); 126 | } 127 | eyeTracker = null; 128 | } 129 | } catch (ParserConfigurationException | TransformerException | IOException | InterruptedException ex) { 130 | throw new RuntimeException(ex); 131 | } 132 | } 133 | 134 | public static boolean isTracking() { 135 | return isTracking; 136 | } 137 | 138 | public static boolean isPaused() { 139 | return !iDETracker.isTracking(); 140 | } 141 | 142 | public static void pauseTracking() { 143 | iDETracker.pauseTracking(); 144 | if (eyeTracker != null) { 145 | eyeTracker.pauseTracking(); 146 | } 147 | } 148 | 149 | public static void resumeTracking() { 150 | iDETracker.resumeTracking(); 151 | if (eyeTracker != null) { 152 | eyeTracker.resumeTracking(); 153 | } 154 | } 155 | 156 | } -------------------------------------------------------------------------------- /src/main/java/api/RealtimeDataImpl.java: -------------------------------------------------------------------------------- 1 | package api; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import trackers.EyeTracker; 5 | import trackers.IDETracker; 6 | 7 | import javax.xml.parsers.ParserConfigurationException; 8 | import javax.xml.transform.TransformerException; 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.net.Socket; 14 | import java.util.function.Consumer; 15 | 16 | /** 17 | * This class provides the API for getting real-time data from the IDE and eye tracker. 18 | */ 19 | public class RealtimeDataImpl { 20 | 21 | // make it singleton 22 | private static RealtimeDataImpl realtimeData = new RealtimeDataImpl(); 23 | Socket socket; 24 | InputStream dataInputStream; 25 | private Consumer ideTrackerDataHandler; 26 | private Consumer eyeTrackerDataHandler; 27 | private static IDETracker ideTracker; 28 | private static EyeTracker eyeTracker; 29 | 30 | private RealtimeDataImpl() { 31 | } 32 | 33 | public static RealtimeDataImpl getInstance() { 34 | return realtimeData; 35 | } 36 | 37 | public void checkEnvironment() { 38 | System.out.println("Hello World!"); 39 | } 40 | 41 | public void getRawIDETrackerData(Project project) throws ParserConfigurationException { 42 | ideTracker = IDETracker.getInstance(); 43 | ideTracker.startTracking(project); 44 | } 45 | 46 | public void getRawEyeTrackerData() { 47 | 48 | } 49 | 50 | public void stopIDETrackerData() throws TransformerException { 51 | ideTracker.stopTracking(); 52 | } 53 | 54 | public void stopEyeTrackerData() { 55 | 56 | } 57 | 58 | public void getHandledIDETrackerData(Project project) throws ParserConfigurationException, IOException { 59 | if (ideTrackerDataHandler == null) { 60 | return; 61 | } 62 | ideTracker = IDETracker.getInstance(); 63 | ideTracker.setIsRealTimeDataTransmitting(true); 64 | ideTracker.startTracking(project); 65 | Thread ideTrackerThread = new Thread(() -> { 66 | try { 67 | 68 | Thread.sleep(2000); 69 | Socket socket = new Socket("localhost", 12346); 70 | InputStream dataInputStream = socket.getInputStream(); 71 | InputStreamReader inputStreamReader = new InputStreamReader(dataInputStream); 72 | BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 73 | while (true) { 74 | String line = bufferedReader.readLine(); 75 | System.out.println(ideTrackerDataHandler.toString()); 76 | // ideTrackerDataHandler.accept(line); 77 | System.out.println(line); 78 | } 79 | } catch (IOException | InterruptedException e) { 80 | throw new RuntimeException(e); 81 | } 82 | }); 83 | ideTrackerThread.start(); 84 | } 85 | 86 | public void getHandledEyeTrackerData() { 87 | if (eyeTrackerDataHandler == null) { 88 | throw new RuntimeException("Eye Tracker Data Handler not set!"); 89 | } 90 | } 91 | 92 | public void setIDETrackerDataHandler(Consumer ideTrackerDataHandler) { 93 | this.ideTrackerDataHandler = ideTrackerDataHandler; 94 | } 95 | 96 | public void setEyeTrackerDataHandler(Consumer eyeTrackerDataHandler) { 97 | this.eyeTrackerDataHandler = eyeTrackerDataHandler; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/components/AlertDialog.java: -------------------------------------------------------------------------------- 1 | package components; 2 | 3 | import com.intellij.openapi.ui.DialogWrapper; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.*; 8 | import java.awt.*; 9 | 10 | /** 11 | * This class is used to create a dialog to show alert message. 12 | */ 13 | public class AlertDialog extends DialogWrapper { 14 | /** 15 | * The label of the alert message. 16 | */ 17 | private final String label; 18 | /** 19 | * The icon of the alert message. 20 | */ 21 | private final Icon icon; 22 | 23 | /** 24 | * The constructor of the class. 25 | * 26 | * @param label The alert message. 27 | * @param icon The icon of the alert message. 28 | */ 29 | public AlertDialog(String label, Icon icon) { 30 | 31 | super(true); // use current window as parent 32 | this.label = label; 33 | this.icon = icon; 34 | init(); 35 | setTitle("Alert"); 36 | 37 | } 38 | 39 | /** 40 | * Create the OK button. 41 | * 42 | * @return The OK button. 43 | */ 44 | @Override 45 | protected Action @NotNull [] createActions() { 46 | return new Action[]{getOKAction()}; 47 | } 48 | 49 | /** 50 | * Create the center panel of the dialog. 51 | * 52 | * @return The center panel of the dialog. 53 | */ 54 | @Override 55 | protected @Nullable JComponent createCenterPanel() { 56 | JPanel dialogPanel = new JPanel(); 57 | dialogPanel.setLayout(new BoxLayout(dialogPanel, BoxLayout.Y_AXIS)); 58 | 59 | JLabel label = new JLabel(this.label); 60 | JLabel icon = new JLabel(); 61 | label.setIcon(this.icon); 62 | 63 | icon.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 5)); 64 | label.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); 65 | label.setAlignmentX(Component.CENTER_ALIGNMENT); 66 | icon.setAlignmentX(Component.CENTER_ALIGNMENT); 67 | dialogPanel.add(label); 68 | 69 | return dialogPanel; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/entity/Config.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonParser; 7 | import com.google.gson.reflect.TypeToken; 8 | import com.intellij.openapi.application.PathManager; 9 | 10 | import java.io.FileReader; 11 | import java.io.FileWriter; 12 | import java.io.Serializable; 13 | import java.util.List; 14 | 15 | /** 16 | * This class is used to store the configuration of the application. 17 | */ 18 | public class Config implements Serializable { 19 | private List checkBoxes; 20 | private List labels; 21 | private Double sampleFreq; 22 | private String pythonInterpreter; 23 | private String dataOutputPath; 24 | private Integer eyeTrackerDevice; 25 | 26 | /** 27 | * The constructor of the Config class. 28 | * 29 | * @param checkBoxes The list of the checkboxes. 30 | * @param labels The list of the labels. 31 | * @param sampleFreq The sample frequency. 32 | * @param pythonInterpreter The path of the python interpreter. 33 | * @param dataOutputPath The path of the data output folder. 34 | * @param eyeTrackerDevice The index of the eye tracker device. 35 | */ 36 | public Config(List checkBoxes, List labels, Double sampleFreq, String pythonInterpreter, String dataOutputPath, Integer eyeTrackerDevice) { 37 | this.checkBoxes = checkBoxes; 38 | this.labels = labels; 39 | this.sampleFreq = sampleFreq; 40 | this.pythonInterpreter = pythonInterpreter; 41 | this.dataOutputPath = dataOutputPath; 42 | this.eyeTrackerDevice = eyeTrackerDevice; 43 | } 44 | 45 | /** 46 | * The constructor of the Config class. 47 | */ 48 | public Config() { 49 | } 50 | 51 | public boolean configExists() { 52 | try (FileReader fileReader = new FileReader(PathManager.getPluginsPath() + "/config.json")) { 53 | return true; 54 | } catch (Exception e) { 55 | return false; 56 | } 57 | } 58 | 59 | /** 60 | * Save the configuration as a JSON file. 61 | */ 62 | public void saveAsJson() { 63 | JsonObject jsonObject = new JsonObject(); 64 | if (sampleFreq == null) sampleFreq = 30.0; 65 | jsonObject.addProperty("pythonInterpreter", pythonInterpreter); 66 | jsonObject.addProperty("sampleFreq", sampleFreq); 67 | jsonObject.addProperty("labels", labels.toString()); 68 | jsonObject.addProperty("checkBoxes", checkBoxes.toString()); 69 | jsonObject.addProperty("dataOutputPath", dataOutputPath); 70 | jsonObject.addProperty("eyeTrackerDevice", eyeTrackerDevice); 71 | 72 | Gson gson = new Gson(); 73 | try (FileWriter fileWriter = new FileWriter(PathManager.getPluginsPath() + "/config.json")) { 74 | System.out.println(PathManager.getPluginsPath() + "/config.json"); 75 | fileWriter.write(gson.toJson(jsonObject)); 76 | } catch (Exception e) { 77 | throw new RuntimeException(e); 78 | } 79 | } 80 | 81 | /** 82 | * Load the configuration from the JSON file. 83 | */ 84 | public void loadFromJson() { 85 | try (FileReader fileReader = new FileReader(PathManager.getPluginsPath() + "/config.json")) { 86 | Gson gson = new Gson(); 87 | JsonElement jsonElement = JsonParser.parseReader(fileReader); 88 | JsonObject jsonObject = jsonElement.getAsJsonObject(); 89 | pythonInterpreter = jsonObject.get("pythonInterpreter").getAsString(); 90 | sampleFreq = jsonObject.get("sampleFreq").getAsDouble(); 91 | dataOutputPath = jsonObject.get("dataOutputPath").getAsString(); 92 | eyeTrackerDevice = jsonObject.get("eyeTrackerDevice").getAsInt(); 93 | String labelsString = jsonObject.get("labels").getAsString().substring(1, jsonObject.get("labels").getAsString().length() - 1); 94 | if (labelsString.equals("")) { 95 | labels = List.of(); 96 | } else labels = List.of(labelsString.split(", ")); 97 | checkBoxes = gson.fromJson(jsonObject.get("checkBoxes").getAsString(), new TypeToken>() { 98 | }.getType()); 99 | } catch (Exception e) { 100 | throw new RuntimeException(e); 101 | } 102 | } 103 | 104 | public String getPythonInterpreter() { 105 | return pythonInterpreter; 106 | } 107 | 108 | public Double getSampleFreq() { 109 | return sampleFreq; 110 | } 111 | 112 | public List getCheckBoxes() { 113 | return checkBoxes; 114 | } 115 | 116 | public List getLabels() { 117 | return labels; 118 | } 119 | 120 | public String getDataOutputPath() { 121 | return dataOutputPath; 122 | } 123 | 124 | public Integer getEyeTrackerDevice() { 125 | return eyeTrackerDevice; 126 | } 127 | 128 | public String toString() { 129 | return "Config{" + 130 | "checkBoxes=" + checkBoxes + 131 | ", labels=" + labels + 132 | ", sampleFreq=" + sampleFreq + 133 | ", pythonInterpreter='" + pythonInterpreter + '\'' + 134 | ", dataOutputPath='" + dataOutputPath + '\'' + 135 | ", eyeTrackerDevice=" + eyeTrackerDevice + 136 | '}'; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/trackers/ScreenRecorder.java: -------------------------------------------------------------------------------- 1 | package trackers; 2 | 3 | import com.opencsv.CSVWriter; 4 | import org.bytedeco.ffmpeg.global.avcodec; 5 | import org.bytedeco.javacv.*; 6 | import org.bytedeco.javacv.Frame; 7 | 8 | import java.awt.*; 9 | import java.io.File; 10 | import java.io.FileWriter; 11 | import java.io.IOException; 12 | import java.util.ArrayList; 13 | import java.util.Timer; 14 | import java.util.TimerTask; 15 | 16 | /** 17 | * This class is the screen recorder. 18 | */ 19 | public class ScreenRecorder { 20 | 21 | /** 22 | * This variable indicates the state of the screen recorder. 0: initial state; only startAction enabled 1: started, not paused; stopAction and pauseAction enabled 2: started, paused; only resumeAction enabled 23 | */ 24 | int state = 0; 25 | /** 26 | * This variable indicates the frame rate of the screen recorder. 27 | */ 28 | int frameRate = 4; // higher frame rate (e.g., 12) will result in larger file size and blurry video 29 | private FrameRecorder recorder; 30 | private FrameGrabber grabber; 31 | private final ArrayList timeList = new ArrayList<>(); 32 | private CSVWriter csvWriter; 33 | boolean isRecording = false; 34 | /** 35 | * This variable indicates the current clip number. 36 | */ 37 | private int clipNumber = 1; 38 | /** 39 | * This variable indicates the current frame number. 40 | */ 41 | private int frameNumber = 0; 42 | private String dataOutputPath = ""; 43 | private static ScreenRecorder instance = null; 44 | 45 | public static ScreenRecorder getInstance() { 46 | if (instance == null) { 47 | instance = new ScreenRecorder(); 48 | } 49 | return instance; 50 | } 51 | 52 | /** 53 | * Create the encoder using {@link FFmpegFrameGrabber} and {@link FFmpegFrameRecorder}. 54 | */ 55 | private void createEncoder() throws IOException { 56 | // avfoundation for macOS, gdigrab for Windows, xcbgrab for Linux 57 | if (utils.OSDetector.isMac()) { 58 | grabber = new FFmpegFrameGrabber("1"); 59 | grabber.setFormat("avfoundation"); 60 | } else if (utils.OSDetector.isWindows()) { 61 | grabber = new FFmpegFrameGrabber("desktop"); 62 | grabber.setFormat("gdigrab"); 63 | } else if (utils.OSDetector.isUnix()) { 64 | grabber = new FFmpegFrameGrabber(":0.0"); 65 | grabber.setFormat("x11grab"); 66 | } else { 67 | throw new IOException("Unsupported OS"); 68 | } 69 | grabber.setFrameRate(frameRate); 70 | GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment(). 71 | getDefaultScreenDevice().getDefaultConfiguration(); 72 | // set image width and height to be the same as the resolution of the *first* screen (in case of multiple screens) 73 | grabber.setImageWidth((int) (Toolkit.getDefaultToolkit().getScreenSize().width * config.getDefaultTransform().getScaleX())); 74 | grabber.setImageHeight((int) (Toolkit.getDefaultToolkit().getScreenSize().height * config.getDefaultTransform().getScaleY())); 75 | grabber.setOption("offset_x", "0"); 76 | grabber.setOption("offset_y", "0"); 77 | grabber.start(); 78 | 79 | recorder = FrameRecorder.createDefault(dataOutputPath + "/screen_recording/clip_" + clipNumber + ".mp4", grabber.getImageWidth(), grabber.getImageHeight()); 80 | recorder.setFrameRate(frameRate); 81 | recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); 82 | recorder.start(); 83 | } 84 | 85 | /** 86 | * Start recording the screen. Reset the clip number and invoke {@link #recordScreen()}. 87 | */ 88 | public void startRecording() throws IOException { 89 | state = 1; 90 | clipNumber = 1; 91 | timeList.clear(); 92 | isRecording = true; 93 | File file = new File(dataOutputPath + "/screen_recording/frames.csv"); 94 | file.getParentFile().mkdirs(); 95 | csvWriter = new CSVWriter(new FileWriter(file)); 96 | csvWriter.writeNext(new String[]{"timestamp", "frame_number", "clip_number"}); 97 | timeList.add(new String[]{String.valueOf(System.currentTimeMillis()), "Start", String.valueOf(clipNumber)}); 98 | try { 99 | recordScreen(); 100 | } catch (AWTException | IOException e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | 105 | /** 106 | * Stop recording the screen. Write the time list to the CSV file. 107 | */ 108 | public void stopRecording() throws IOException { 109 | state = 0; 110 | isRecording = false; 111 | timeList.add(new String[]{String.valueOf(System.currentTimeMillis()), "Stop", String.valueOf(clipNumber)}); 112 | csvWriter.writeAll(timeList); 113 | csvWriter.close(); 114 | } 115 | 116 | /** 117 | * Pause recording the screen. Write the time list to the CSV file and increment the clip number. 118 | */ 119 | public void pauseRecording() throws IOException { 120 | state = 2; 121 | isRecording = false; 122 | timeList.add(new String[]{String.valueOf(System.currentTimeMillis()), "Pause", String.valueOf(clipNumber)}); 123 | clipNumber++; 124 | } 125 | 126 | /** 127 | * Resume recording the screen. Invoke {@link #recordScreen()}. 128 | */ 129 | public void resumeRecording() { 130 | state = 1; 131 | isRecording = true; 132 | timeList.add(new String[]{String.valueOf(System.currentTimeMillis()), "Resume", String.valueOf(clipNumber)}); 133 | try { 134 | recordScreen(); 135 | } catch (AWTException | IOException e) { 136 | e.printStackTrace(); 137 | } 138 | } 139 | 140 | /** 141 | * Record the screen. Use {@link Timer} to schedule the recording with the given frame rate. 142 | */ 143 | private void recordScreen() throws AWTException, IOException { 144 | createEncoder(); 145 | frameNumber = 0; 146 | Timer timer = new Timer(); 147 | timer.scheduleAtFixedRate(new TimerTask() { 148 | @Override 149 | public void run() { 150 | if (!isRecording) { 151 | try { 152 | grabber.stop(); 153 | recorder.stop(); 154 | grabber.release(); 155 | recorder.release(); 156 | } catch (IOException e) { 157 | throw new RuntimeException(e); 158 | } 159 | timer.cancel(); 160 | } else { 161 | frameNumber++; 162 | timeList.add(new String[]{String.valueOf(System.currentTimeMillis()), String.valueOf(frameNumber), String.valueOf(clipNumber)}); 163 | try { 164 | Frame frame = grabber.grabFrame(); 165 | recorder.record(frame); 166 | } catch (FrameGrabber.Exception | FrameRecorder.Exception e) { 167 | throw new RuntimeException(e); 168 | } 169 | 170 | } 171 | } 172 | }, 0, 1000 / frameRate); 173 | } 174 | 175 | /** 176 | * Set the data output path. 177 | * 178 | * @param dataOutputPath The data output path. 179 | */ 180 | public void setDataOutputPath(String dataOutputPath) { 181 | this.dataOutputPath = dataOutputPath; 182 | } 183 | } -------------------------------------------------------------------------------- /src/main/java/utils/AvailabilityChecker.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.List; 8 | 9 | /** 10 | * This class is used to check the availability of the python environment and the eye-tracking device, and to get the eye tracker name and the available frequencies. 11 | */ 12 | public class AvailabilityChecker { 13 | 14 | /** 15 | * Check the availability of the python environment, i.e., whether the required python packages are installed. 16 | * 17 | * @param pythonInterpreter The path of the python interpreter. 18 | * @return {@code true} if the python environment is available, {@code false} otherwise. 19 | */ 20 | public static boolean checkPythonEnvironment(String pythonInterpreter) throws IOException, InterruptedException { 21 | String pythonScript = """ 22 | import tobii_research as tr 23 | from screeninfo import get_monitors 24 | import pyautogui 25 | import time 26 | import sys 27 | import math 28 | 29 | print('OK') 30 | """; 31 | 32 | String line = runPythonScript(pythonInterpreter, pythonScript); 33 | return line.equals("OK"); 34 | } 35 | 36 | /** 37 | * Check the availability of the eye-tracking device. 38 | * 39 | * @param pythonInterpreter The path of the python interpreter. 40 | * @return {@code true} if the eye-tracking device is available, {@code false} otherwise. 41 | */ 42 | public static boolean checkEyeTracker(String pythonInterpreter) throws IOException, InterruptedException { 43 | String pythonScript = """ 44 | import tobii_research as tr 45 | 46 | found_eyetrackers = tr.find_all_eyetrackers() 47 | if found_eyetrackers == (): 48 | print('Not Found') 49 | else: 50 | print('Found') 51 | """; 52 | 53 | String line = runPythonScript(pythonInterpreter, pythonScript); 54 | return line.equals("Found"); 55 | } 56 | 57 | /** 58 | * Get the name of the eye-tracking device. 59 | * 60 | * @param pythonInterpreter The path of the python interpreter. 61 | * @return The name of the eye tracker. 62 | */ 63 | public static String getEyeTrackerName(String pythonInterpreter) throws IOException, InterruptedException { 64 | String pythonScript = """ 65 | import tobii_research as tr 66 | 67 | found_eyetrackers = tr.find_all_eyetrackers() 68 | if found_eyetrackers == (): 69 | print('Not Found') 70 | else: 71 | print(found_eyetrackers[0].device_name) 72 | """; 73 | 74 | return runPythonScript(pythonInterpreter, pythonScript); 75 | } 76 | 77 | /** 78 | * Get the available frequencies of the eye-tracking device. 79 | * 80 | * @param pythonInterpreter The path of the python interpreter. 81 | * @return The available frequencies of the eye tracker. 82 | */ 83 | public static List getFrequencies(String pythonInterpreter) throws IOException, InterruptedException { 84 | String pythonScript = """ 85 | import tobii_research as tr 86 | 87 | found_eyetrackers = tr.find_all_eyetrackers() 88 | if found_eyetrackers == (): 89 | print('Not Found') 90 | else: 91 | print(found_eyetrackers[0].get_all_gaze_output_frequencies()) 92 | """; 93 | String resultTuple = runPythonScript(pythonInterpreter, pythonScript); //(30.0, 60.0, 90.0) 94 | 95 | return List.of(resultTuple.substring(1, resultTuple.length() - 1).split(", ")); 96 | } 97 | 98 | /** 99 | * Run a python script with {@code ProcessBuilder} and use {@code BufferedReader} to get the first line of the output. 100 | * 101 | * @param pythonInterpreter The path of the python interpreter. 102 | * @param pythonScript The python script to run. 103 | * @return The first line of the output. 104 | */ 105 | private static String runPythonScript(String pythonInterpreter, String pythonScript) throws IOException, InterruptedException { 106 | ProcessBuilder pb = new ProcessBuilder(pythonInterpreter, "-c", pythonScript); 107 | pb.redirectErrorStream(true); 108 | Process p; 109 | p = pb.start(); 110 | 111 | InputStream stdout = p.getInputStream(); 112 | BufferedReader reader = new BufferedReader(new InputStreamReader(stdout)); 113 | String line = reader.readLine(); 114 | p.waitFor(); 115 | return line; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/utils/OSDetector.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * This class is used to detect the operating system. 5 | */ 6 | public class OSDetector { 7 | /** 8 | * The operating system name. 9 | */ 10 | private static final String OS = System.getProperty("os.name").toLowerCase(); 11 | 12 | /** 13 | * Check if the operating system is Windows. 14 | * 15 | * @return {@code true} if the operating system is Windows, {@code false} otherwise. 16 | */ 17 | public static boolean isWindows() { 18 | return (OS.contains("win")); 19 | } 20 | 21 | /** 22 | * Check if the operating system is Mac. 23 | * 24 | * @return {@code true} if the operating system is Mac, {@code false} otherwise. 25 | */ 26 | public static boolean isMac() { 27 | return (OS.contains("mac")); 28 | } 29 | 30 | /** 31 | * Check if the operating system is Unix. 32 | * 33 | * @return {@code true} if the operating system is Unix, {@code false} otherwise. 34 | */ 35 | public static boolean isUnix() { 36 | return (OS.contains("nix") || OS.contains("nux") || OS.contains("aix")); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/utils/RelativePathGetter.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * This class is used to get the relative path of a file compared to the project path. 5 | */ 6 | public class RelativePathGetter { 7 | /** 8 | * Get the relative path of a file compared to the project path. If the project path is not a prefix of the file path, the absolute path of the file is returned. 9 | * 10 | * @param absolutePath The absolute path of the file. 11 | * @param projectPath The absolute path of the project. 12 | * @return The relative path of the file compared to the project path. 13 | */ 14 | public static String getRelativePath(String absolutePath, String projectPath) { 15 | if (absolutePath.length() > projectPath.length() && absolutePath.startsWith(projectPath)) { 16 | return absolutePath.substring(projectPath.length()); 17 | } 18 | return absolutePath; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/utils/XMLWriter.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import org.w3c.dom.Document; 4 | 5 | import javax.xml.transform.OutputKeys; 6 | import javax.xml.transform.Transformer; 7 | import javax.xml.transform.TransformerException; 8 | import javax.xml.transform.TransformerFactory; 9 | import javax.xml.transform.dom.DOMSource; 10 | import javax.xml.transform.stream.StreamResult; 11 | import java.io.File; 12 | 13 | /** 14 | * This class is used to write the XML document to the XML file. 15 | */ 16 | public class XMLWriter { 17 | /** 18 | * Write the formatted XML document to the XML file. 19 | * 20 | * @param document The XML document. 21 | * @param filePath The path of the XML file. 22 | */ 23 | public static void writeToXML(Document document, String filePath) throws TransformerException { 24 | TransformerFactory transformerFactory = TransformerFactory.newInstance(); 25 | Transformer transformer = transformerFactory.newTransformer(); 26 | transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 27 | DOMSource source = new DOMSource(document); 28 | StreamResult result = new StreamResult(new File(filePath)); 29 | transformer.transform(source, result); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.nd.codegrits 5 | 6 | 8 | CodeGRITS 9 | 10 | 11 | SaNDwich Lab 12 | 13 | 16 | Gaze Recording & IDE 18 | Tracking System, which is a plugin specifically designed for SE researchers. 19 | CodeGRITS is built on top of IntelliJ’s SDK, with wide compatibility with the entire family of JetBrains IDEs to 20 | track developers’ IDE interactions and eye gaze data. For more information, please visit our website 21 | CodeGRITS. 22 | ]]> 23 | 24 | 26 | com.intellij.modules.platform 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 52 | 53 | 54 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | --------------------------------------------------------------------------------