mirror of
https://github.com/arduino/Arduino.git
synced 2025-02-26 20:54:22 +01:00
Merge branch 'master' into serialPlotterRemoteCommands
This commit is contained in:
commit
819b87cb2d
13
.classpath
13
.classpath
@ -4,7 +4,6 @@
|
||||
<classpathentry kind="src" path="app/test"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="app/lib/apple.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/ecj.jar"/>
|
||||
<classpathentry kind="lib" path="app/test-lib/junit-4.11.jar"/>
|
||||
<classpathentry kind="lib" path="app/test-lib/fest-assert-1.2.jar"/>
|
||||
<classpathentry kind="lib" path="app/test-lib/fest-reflect-1.2.jar"/>
|
||||
@ -16,17 +15,17 @@
|
||||
<classpathentry kind="lib" path="app/lib/commons-httpclient-3.1.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/commons-logging-1.0.4.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/commons-net-3.3.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jmdns-3.5.1.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jmdns-3.5.3.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/slf4j-api-1.7.22.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jsch-0.1.50.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jssc-2.8.0.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jssc-2.8.0-arduino3.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/bcpg-jdk15on-152.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/bcprov-jdk15on-152.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jackson-core-2.6.3.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jackson-databind-2.6.3.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jackson-module-mrbean-2.6.3.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jackson-annotations-2.6.3.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jackson-core-2.9.5.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jackson-databind-2.9.5.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/jackson-annotations-2.9.5.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/commons-compress-1.8.jar"/>
|
||||
<classpathentry kind="lib" path="app/lib/commons-lang3-3.8.1.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/arduino-core"/>
|
||||
<classpathentry kind="output" path="app/bin"/>
|
||||
</classpath>
|
||||
|
29
.github/workflows/ant.yml
vendored
Normal file
29
.github/workflows/ant.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Java CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build with Ant
|
||||
working-directory: ./build
|
||||
run: |
|
||||
sed -i 's#<input .*/>##' build.xml
|
||||
ant clean dist
|
||||
- name: Install X virtual framebuffer
|
||||
run: sudo apt-get install -y xvfb
|
||||
- name: Run tests
|
||||
working-directory: ./app
|
||||
run: xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" ant test
|
||||
- name: Cleanup xvfb
|
||||
uses: bcomnes/cleanup-xvfb@v1
|
156
CONTRIBUTING.md
156
CONTRIBUTING.md
@ -1,102 +1,70 @@
|
||||
## Contributing guide
|
||||
This document serves as a checklist before contributing to this repository. It includes includes links to read up on if topics are unclear to you.
|
||||
# Contributing Rules
|
||||
Thanks for your interest in contributing to this free open source project! Arduino welcomes help from the community. There are several ways you can get involved:
|
||||
|
||||
This guide mainly focuses on the proper use of Git. It has some overlap with the more general information found in the [Development Policy File](https://github.com/arduino/Arduino/wiki/Development-Policy).
|
||||
| Type of contribution | Contribution method |
|
||||
|-|-|
|
||||
| - Support request<br/>- Question<br/>- Problem with your Arduino<br/>- Discussion | Post on the [Arduino Forum](http://forum.arduino.cc) |
|
||||
| - Bug report<br/>- [Arduino website](https://www.arduino.cc/) issue or improvement<br/>- Feature request | Issue report (read the [issue guidelines](#issues)) |
|
||||
| - Bug fix<br/>- Enhancement | Pull Request (read the [pull request guidelines](#pull-requests)) |
|
||||
| Translations for the Arduino IDE | [transifex](https://www.transifex.com/mbanzi/arduino-ide-15/) |
|
||||
| Translations for the [Language Reference](https://www.arduino.cc/reference) | [Reference repositories](https://github.com/arduino?q=reference-) |
|
||||
| Monetary | - [Donate](https://www.arduino.cc/en/Main/Contribute)<br/>- [Buy official products](https://store.arduino.cc) |
|
||||
|
||||
### 1. Before using the issue tracker
|
||||
To report a bug or a small enhancement please use the [issue tracker](http://github.com/arduino/Arduino/issues). But check the following boxes before posting an issue:
|
||||
|
||||
- [ ] `Your issue is NOT a question about an Arduino sketch.` Sketch questions are handled on the [Arduino Forum](http://forum.arduino.cc/).
|
||||
- [ ] `Check if your issue has already been resolved in the` [hourly build](http://www.arduino.cc/en/Main/Software#hourly)
|
||||
- [ ] `Your issue is not a duplicate.` So search for similar open and closed issues and pull-requests.
|
||||
- [ ] `Make sure you are working on the right repository. See the table below.`
|
||||
## Issues
|
||||
- Do you need help or have a question about using Arduino? Support requests should be made to the appropriate section of the [Arduino forum](http://forum.arduino.cc) rather than an issue report. **Issue reports are to be used to report bugs or make feature requests only.**
|
||||
- Check if your issue has already been resolved in the [hourly build](http://www.arduino.cc/en/Main/Software#hourly).
|
||||
- Submit issue reports to the correct repository:
|
||||
|
||||
| Repositories | Projects |
|
||||
|---|---|
|
||||
|[Arduino](https://github.com/arduino/Arduino) | Arduino IDE, arduino.cc (but not the Arduino Playground), Library Manager |
|
||||
|[Arduino Playground](http://forum.arduino.cc/index.php?board=24.0) | This is a publicly editable wiki. Please either make the edit yourself or create a post |
|
||||
|[Arduino Forum](https://github.com/arduino/forum-issues) | Issues about the Arduino Forum |
|
||||
| [Libraries for Arduino IDE](https://github.com/arduino-libraries) | Changing libraries for the IDE |
|
||||
| [Arduino-builder](https://github.com/arduino/arduino-builder)| |
|
||||
|[Arduino Web Editor](https://github.com/arduino/arduino-create-agent) | |
|
||||
|[Arduino SAMD Boards](https://github.com/arduino/ArduinoCore-samd)|Zero, MKR1000, MKRZero, etc. |
|
||||
|[Arduino SAM Boards](https://github.com/arduino/ArduinoCore-sam)| Due |
|
||||
|[Arduino AVR Boards toolchain (avr-gcc)](https://github.com/arduino/toolchain-avr)| |
|
||||
|[Arduino's build of AVRDUDE](https://github.com/arduino/avrdude-build-script)||
|
||||
|Third party repository | for third party libraries, hardware packages or sketches |
|
||||
| Issue topic | Report at |
|
||||
|-|-|
|
||||
| Arduino IDE, Arduino AVR Boards, arduino.cc (but not the Arduino Forum), Library Manager additions | [arduino/Arduino](https://github.com/arduino/Arduino/issues) |
|
||||
| [Language Reference](https://www.arduino.cc/reference) | [Reference repositories](https://github.com/arduino?q=reference-) |
|
||||
| Arduino Forum | [arduino/forum-issues](https://github.com/arduino/forum-issues/issues) |
|
||||
| Arduino libraries | [arduino-libraries](https://github.com/arduino-libraries) |
|
||||
| arduino-builder | [arduino/arduino-builder](https://github.com/arduino/arduino-builder/issues) |
|
||||
| [Arduino Web Editor](https://create.arduino.cc/editor) | [**Create > Editor** section of the Arduino Forum](http://forum.arduino.cc/index.php?board=101.0) |
|
||||
| Arduino AVR Boards (Uno, Mega, Leonardo, etc.) | [arduino/ArduinoCore-avr](https://github.com/arduino/ArduinoCore-avr/issues) |
|
||||
| Arduino SAMD Boards (Zero, MKR1000, MKRZero, etc.) | [arduino/ArduinoCore-samd](https://github.com/arduino/ArduinoCore-samd/issues) |
|
||||
| Arduino SAM Boards (Due) | [arduino/ArduinoCore-sam](https://github.com/arduino/ArduinoCore-sam/issues) |
|
||||
| AVR Toolchain for Arduino | [arduino/toolchain-avr](https://github.com/arduino/toolchain-avr/issues) |
|
||||
| Arduino's build of AVRDUDE | [arduino/avrdude-build-script](https://github.com/arduino/avrdude-build-script/issues) |
|
||||
| Security vulnerability | See: [Coordinated Vulnerability Disclosure Policy](https://github.com/arduino/arduino-cvd-policy) |
|
||||
| 3rd party libraries, hardware, or sketches | Report issues to the author of the software, *not* Arduino. |
|
||||
|
||||
### 2. Posting the issue
|
||||
When you have checked the previous boxes. Please consider the following points before posting the issue.
|
||||
When you're not sure where your issue belongs, report it at [arduino/Arduino](https://github.com/arduino/Arduino) and we'll move it to where it belongs (but remember: Only bug reports and feature requests, do not ask for help with your own code there).
|
||||
|
||||
- [ ] `Describe the issue based on the behaviour you were expecting`
|
||||
- [ ] `Post complete error messages using markdown code fencing:` [Markdown Code Fencing Example](https://guides.github.com/features/mastering-markdown/#examples)
|
||||
- [ ] `Provide a full set of steps necessary to reproduce the issue`
|
||||
- [ ] `Demonstration code should be complete, correct and the minimum amount necessary to reproduce the issue`
|
||||
- [ ] `Library Manager submissions: make sure your library meets all the requirements listed in the` [Library Manager FAQ](https://github.com/arduino/Arduino/wiki/Library-Manager-FAQ)
|
||||
- Search [existing pull requests and issues](https://github.com/arduino/Arduino/issues?q=) to be sure it hasn't already been reported. If you have additional information to provide about an existing issue then please comment on that issue. If you simply want to express your support then use the [Reactions feature](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments).
|
||||
- State the newest version of the Arduino IDE you have verified the issue with and which operating system you are using.
|
||||
- The issue title should be concise yet descriptive. Vague titles make it difficult to know the purpose of the issue when looking through the list of reports and may cause your issue to not be given proper attention.
|
||||
- Describe the issue and what behavior you were expecting. Post complete error messages using [Markdown code fencing](https://guides.github.com/features/mastering-markdown/#examples).
|
||||
- Provide a full set of steps necessary to reproduce the issue. Demonstration code should be complete, correct, and simplified to the minimum amount of code necessary to reproduce the issue. Please use [Markdown code fencing](https://guides.github.com/features/mastering-markdown/#examples) when posting code.
|
||||
- Be responsive. We may need you to provide more information, please respond as soon as possible.
|
||||
- If you find a solution to your problem update your issue report with an explanation of how you were able to fix it and close the issue.
|
||||
- Library Manager submissions: make sure your library meets all the requirements listed in the [Library Manager FAQ](https://github.com/arduino/Arduino/wiki/Library-Manager-FAQ).
|
||||
|
||||
### 3. Pull Requests
|
||||
Before starting to work on bigger topics like modifying the API or changes with backward compatibility trade-offs please discuss them in the [mailing list](https://groups.google.com/a/arduino.cc/forum/#!forum/developers) first.
|
||||
|
||||
### 4. Commit messages
|
||||
An easy to read pull request will speed up the merging process. Your commit messages need to be logically separate. And containing enough information on their own. When this is done consistently your pull request will have an easy to read log of changes.
|
||||
|
||||
Your commits need to be [atomic](https://www.freshconsulting.com/atomic-commits/) which allows the repository to remain flexible after merging.
|
||||
|
||||
If you did not read the following 7 points before or just want to fresh up. Please read up on them on this [website](https://chris.beams.io/posts/git-commit) by Chris Beams.
|
||||
|
||||
1. Separate subject from body with a blank line
|
||||
2. Limit the subject line (first line) to 50 characters
|
||||
3. Capitalize the subject line
|
||||
4. Do not end the subject line with a period `(.)`
|
||||
5. Use the imperative mood in the subject line.
|
||||
This should be in the written as giving an instruction for example "Fixed save-as bug" (it shows what the PR achieves when merging it)
|
||||
6. Wrap body at 72 characters
|
||||
7. Use the body to explain what, why and how
|
||||
|
||||
If your pull request fixes, closes or resolves an issue please reference it in the body with the following [syntax](https://help.github.com/articles/closing-issues-via-commit-messages/). Also see the last lines of the following example.
|
||||
|
||||
A general example with these 7 guidelines in mind is shown below (from the same website of [Chris Beams](https://chris.beams.io/posts/git-commit)):
|
||||
```
|
||||
Summarize changes in around 50 characters or less
|
||||
|
||||
More detailed explanatory text, if necessary. Wrap it to about 72
|
||||
characters or so. In some contexts, the first line is treated as the
|
||||
subject of the commit and the rest of the text as the body. The
|
||||
blank line separating the summary from the body is critical (unless
|
||||
you omit the body entirely); various tools like `log`, `shortlog`
|
||||
and `rebase` can get confused if you run the two together.
|
||||
|
||||
Explain the problem that this commit is solving. Focus on why you
|
||||
are making this change as opposed to how (the code explains that).
|
||||
Are there side effects or other unintuitive consequences of this
|
||||
change? Here's the place to explain them.
|
||||
|
||||
Further paragraphs come after blank lines.
|
||||
|
||||
- Bullet points are okay, too
|
||||
|
||||
- Typically a hyphen or asterisk is used for the bullet, preceded
|
||||
by a single space, with blank lines in between, but conventions
|
||||
vary here
|
||||
|
||||
If you use an issue tracker, put references to them at the bottom,
|
||||
like this:
|
||||
|
||||
Resolves: #123
|
||||
See also: #456, #789
|
||||
```
|
||||
|
||||
### 5. Rebasing pull requests
|
||||
When different people are working on the Arduino project simultaneously, pull requests can go stale quickly. A "stale" pull request is one that is no longer up to date with the latest merges in the project. It needs to be updated before it can be merged.
|
||||
|
||||
Most often pull requests become stale when merge conflicts occur. This happens when two pull requests both modify similar lines in the same file and one gets merged, the unmerged request will now have a merge conflict and needs updating.
|
||||
|
||||
When your pull request is stale, you will need to rebase your branch on the current master branch before you can merge it without conflicts.
|
||||
|
||||
More information about rebasing can be found at the repository of [edX](https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request).
|
||||
|
||||
### 6. Merged!
|
||||
When your pull request is merged please update the documentation if the changes require it:
|
||||
|
||||
- [ ] Edit appropiate [Wiki pages](https://github.com/arduino/Arduino/wiki/_pages)
|
||||
- [ ] Submit an [issue report](https://github.com/arduino/Arduino/issues/new) requesting changes to the [arduino.cc reference pages](https://www.arduino.cc/en/Reference/HomePage)
|
||||
## Pull Requests
|
||||
Pull requests are an easy and effective way to submit a proposal for a change to the content of one of Arduino's repositories. The Arduino team can merge your change with a single click! You can find more information about pull requests [here](https://help.github.com/articles/creating-a-pull-request/).
|
||||
- Big changes, changes to the API, or changes with backward compatibility trade-offs should be first discussed in the [Arduino Developers Mailing List](https://groups.google.com/a/arduino.cc/forum/#!forum/developers).
|
||||
- Search [existing pull requests](https://github.com/arduino/Arduino/pulls?q=) to see if one has already been submitted for this change. Search the [issues](https://github.com/arduino/Arduino/issues?q=is%3Aissue) to see if there has been a discussion on this topic and whether your pull request can close any issues.
|
||||
- Code formatting should be consistent with the style used in the existing code.
|
||||
- Don't leave commented out code. A record of this code is already preserved in the commit history.
|
||||
- Note that the Arduino core libraries support many boards and processors. When fixing or adding functionality for one of them, it's easy to break something on the others. Please test your changes on as many processors as possible. Even if you don't have a particular board, try compiling your patch for it to make sure that you haven't introduced any errors.
|
||||
- All commits must be atomic. This means that the commit completely accomplishes a single task. Each commit should result in fully functional code. Multiple tasks should not be combined in a single commit, but a single task should not be split over multiple commits (e.g. one commit per file modified is not a good practice). For more information see http://www.freshconsulting.com/atomic-commits.
|
||||
- Each pull request should address a single bug fix or enhancement. This may consist of multiple commits. If you have multiple, unrelated fixes or enhancements to contribute, submit them as separate pull requests.
|
||||
- Commit messages:
|
||||
- Use the [imperative mood](http://chris.beams.io/posts/git-commit/#imperative) in the title. For example: "Apply editor.indent preference"
|
||||
- Capitalize the title.
|
||||
- Do not end the title with a period.
|
||||
- Separate title from the body with a blank line. If you're committing via GitHub or GitHub Desktop this will be done automatically.
|
||||
- Wrap body at 72 characters.
|
||||
- Completely explain the purpose of the commit. Include a rationale for the change, any caveats, side-effects, etc.
|
||||
- If your pull request fixes an issue in the issue tracker, use the [closes/fixes/resolves syntax](https://help.github.com/articles/closing-issues-via-commit-messages) in the body to indicate this.
|
||||
- See http://chris.beams.io/posts/git-commit for more tips on writing good commit messages.
|
||||
- Pull request title and description should follow the same guidelines as commit messages.
|
||||
- Rebasing pull requests is OK and encouraged. After submitting your pull request some changes may be requested. Rather than adding unnecessary extra commits to the pull request, you can squash these changes into the existing commit and then do a force push to your fork. When you do a force push to your fork, the PR will be updated with your new changes, so there is no need to open a new PR to make changes. Leave a comment on the pull request thread to explain that the history has been changed. This will help to keep the commit history of the repository clean.
|
||||
- After your pull request is merged please update the documentation if the changes require it:
|
||||
- Edit appropriate [Wiki pages](https://github.com/arduino/Arduino/wiki/_pages).
|
||||
- Submit an [issue report](https://github.com/arduino/Arduino/issues/new) requesting changes to the [arduino.cc reference pages](https://www.arduino.cc/en/Reference/HomePage).
|
||||
- For more contributing guidelines, see the [Arduino Development Policy](https://github.com/arduino/Arduino/wiki/Development-Policy).
|
||||
|
1
ISSUE_TEMPLATE.md
Normal file
1
ISSUE_TEMPLATE.md
Normal file
@ -0,0 +1 @@
|
||||
Please read the Issues section of the Contributing Rules at the "Contributing" link to the right before submitting an issue report.
|
17
PULL_REQUEST_TEMPLATE.md
Normal file
17
PULL_REQUEST_TEMPLATE.md
Normal file
@ -0,0 +1,17 @@
|
||||
### All Submissions:
|
||||
|
||||
* [ ] Have you followed the guidelines in our Contributing document?
|
||||
* [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/arduino/Arduino/pulls?q=) for the same update/change?
|
||||
|
||||
<!-- You can erase any parts of this template not applicable to your Pull Request. -->
|
||||
|
||||
### New Feature Submissions:
|
||||
|
||||
1. [ ] Does your submission pass tests?
|
||||
2. [ ] Have you lint your code locally prior to submission?
|
||||
|
||||
### Changes to Core Features:
|
||||
|
||||
* [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
|
||||
* [ ] Have you written new tests for your core changes, as applicable?
|
||||
* [ ] Have you successfully ran tests with your changes
|
61
README.md
61
README.md
@ -1,40 +1,47 @@
|
||||
Arduino
|
||||
========
|
||||
<p align="center">
|
||||
<img src="http://content.arduino.cc/brand/arduino-color.svg" width="50%" />
|
||||
</p>
|
||||
|
||||
* Arduino is an open-source physical computing platform based on a simple I/O
|
||||
Arduino is an open-source physical computing platform based on a simple I/O
|
||||
board and a development environment that implements the Processing/Wiring
|
||||
language. Arduino can be used to develop stand-alone interactive objects or
|
||||
can be connected to software on your computer (e.g. Flash, Processing and MaxMSP).
|
||||
The boards can be assembled by hand or purchased preassembled; the open-source
|
||||
IDE can be downloaded for free at https://www.arduino.cc/en/Main/Software
|
||||
IDE can be downloaded for free at [https://arduino.cc](https://www.arduino.cc/en/Main/Software)
|
||||
|
||||
* For more information, see the website at: https://www.arduino.cc/
|
||||
or the forums at: https://forum.arduino.cc/
|
||||
You can also follow Arduino on Twitter at: https://twitter.com/arduino or
|
||||
like Arduino on Facebook at: https://www.facebook.com/official.arduino
|
||||
## More info at
|
||||
|
||||
* To report a *bug* in the software or to request *a simple enhancement* go to:
|
||||
https://github.com/arduino/Arduino/issues
|
||||
- [Our website](https://www.arduino.cc/)
|
||||
|
||||
* More complex requests and technical discussion should go on the Arduino Developers
|
||||
mailing list:
|
||||
https://groups.google.com/a/arduino.cc/forum/#!forum/developers
|
||||
- [The forums](https://forum.arduino.cc/)
|
||||
|
||||
* If you're interested in modifying or extending the Arduino software, we strongly
|
||||
suggest discussing your ideas on the Developers mailing list *before* starting
|
||||
to work on them. That way you can coordinate with the Arduino Team and others,
|
||||
- Follow us on [Twitter](https://twitter.com/arduino)
|
||||
- And like us at [Facebook](https://www.facebook.com/official.arduino)
|
||||
|
||||
## Bug reports and technical discussions
|
||||
|
||||
- To report a *bug* in the software or to request *a simple enhancement* go to [Github Issues](https://github.com/arduino/Arduino/issues)
|
||||
|
||||
- More complex requests and technical discussion should go on the [Arduino Developers
|
||||
mailing list](https://groups.google.com/a/arduino.cc/forum/#!forum/developers)
|
||||
|
||||
- If you're interested in modifying or extending the Arduino software, we strongly
|
||||
suggest discussing your ideas on the
|
||||
[Developers mailing list](https://groups.google.com/a/arduino.cc/forum/#!forum/developers)
|
||||
*before* starting to work on them.
|
||||
That way you can coordinate with the Arduino Team and others,
|
||||
giving your work a higher chance of being integrated into the official release
|
||||
https://groups.google.com/a/arduino.cc/forum/#!forum/developers
|
||||
|
||||
Installation
|
||||
------------
|
||||
Detailed instructions for installation in popular operating systems.
|
||||
For Linux: https://www.arduino.cc/en/Guide/Linux (see also the Arduino playground page https://playground.arduino.cc/Learning/Linux)
|
||||
For macOS X: https://www.arduino.cc/en/Guide/MacOSX
|
||||
For Windows: https://www.arduino.cc/en/Guide/Windows
|
||||
## Installation
|
||||
|
||||
Detailed instructions for installation in popular operating systems can be found at:
|
||||
|
||||
- [Linux](https://www.arduino.cc/en/Guide/Linux) (see also the [Arduino playground](https://playground.arduino.cc/Learning/Linux))
|
||||
- [macOS](https://www.arduino.cc/en/Guide/MacOSX)
|
||||
- [Windows](https://www.arduino.cc/en/Guide/Windows)
|
||||
|
||||
## Credits
|
||||
|
||||
Credits
|
||||
--------
|
||||
Arduino is an open source project, supported by many.
|
||||
|
||||
The Arduino team is composed of Massimo Banzi, David Cuartielles, Tom Igoe
|
||||
@ -43,8 +50,8 @@ and David A. Mellis.
|
||||
Arduino uses
|
||||
[GNU avr-gcc toolchain](https://gcc.gnu.org/wiki/avr-gcc),
|
||||
[GCC ARM Embedded toolchain](https://launchpad.net/gcc-arm-embedded),
|
||||
[avr-libc](http://www.nongnu.org/avr-libc/),
|
||||
[avrdude](http://www.nongnu.org/avrdude/),
|
||||
[avr-libc](https://www.nongnu.org/avr-libc/),
|
||||
[avrdude](https://www.nongnu.org/avrdude/),
|
||||
[bossac](http://www.shumatech.com/web/products/bossa),
|
||||
[openOCD](http://openocd.org/)
|
||||
and code from [Processing](https://www.processing.org)
|
||||
|
@ -4,7 +4,6 @@
|
||||
<classpathentry kind="src" path="test"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/arduino-core"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
<classpathentry kind="lib" path="lib/apple.jar"/>
|
||||
<classpathentry kind="lib" path="lib/batik-1.8.jar"/>
|
||||
<classpathentry kind="lib" path="lib/batik-anim-1.8.jar"/>
|
||||
@ -30,20 +29,19 @@
|
||||
<classpathentry kind="lib" path="lib/commons-compress-1.8.jar"/>
|
||||
<classpathentry kind="lib" path="lib/commons-exec-1.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/commons-httpclient-3.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/commons-lang3-3.3.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/commons-logging-1.0.4.jar"/>
|
||||
<classpathentry kind="lib" path="lib/commons-net-3.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jackson-annotations-2.6.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jackson-core-2.6.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jackson-databind-2.6.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jackson-module-mrbean-2.6.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jackson-annotations-2.9.5.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jackson-core-2.9.5.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jackson-databind-2.9.5.jar"/>
|
||||
<classpathentry kind="lib" path="lib/java-semver-0.8.0.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jmdns-3.5.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jmdns-3.5.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/slf4j-api-1.7.22.jar"/>
|
||||
<classpathentry kind="lib" path="lib/slf4j-simple-1.7.22.jar"/>
|
||||
<classpathentry kind="lib" path="lib/log4j-api-2.12.0.jar"/>
|
||||
<classpathentry kind="lib" path="lib/log4j-core-2.12.0.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jsch-0.1.50.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jssc-2.8.0-arduino1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/rsyntaxtextarea-2.6.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/rsyntaxtextarea-3.0.3-SNAPSHOT.jar"/>
|
||||
<classpathentry kind="lib" path="lib/xml-apis-1.3.04.jar"/>
|
||||
<classpathentry kind="lib" path="lib/xml-apis-ext-1.3.04.jar"/>
|
||||
<classpathentry kind="lib" path="lib/xmlgraphics-commons-2.0.jar"/>
|
||||
@ -53,4 +51,8 @@
|
||||
<classpathentry kind="lib" path="test-lib/fest-swing-1.2.jar"/>
|
||||
<classpathentry kind="lib" path="test-lib/fest-util-1.1.2.jar"/>
|
||||
<classpathentry kind="lib" path="test-lib/jcip-annotations-1.0.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jtouchbar-1.0.0.jar"/>
|
||||
<classpathentry kind="lib" path="lib/commons-lang3-3.8.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jssc-2.8.0-arduino4.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
@ -80,6 +80,10 @@
|
||||
includeAntRuntime="false"
|
||||
debug="true"
|
||||
classpathref="class.path" />
|
||||
<!-- If you want to add files in the jars -->
|
||||
<copy todir="bin" overwrite="true" verbose="true">
|
||||
<fileset dir="src" includes="log4j2.xml" />
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="test" depends="compile" description="Runs the test">
|
||||
@ -105,7 +109,7 @@
|
||||
<fileset dir="test" includes="**/*.pac" />
|
||||
</copy>
|
||||
|
||||
<junit printsummary="yes" dir="${work.dir}" fork="true">
|
||||
<junit printsummary="yes" dir="${work.dir}" fork="true" showoutput="yes" failureproperty="test.failed">
|
||||
<jvmarg value="-Djava.library.path=${java.additional.library.path}"/>
|
||||
<jvmarg value="-DWORK_DIR=."/>
|
||||
<jvmarg value="-ea"/>
|
||||
@ -127,6 +131,7 @@
|
||||
</batchtest>
|
||||
</junit>
|
||||
|
||||
<fail if="test.failed"/>
|
||||
</target>
|
||||
|
||||
<target name="build" depends="compile" description="Build PDE">
|
||||
|
BIN
app/lib/commons-io-2.6.jar
Normal file
BIN
app/lib/commons-io-2.6.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/lib/commons-lang3-3.8.1.jar
Normal file
BIN
app/lib/commons-lang3-3.8.1.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/lib/jackson-annotations-2.9.5.jar
Normal file
BIN
app/lib/jackson-annotations-2.9.5.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/lib/jackson-core-2.9.5.jar
Normal file
BIN
app/lib/jackson-core-2.9.5.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/lib/jackson-databind-2.9.5.jar
Normal file
BIN
app/lib/jackson-databind-2.9.5.jar
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app/lib/jmdns-3.5.3.jar
Normal file
BIN
app/lib/jmdns-3.5.3.jar
Normal file
Binary file not shown.
BIN
app/lib/jssc-2.8.0-arduino1.jar → app/lib/jssc-2.8.0-arduino4.jar
Executable file → Normal file
BIN
app/lib/jssc-2.8.0-arduino1.jar → app/lib/jssc-2.8.0-arduino4.jar
Executable file → Normal file
Binary file not shown.
BIN
app/lib/jtouchbar-1.0.0.jar
Normal file
BIN
app/lib/jtouchbar-1.0.0.jar
Normal file
Binary file not shown.
21
app/lib/jtouchbar.LICENSE.MIT.txt
Normal file
21
app/lib/jtouchbar.LICENSE.MIT.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 thizzer.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
BIN
app/lib/log4j-api-2.12.0.jar
Normal file
BIN
app/lib/log4j-api-2.12.0.jar
Normal file
Binary file not shown.
BIN
app/lib/log4j-core-2.12.0.jar
Normal file
BIN
app/lib/log4j-core-2.12.0.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/lib/rsyntaxtextarea-3.0.3-SNAPSHOT.jar
Normal file
BIN
app/lib/rsyntaxtextarea-3.0.3-SNAPSHOT.jar
Normal file
Binary file not shown.
@ -52,7 +52,7 @@ import java.io.PrintStream;
|
||||
*/
|
||||
public class ConsoleOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
private final SimpleAttributeSet attributes;
|
||||
private SimpleAttributeSet attributes;
|
||||
private final PrintStream printStream;
|
||||
private final Timer timer;
|
||||
|
||||
@ -73,6 +73,10 @@ public class ConsoleOutputStream extends ByteArrayOutputStream {
|
||||
timer.setRepeats(false);
|
||||
}
|
||||
|
||||
public void setAttibutes(SimpleAttributeSet attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void setCurrentEditorConsole(EditorConsole console) {
|
||||
this.editorConsole = console;
|
||||
}
|
||||
|
@ -29,9 +29,6 @@
|
||||
|
||||
package cc.arduino.contributions;
|
||||
|
||||
import cc.arduino.contributions.filters.BuiltInPredicate;
|
||||
import cc.arduino.contributions.filters.InstalledPredicate;
|
||||
import cc.arduino.contributions.packages.ContributedPackage;
|
||||
import cc.arduino.contributions.packages.ContributedPlatform;
|
||||
import processing.app.Base;
|
||||
import processing.app.BaseNoGui;
|
||||
@ -39,8 +36,8 @@ import processing.app.I18n;
|
||||
import processing.app.PreferencesData;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
@ -67,13 +64,21 @@ public class BuiltInCoreIsNewerCheck implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ContributedPlatform> contributedPlatforms = BaseNoGui.indexer.getPackages().stream().map(ContributedPackage::getPlatforms).flatMap(Collection::stream).collect(Collectors.toList());
|
||||
List<ContributedPlatform> contributedPlatforms = BaseNoGui.indexer
|
||||
.getPackages().stream() //
|
||||
.map(pack -> pack.getPlatforms()) //
|
||||
.flatMap(platfs -> platfs.stream()) //
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<ContributedPlatform> installedBuiltInPlatforms = contributedPlatforms.stream().filter(new InstalledPredicate()).filter(new BuiltInPredicate()).collect(Collectors.toList());
|
||||
if (installedBuiltInPlatforms.size() != 1) {
|
||||
Optional<ContributedPlatform> mayInstalledBuiltIn = contributedPlatforms
|
||||
.stream() //
|
||||
.filter(p -> p.isInstalled()) //
|
||||
.filter(p -> p.isBuiltIn()) //
|
||||
.findFirst();
|
||||
if (!mayInstalledBuiltIn.isPresent()) {
|
||||
return;
|
||||
}
|
||||
final ContributedPlatform installedBuiltIn = installedBuiltInPlatforms.get(0);
|
||||
final ContributedPlatform installedBuiltIn = mayInstalledBuiltIn.get();
|
||||
|
||||
ContributedPlatform installedNotBuiltIn = BaseNoGui.indexer.getInstalled(installedBuiltIn.getParentPackage().getName(), installedBuiltIn.getArchitecture());
|
||||
if (installedNotBuiltIn == null) {
|
||||
@ -84,7 +89,7 @@ public class BuiltInCoreIsNewerCheck implements Runnable {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
if (VersionHelper.valueOf(installedBuiltIn.getParsedVersion()).greaterThan(VersionHelper.valueOf(installedNotBuiltIn.getParsedVersion()))) {
|
||||
if (VersionComparator.greaterThan(installedBuiltIn.getParsedVersion(), installedNotBuiltIn.getParsedVersion())) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
PreferencesData.setInteger("builtin_platform_is_newer", BaseNoGui.REVISION);
|
||||
assert base.hasActiveEditor();
|
||||
|
@ -29,32 +29,34 @@
|
||||
|
||||
package cc.arduino.contributions;
|
||||
|
||||
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
|
||||
import cc.arduino.contributions.libraries.LibraryInstaller;
|
||||
import cc.arduino.contributions.libraries.filters.UpdatableLibraryPredicate;
|
||||
import cc.arduino.contributions.packages.ContributionInstaller;
|
||||
import cc.arduino.contributions.packages.filters.UpdatablePlatformPredicate;
|
||||
import cc.arduino.view.NotificationPopup;
|
||||
import processing.app.Base;
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.Editor;
|
||||
import processing.app.I18n;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import processing.app.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowFocusListener;
|
||||
import java.net.URL;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
public class ContributionsSelfCheck extends TimerTask {
|
||||
public class ContributionsSelfCheck extends TimerTask implements NotificationPopup.OptionalButtonCallbacks {
|
||||
|
||||
private final Base base;
|
||||
private final HyperlinkListener hyperlinkListener;
|
||||
private final ContributionInstaller contributionInstaller;
|
||||
private final LibraryInstaller libraryInstaller;
|
||||
private final ProgressListener progressListener;
|
||||
private final String boardsManagerURL = "http://boardsmanager/DropdownUpdatableCoresItem";
|
||||
private final String libraryManagerURL = "http://librarymanager/DropdownUpdatableLibrariesItem";
|
||||
|
||||
private volatile boolean cancelled;
|
||||
private volatile NotificationPopup notificationPopup;
|
||||
@ -73,25 +75,49 @@ public class ContributionsSelfCheck extends TimerTask {
|
||||
updateContributionIndex();
|
||||
updateLibrariesIndex();
|
||||
|
||||
long updatablePlatforms = BaseNoGui.indexer.getPackages().stream()
|
||||
.flatMap(pack -> pack.getPlatforms().stream())
|
||||
.filter(new UpdatablePlatformPredicate()).count();
|
||||
boolean updatablePlatforms = checkForUpdatablePlatforms();
|
||||
|
||||
long updatableLibraries = BaseNoGui.librariesIndexer.getInstalledLibraries().stream()
|
||||
.filter(new UpdatableLibraryPredicate())
|
||||
.count();
|
||||
boolean updatableLibraries = checkForUpdatableLibraries();
|
||||
|
||||
if (updatableLibraries <= 0 && updatablePlatforms <= 0) {
|
||||
if (!updatableLibraries && !updatablePlatforms) {
|
||||
return;
|
||||
}
|
||||
|
||||
String text;
|
||||
if (updatableLibraries > 0 && updatablePlatforms <= 0) {
|
||||
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), "<a href=\"http://librarymanager/DropdownUpdatableLibrariesItem\">", "</a>");
|
||||
} else if (updatableLibraries <= 0 && updatablePlatforms > 0) {
|
||||
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), "<a href=\"http://boardsmanager/DropdownUpdatableCoresItem\">", "</a>");
|
||||
boolean setAccessible = PreferencesData.getBoolean("ide.accessible");
|
||||
final String text;
|
||||
final String button1Name;
|
||||
final String button2Name;
|
||||
String openAnchorBoards = "<a href=\"" + boardsManagerURL + "\">";
|
||||
String closeAnchorBoards = "</a>";
|
||||
String openAnchorLibraries = "<a href=\"" + libraryManagerURL + "\">";
|
||||
String closeAnchorLibraries = "</a>";
|
||||
|
||||
// if accessibility mode and board updates are available set the button name and clear the anchors
|
||||
if(setAccessible && updatablePlatforms) {
|
||||
button1Name = tr("Boards");
|
||||
openAnchorBoards = "";
|
||||
closeAnchorBoards = "";
|
||||
}
|
||||
else { // when not accessibility mode or no boards to update no button is needed
|
||||
button1Name = null;
|
||||
}
|
||||
|
||||
// if accessibility mode and libraries updates are available set the button name and clear the anchors
|
||||
if (setAccessible && updatableLibraries) {
|
||||
button2Name = tr("Libraries");
|
||||
openAnchorLibraries = "";
|
||||
closeAnchorLibraries = "";
|
||||
}
|
||||
else { // when not accessibility mode or no libraries to update no button is needed
|
||||
button2Name = null;
|
||||
}
|
||||
|
||||
if (updatableLibraries && !updatablePlatforms) {
|
||||
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), openAnchorLibraries, closeAnchorLibraries);
|
||||
} else if (!updatableLibraries && updatablePlatforms) {
|
||||
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), openAnchorBoards, closeAnchorBoards);
|
||||
} else {
|
||||
text = I18n.format(tr("Updates available for some of your {0}boards{1} and {2}libraries{3}"), "<a href=\"http://boardsmanager/DropdownUpdatableCoresItem\">", "</a>", "<a href=\"http://librarymanager/DropdownUpdatableLibrariesItem\">", "</a>");
|
||||
text = I18n.format(tr("Updates available for some of your {0}boards{1} and {2}libraries{3}"), openAnchorBoards, closeAnchorBoards, openAnchorLibraries, closeAnchorLibraries);
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
@ -100,7 +126,13 @@ public class ContributionsSelfCheck extends TimerTask {
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
Editor ed = base.getActiveEditor();
|
||||
boolean accessibleIde = PreferencesData.getBoolean("ide.accessible");
|
||||
if (accessibleIde) {
|
||||
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text, false, this, button1Name, button2Name);
|
||||
}
|
||||
else { // if not accessible view leave it the same
|
||||
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
|
||||
}
|
||||
if (ed.isFocused()) {
|
||||
notificationPopup.begin();
|
||||
return;
|
||||
@ -126,6 +158,35 @@ public class ContributionsSelfCheck extends TimerTask {
|
||||
});
|
||||
}
|
||||
|
||||
private void goToManager(String link) {
|
||||
try {
|
||||
((UpdatableBoardsLibsFakeURLsHandler) hyperlinkListener).openBoardLibManager(new URL(link));
|
||||
}
|
||||
catch (Exception e){
|
||||
LogManager.getLogger(ContributionsSelfCheck.class).warn("Exception while attempting to go to board manager", e);
|
||||
}
|
||||
}
|
||||
// callback for boards button
|
||||
public void onOptionalButton1Callback() {
|
||||
goToManager(boardsManagerURL);
|
||||
}
|
||||
|
||||
// callback for libraries button
|
||||
public void onOptionalButton2Callback() {
|
||||
goToManager(libraryManagerURL);
|
||||
}
|
||||
|
||||
static boolean checkForUpdatablePlatforms() {
|
||||
return BaseNoGui.indexer.getPackages().stream()
|
||||
.flatMap(pack -> pack.getPlatforms().stream())
|
||||
.anyMatch(new UpdatablePlatformPredicate());
|
||||
}
|
||||
|
||||
static boolean checkForUpdatableLibraries() {
|
||||
return BaseNoGui.librariesIndexer.getIndex().getLibraries().stream()
|
||||
.anyMatch(new UpdatableLibraryPredicate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
cancelled = true;
|
||||
|
@ -31,18 +31,23 @@ package cc.arduino.contributions.libraries;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class LibraryOfSameTypeComparator implements Comparator<ContributedLibrary> {
|
||||
import processing.app.packages.UserLibrary;
|
||||
|
||||
public class LibraryOfSameTypeComparator implements Comparator<UserLibrary> {
|
||||
|
||||
@Override
|
||||
public int compare(ContributedLibrary o1, ContributedLibrary o2) {
|
||||
if (o1.getTypes() == null) {
|
||||
public int compare(UserLibrary o1, UserLibrary o2) {
|
||||
if (o1.getTypes().isEmpty() && o2.getTypes().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
if (o1.getTypes().isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
if (o2.getTypes() == null) {
|
||||
if (o2.getTypes().isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
if (!o1.getTypes().get(0).equals(o2.getTypes().get(0))) {
|
||||
return 0;
|
||||
return o1.getTypes().get(0).compareTo(o2.getTypes().get(0));
|
||||
}
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
|
||||
package cc.arduino.contributions.libraries.filters;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class CategoryPredicate implements Predicate<ContributedLibrary> {
|
||||
|
||||
private final String category;
|
||||
|
||||
public CategoryPredicate(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(ContributedLibrary input) {
|
||||
return input.getCategory() != null && category.equals(input.getCategory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof CategoryPredicate && ((CategoryPredicate) obj).category.equals(category);
|
||||
}
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
|
||||
package cc.arduino.contributions.libraries.filters;
|
||||
|
||||
import cc.arduino.contributions.filters.InstalledPredicate;
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import processing.app.BaseNoGui;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class InstalledLibraryPredicate implements Predicate<ContributedLibrary> {
|
||||
|
||||
@Override
|
||||
public boolean test(ContributedLibrary input) {
|
||||
if (input.isInstalled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<ContributedLibrary> libraries = BaseNoGui.librariesIndexer.getIndex().find(input.getName());
|
||||
|
||||
return libraries.stream()
|
||||
.filter(new InstalledPredicate())
|
||||
.count() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof InstalledLibraryPredicate;
|
||||
}
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
|
||||
package cc.arduino.contributions.libraries.filters;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import processing.app.packages.UserLibrary;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class OnlyUpstreamReleasePredicate implements Predicate<ContributedLibrary> {
|
||||
|
||||
@Override
|
||||
public boolean test(ContributedLibrary input) {
|
||||
return !(input instanceof UserLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof OnlyUpstreamReleasePredicate;
|
||||
}
|
||||
}
|
@ -29,32 +29,34 @@
|
||||
|
||||
package cc.arduino.contributions.libraries.filters;
|
||||
|
||||
import cc.arduino.contributions.VersionComparator;
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.packages.UserLibrary;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import cc.arduino.contributions.VersionComparator;
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.LibrariesIndexer;
|
||||
import processing.app.BaseNoGui;
|
||||
|
||||
public class UpdatableLibraryPredicate implements Predicate<ContributedLibrary> {
|
||||
|
||||
private final VersionComparator versionComparator;
|
||||
LibrariesIndexer librariesIndexer;
|
||||
|
||||
public UpdatableLibraryPredicate() {
|
||||
this.versionComparator = new VersionComparator();
|
||||
librariesIndexer = BaseNoGui.librariesIndexer;
|
||||
}
|
||||
|
||||
public UpdatableLibraryPredicate(LibrariesIndexer indexer) {
|
||||
librariesIndexer = indexer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(ContributedLibrary contributedLibrary) {
|
||||
String libraryName = contributedLibrary.getName();
|
||||
UserLibrary installed = BaseNoGui.librariesIndexer.getInstalledLibraries().getByName(libraryName);
|
||||
if (installed == null) {
|
||||
public boolean test(ContributedLibrary lib) {
|
||||
if (!lib.isLibraryInstalled()) {
|
||||
return false;
|
||||
}
|
||||
List<ContributedLibrary> libraries = BaseNoGui.librariesIndexer.getIndex().find(libraryName);
|
||||
return libraries.stream()
|
||||
.filter(library -> versionComparator.greaterThan(library.getParsedVersion(), installed.getParsedVersion()))
|
||||
.count() > 0;
|
||||
String libraryName = lib.getName();
|
||||
List<ContributedLibrary> libraries = librariesIndexer.getIndex().find(libraryName);
|
||||
ContributedLibrary latest = libraries.stream().reduce(VersionComparator::max).get();
|
||||
return !latest.isLibraryInstalled();
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,11 @@
|
||||
package cc.arduino.contributions.libraries.ui;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class ContributedLibraryReleasesComparator implements Comparator<ContributedLibraryReleases> {
|
||||
|
||||
@ -43,12 +46,14 @@ public class ContributedLibraryReleasesComparator implements Comparator<Contribu
|
||||
|
||||
@Override
|
||||
public int compare(ContributedLibraryReleases o1, ContributedLibraryReleases o2) {
|
||||
ContributedLibrary lib1 = o1.getLibrary();
|
||||
ContributedLibrary lib2 = o2.getLibrary();
|
||||
ContributedLibrary lib1 = o1.getLatest();
|
||||
ContributedLibrary lib2 = o2.getLatest();
|
||||
|
||||
List<String> types1 = lib1.getTypes();
|
||||
List<String> types2 = lib2.getTypes();
|
||||
if (types1 == null) types1 = Arrays.asList();
|
||||
if (types2 == null) types2 = Arrays.asList();
|
||||
|
||||
if (lib1.getTypes() == null || lib2.getTypes() == null) {
|
||||
return compareName(lib1, lib2);
|
||||
}
|
||||
if (lib1.getTypes().contains(firstType) && lib2.getTypes().contains(firstType)) {
|
||||
return compareName(lib1, lib2);
|
||||
}
|
||||
|
@ -36,17 +36,15 @@ import java.awt.Component;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JTable;
|
||||
|
||||
import cc.arduino.contributions.DownloadableContributionVersionComparator;
|
||||
import cc.arduino.contributions.VersionComparator;
|
||||
import cc.arduino.contributions.filters.BuiltInPredicate;
|
||||
import cc.arduino.contributions.filters.InstalledPredicate;
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.filters.OnlyUpstreamReleasePredicate;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.ui.InstallerTableCell;
|
||||
import cc.arduino.utils.ReverseComparator;
|
||||
|
||||
@ -76,64 +74,58 @@ public class ContributedLibraryTableCellEditor extends InstallerTableCell {
|
||||
ContributedLibrary lib = (ContributedLibrary) chooser.getSelectedItem();
|
||||
onInstall(lib, editorValue.getInstalled());
|
||||
});
|
||||
editorCell.versionToInstallChooser.addItemListener(e -> editorValue
|
||||
.select((ContributedLibrary) editorCell.versionToInstallChooser
|
||||
.getSelectedItem()));
|
||||
editorCell.versionToInstallChooser.addActionListener(e -> {
|
||||
editorValue.select((ContributedLibrary) editorCell.versionToInstallChooser.getSelectedItem());
|
||||
if (editorCell.versionToInstallChooser.getSelectedIndex() != 0) {
|
||||
InstallerTableCell.dropdownSelected(true);
|
||||
}
|
||||
});
|
||||
|
||||
setEnabled(true);
|
||||
|
||||
final ContributedLibrary installed = editorValue.getInstalled();
|
||||
final Optional<ContributedLibrary> mayInstalled = editorValue.getInstalled();
|
||||
|
||||
List<ContributedLibrary> releases = editorValue.getReleases().stream()
|
||||
.filter(new OnlyUpstreamReleasePredicate())
|
||||
.collect(Collectors.toList());
|
||||
List<ContributedLibrary> uninstalledReleases = releases.stream()
|
||||
.filter(new InstalledPredicate().negate()).collect(Collectors.toList());
|
||||
|
||||
List<ContributedLibrary> installedBuiltIn = releases.stream()
|
||||
.filter(new InstalledPredicate()).filter(new BuiltInPredicate())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (installed != null && !installedBuiltIn.contains(installed)) {
|
||||
uninstalledReleases.addAll(installedBuiltIn);
|
||||
List<ContributedLibrary> releases = editorValue.getReleases();
|
||||
List<ContributedLibrary> notInstalled = new LinkedList<>(releases);
|
||||
if (mayInstalled.isPresent()) {
|
||||
notInstalled.remove(editorValue.getInstalled().get());
|
||||
}
|
||||
|
||||
Collections.sort(uninstalledReleases, new ReverseComparator<>(
|
||||
Collections.sort(notInstalled, new ReverseComparator<>(
|
||||
new DownloadableContributionVersionComparator()));
|
||||
|
||||
editorCell.downgradeChooser.removeAllItems();
|
||||
editorCell.downgradeChooser.addItem(tr("Select version"));
|
||||
|
||||
final List<ContributedLibrary> uninstalledPreviousReleases = new LinkedList<>();
|
||||
final List<ContributedLibrary> uninstalledNewerReleases = new LinkedList<>();
|
||||
final List<ContributedLibrary> notInstalledPrevious = new LinkedList<>();
|
||||
final List<ContributedLibrary> notInstalledNewer = new LinkedList<>();
|
||||
|
||||
final VersionComparator versionComparator = new VersionComparator();
|
||||
uninstalledReleases.stream().forEach(input -> {
|
||||
if (installed == null
|
||||
|| versionComparator.greaterThan(installed.getParsedVersion(),
|
||||
input.getParsedVersion())) {
|
||||
uninstalledPreviousReleases.add(input);
|
||||
notInstalled.stream().forEach(input -> {
|
||||
if (!mayInstalled.isPresent()
|
||||
|| VersionComparator.greaterThan(mayInstalled.get(), input)) {
|
||||
notInstalledPrevious.add(input);
|
||||
} else {
|
||||
uninstalledNewerReleases.add(input);
|
||||
notInstalledNewer.add(input);
|
||||
}
|
||||
});
|
||||
uninstalledNewerReleases.forEach(editorCell.downgradeChooser::addItem);
|
||||
uninstalledPreviousReleases.forEach(editorCell.downgradeChooser::addItem);
|
||||
notInstalledNewer.forEach(editorCell.downgradeChooser::addItem);
|
||||
notInstalledPrevious.forEach(editorCell.downgradeChooser::addItem);
|
||||
|
||||
editorCell.downgradeChooser
|
||||
.setVisible(installed != null
|
||||
&& (!uninstalledPreviousReleases.isEmpty()
|
||||
|| uninstalledNewerReleases.size() > 1));
|
||||
.setVisible(mayInstalled.isPresent()
|
||||
&& (!notInstalledPrevious.isEmpty()
|
||||
|| notInstalledNewer.size() > 1));
|
||||
editorCell.downgradeButton
|
||||
.setVisible(installed != null
|
||||
&& (!uninstalledPreviousReleases.isEmpty()
|
||||
|| uninstalledNewerReleases.size() > 1));
|
||||
.setVisible(mayInstalled.isPresent()
|
||||
&& (!notInstalledPrevious.isEmpty()
|
||||
|| notInstalledNewer.size() > 1));
|
||||
|
||||
editorCell.versionToInstallChooser.removeAllItems();
|
||||
uninstalledReleases.forEach(editorCell.versionToInstallChooser::addItem);
|
||||
notInstalled.forEach(editorCell.versionToInstallChooser::addItem);
|
||||
editorCell.versionToInstallChooser
|
||||
.setVisible(installed == null && uninstalledReleases.size() > 1);
|
||||
.setVisible(!mayInstalled.isPresent() && notInstalled.size() > 1);
|
||||
|
||||
editorCell.setForeground(Color.BLACK);
|
||||
editorCell.setBackground(new Color(218, 227, 227)); // #dae3e3
|
||||
return editorCell;
|
||||
}
|
||||
@ -152,7 +144,7 @@ public class ContributedLibraryTableCellEditor extends InstallerTableCell {
|
||||
}
|
||||
|
||||
protected void onInstall(ContributedLibrary selected,
|
||||
ContributedLibrary installed) {
|
||||
Optional<ContributedLibrary> mayInstalled) {
|
||||
// Empty
|
||||
}
|
||||
|
||||
|
@ -3,20 +3,12 @@ package cc.arduino.contributions.libraries.ui;
|
||||
import static processing.app.I18n.format;
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Insets;
|
||||
import java.awt.*;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.html.HTMLDocument;
|
||||
@ -24,12 +16,15 @@ import javax.swing.text.html.StyleSheet;
|
||||
|
||||
import cc.arduino.contributions.DownloadableContributionVersionComparator;
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.ui.InstallerTableCell;
|
||||
import processing.app.Base;
|
||||
import processing.app.PreferencesData;
|
||||
import processing.app.Theme;
|
||||
|
||||
public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
|
||||
final JButton moreInfoButton;
|
||||
final JButton installButton;
|
||||
final Component installButtonPlaceholder;
|
||||
final JComboBox downgradeChooser;
|
||||
@ -38,12 +33,22 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
final JPanel buttonsPanel;
|
||||
final JPanel inactiveButtonsPanel;
|
||||
final JLabel statusLabel;
|
||||
final JTextPane description;
|
||||
final TitledBorder titledBorder;
|
||||
private final String moreInfoLbl = tr("More info");
|
||||
|
||||
public ContributedLibraryTableCellJPanel(JTable parentTable, Object value,
|
||||
boolean isSelected) {
|
||||
super();
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
|
||||
// Actual title set below
|
||||
titledBorder = BorderFactory.createTitledBorder("");
|
||||
titledBorder.setTitleFont(getFont().deriveFont(Font.BOLD));
|
||||
setBorder(titledBorder);
|
||||
|
||||
moreInfoButton = new JButton(moreInfoLbl);
|
||||
moreInfoButton.setVisible(false);
|
||||
installButton = new JButton(tr("Install"));
|
||||
int width = installButton.getPreferredSize().width;
|
||||
installButtonPlaceholder = Box.createRigidArea(new Dimension(width, 1));
|
||||
@ -52,25 +57,37 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
|
||||
downgradeChooser = new JComboBox();
|
||||
downgradeChooser.addItem("-");
|
||||
downgradeChooser.setMaximumSize(downgradeChooser.getPreferredSize());
|
||||
downgradeChooser.addItemListener(e -> {
|
||||
downgradeChooser.setMaximumSize(new Dimension((int)downgradeChooser.getPreferredSize().getWidth() + 50, (int)downgradeChooser.getPreferredSize().getHeight()));
|
||||
downgradeChooser.setMinimumSize(new Dimension((int)downgradeChooser.getPreferredSize().getWidth() + 50, (int)downgradeChooser.getPreferredSize().getHeight()));
|
||||
downgradeChooser.addActionListener(e -> {
|
||||
Object selectVersionItem = downgradeChooser.getItemAt(0);
|
||||
boolean disableDowngrade = (e.getItem() == selectVersionItem);
|
||||
boolean disableDowngrade = (downgradeChooser.getSelectedItem() == selectVersionItem);
|
||||
downgradeButton.setEnabled(!disableDowngrade);
|
||||
if (!disableDowngrade) {
|
||||
InstallerTableCell.dropdownSelected(true);
|
||||
}
|
||||
});
|
||||
|
||||
versionToInstallChooser = new JComboBox();
|
||||
versionToInstallChooser.addItem("-");
|
||||
versionToInstallChooser
|
||||
.setMaximumSize(versionToInstallChooser.getPreferredSize());
|
||||
.setMaximumSize(new Dimension((int)versionToInstallChooser.getPreferredSize().getWidth() + 50, (int)versionToInstallChooser.getPreferredSize().getHeight()));
|
||||
versionToInstallChooser
|
||||
.setMinimumSize(new Dimension((int)versionToInstallChooser.getPreferredSize().getWidth() + 50, (int)versionToInstallChooser.getPreferredSize().getHeight()));
|
||||
|
||||
makeNewDescription();
|
||||
description = makeNewDescription();
|
||||
add(description);
|
||||
|
||||
buttonsPanel = new JPanel();
|
||||
buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.X_AXIS));
|
||||
buttonsPanel.setOpaque(false);
|
||||
|
||||
buttonsPanel.add(Box.createHorizontalStrut(7));
|
||||
if (PreferencesData.getBoolean("ide.accessible")) {
|
||||
buttonsPanel.add(moreInfoButton);
|
||||
buttonsPanel.add(Box.createHorizontalStrut(5));
|
||||
buttonsPanel.add(Box.createHorizontalStrut(15));
|
||||
}
|
||||
buttonsPanel.add(downgradeChooser);
|
||||
buttonsPanel.add(Box.createHorizontalStrut(5));
|
||||
buttonsPanel.add(downgradeButton);
|
||||
@ -103,23 +120,23 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
add(Box.createVerticalStrut(15));
|
||||
|
||||
ContributedLibraryReleases releases = (ContributedLibraryReleases) value;
|
||||
JTextPane description = makeNewDescription();
|
||||
|
||||
// FIXME: happens on macosx, don't know why
|
||||
if (releases == null)
|
||||
return;
|
||||
|
||||
ContributedLibrary selected = releases.getSelected();
|
||||
ContributedLibrary installed = releases.getInstalled();
|
||||
titledBorder.setTitle(selected.getName());
|
||||
Optional<ContributedLibrary> mayInstalled = releases.getInstalled();
|
||||
|
||||
boolean installable, upgradable;
|
||||
if (installed == null) {
|
||||
if (!mayInstalled.isPresent()) {
|
||||
installable = true;
|
||||
upgradable = false;
|
||||
} else {
|
||||
installable = false;
|
||||
upgradable = new DownloadableContributionVersionComparator()
|
||||
.compare(selected, installed) > 0;
|
||||
.compare(selected, mayInstalled.get()) > 0;
|
||||
}
|
||||
if (installable) {
|
||||
installButton.setText(tr("Install"));
|
||||
@ -133,7 +150,7 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
String name = selected.getName();
|
||||
String author = selected.getAuthor();
|
||||
// String maintainer = selectedLib.getMaintainer();
|
||||
String website = selected.getWebsite();
|
||||
final String website = selected.getWebsite();
|
||||
String sentence = selected.getSentence();
|
||||
String paragraph = selected.getParagraph();
|
||||
// String availableVer = selectedLib.getVersion();
|
||||
@ -144,8 +161,8 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
String desc = "<html><body>";
|
||||
|
||||
// Library name...
|
||||
desc += format("<b>{0}</b>", name);
|
||||
if (installed != null && installed.isReadOnly()) {
|
||||
// desc += format("<b>{0}</b>", name);
|
||||
if (mayInstalled.isPresent() && mayInstalled.get().isIDEBuiltIn()) {
|
||||
desc += " Built-In ";
|
||||
}
|
||||
|
||||
@ -156,8 +173,8 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
}
|
||||
|
||||
// ...version.
|
||||
if (installed != null) {
|
||||
String installedVer = installed.getParsedVersion();
|
||||
if (mayInstalled.isPresent()) {
|
||||
String installedVer = mayInstalled.get().getParsedVersion();
|
||||
if (installedVer == null) {
|
||||
desc += " " + tr("Version unknown");
|
||||
} else {
|
||||
@ -166,7 +183,7 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
}
|
||||
desc += "</font>";
|
||||
|
||||
if (installed != null) {
|
||||
if (mayInstalled.isPresent()) {
|
||||
desc += " <strong><font color=\"#00979D\">INSTALLED</font></strong>";
|
||||
}
|
||||
|
||||
@ -180,12 +197,13 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
desc += "<br />";
|
||||
}
|
||||
if (author != null && !author.isEmpty()) {
|
||||
desc += format("<a href=\"{0}\">More info</a>", website);
|
||||
desc = setButtonOrLink(moreInfoButton, desc, moreInfoLbl, website);
|
||||
}
|
||||
|
||||
desc += "</body></html>";
|
||||
description.setText(desc);
|
||||
description.setBackground(Color.WHITE);
|
||||
// copy description to accessibility context for screen readers to use
|
||||
description.getAccessibleContext().setAccessibleDescription(desc);
|
||||
|
||||
// for modelToView to work, the text area has to be sized. It doesn't
|
||||
// matter if it's visible or not.
|
||||
@ -195,20 +213,29 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
InstallerTableCell
|
||||
.setJTextPaneDimensionToFitContainedText(description,
|
||||
parentTable.getBounds().width);
|
||||
|
||||
if (isSelected) {
|
||||
setBackground(parentTable.getSelectionBackground());
|
||||
setForeground(parentTable.getSelectionForeground());
|
||||
} else {
|
||||
setBackground(parentTable.getBackground());
|
||||
setForeground(parentTable.getForeground());
|
||||
}
|
||||
}
|
||||
|
||||
// same function as in ContributedPlatformTableCellJPanel - is there a utils file this can move to?
|
||||
private String setButtonOrLink(JButton button, String desc, String label, String url) {
|
||||
boolean accessibleIDE = PreferencesData.getBoolean("ide.accessible");
|
||||
String retString = desc;
|
||||
|
||||
if (accessibleIDE) {
|
||||
button.setVisible(true);
|
||||
button.addActionListener(e -> {
|
||||
Base.openURL(url);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// if not accessible IDE, keep link the same EXCEPT that now the link text is translated!
|
||||
retString += format("<a href=\"{0}\">{1}</a><br/>", url, label);
|
||||
}
|
||||
|
||||
return retString;
|
||||
}
|
||||
|
||||
// TODO Make this a method of Theme
|
||||
private JTextPane makeNewDescription() {
|
||||
if (getComponentCount() > 0) {
|
||||
remove(0);
|
||||
}
|
||||
JTextPane description = new JTextPane();
|
||||
description.setInheritsPopupMenu(true);
|
||||
Insets margin = description.getMargin();
|
||||
@ -234,7 +261,6 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
}
|
||||
});
|
||||
// description.addKeyListener(new DelegatingKeyListener(parentTable));
|
||||
add(description, 0);
|
||||
return description;
|
||||
}
|
||||
|
||||
@ -243,4 +269,13 @@ public class ContributedLibraryTableCellJPanel extends JPanel {
|
||||
buttonsPanel.setVisible(enabled);
|
||||
inactiveButtonsPanel.setVisible(!enabled);
|
||||
}
|
||||
|
||||
public void setForeground(Color c) {
|
||||
super.setForeground(c);
|
||||
// The description is not opaque, so copy our foreground color to it.
|
||||
if (description != null)
|
||||
description.setForeground(c);
|
||||
if (titledBorder != null)
|
||||
titledBorder.setTitleColor(c);
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ public class ContributedLibraryTableCellRenderer implements TableCellRenderer {
|
||||
value, isSelected);
|
||||
cell.setButtonsVisible(false);
|
||||
|
||||
cell.setForeground(Color.BLACK);
|
||||
if (row % 2 == 0) {
|
||||
cell.setBackground(new Color(236, 241, 241)); // #ecf1f1
|
||||
} else {
|
||||
|
@ -27,29 +27,24 @@
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
|
||||
package cc.arduino.contributions.ui;
|
||||
package cc.arduino.contributions.libraries.ui;
|
||||
|
||||
import cc.arduino.contributions.DownloadableContribution;
|
||||
import cc.arduino.contributions.filters.NoopPredicate;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.ui.DropdownItem;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
public class DropdownAllItem implements DropdownItem<DownloadableContribution> {
|
||||
public class DropdownAllLibraries implements DropdownItem<ContributedLibraryReleases> {
|
||||
|
||||
public String toString() {
|
||||
return tr("All");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<DownloadableContribution> getFilterPredicate() {
|
||||
return new NoopPredicate<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DropdownAllItem;
|
||||
public Predicate<ContributedLibraryReleases> getFilterPredicate() {
|
||||
return x -> true;
|
||||
}
|
||||
|
||||
}
|
@ -29,28 +29,27 @@
|
||||
|
||||
package cc.arduino.contributions.libraries.ui;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.filters.InstalledLibraryPredicate;
|
||||
import cc.arduino.contributions.ui.DropdownItem;
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.ui.DropdownItem;
|
||||
|
||||
public class DropdownInstalledLibraryItem implements DropdownItem<ContributedLibrary> {
|
||||
public class DropdownInstalledLibraryItem implements DropdownItem<ContributedLibraryReleases> {
|
||||
|
||||
public String toString() {
|
||||
return tr("Installed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ContributedLibrary> getFilterPredicate() {
|
||||
return new InstalledLibraryPredicate();
|
||||
}
|
||||
|
||||
public Predicate<ContributedLibraryReleases> getFilterPredicate() {
|
||||
return new Predicate<ContributedLibraryReleases>() {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DropdownInstalledLibraryItem;
|
||||
public boolean test(ContributedLibraryReleases t) {
|
||||
return t.getInstalled().isPresent();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,14 +30,14 @@
|
||||
package cc.arduino.contributions.libraries.ui;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.filters.CategoryPredicate;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.ui.DropdownItem;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
public class DropdownLibraryOfCategoryItem implements DropdownItem<ContributedLibrary> {
|
||||
public class DropdownLibraryOfCategoryItem implements DropdownItem<ContributedLibraryReleases> {
|
||||
|
||||
private final String category;
|
||||
|
||||
@ -50,13 +50,14 @@ public class DropdownLibraryOfCategoryItem implements DropdownItem<ContributedLi
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ContributedLibrary> getFilterPredicate() {
|
||||
return new CategoryPredicate(category);
|
||||
}
|
||||
|
||||
public Predicate<ContributedLibraryReleases> getFilterPredicate() {
|
||||
return new Predicate<ContributedLibraryReleases>() {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DropdownLibraryOfCategoryItem && ((DropdownLibraryOfCategoryItem) obj).category.equals(category);
|
||||
public boolean test(ContributedLibraryReleases rel) {
|
||||
ContributedLibrary lib = rel.getLatest();
|
||||
return category.equals(lib.getCategory());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,15 +29,15 @@
|
||||
|
||||
package cc.arduino.contributions.libraries.ui;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.filters.TypePredicate;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.ui.DropdownItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
public class DropdownLibraryOfTypeItem implements DropdownItem<ContributedLibrary> {
|
||||
public class DropdownLibraryOfTypeItem implements DropdownItem<ContributedLibraryReleases> {
|
||||
|
||||
private final String type;
|
||||
|
||||
@ -50,13 +50,14 @@ public class DropdownLibraryOfTypeItem implements DropdownItem<ContributedLibrar
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ContributedLibrary> getFilterPredicate() {
|
||||
return new TypePredicate(type);
|
||||
}
|
||||
|
||||
public Predicate<ContributedLibraryReleases> getFilterPredicate() {
|
||||
return new Predicate<ContributedLibraryReleases>() {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DropdownLibraryOfTypeItem && ((DropdownLibraryOfTypeItem) obj).type.equals(type);
|
||||
public boolean test(ContributedLibraryReleases lib) {
|
||||
List<String> types = lib.getLatest().getTypes();
|
||||
return types != null && types.contains(type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,18 +30,28 @@
|
||||
package cc.arduino.contributions.libraries.ui;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.filters.UpdatableLibraryPredicate;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.ui.DropdownItem;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
public class DropdownUpdatableLibrariesItem implements DropdownItem<ContributedLibrary> {
|
||||
public class DropdownUpdatableLibrariesItem implements DropdownItem<ContributedLibraryReleases> {
|
||||
|
||||
@Override
|
||||
public Predicate<ContributedLibrary> getFilterPredicate() {
|
||||
return new UpdatableLibraryPredicate();
|
||||
public Predicate<ContributedLibraryReleases> getFilterPredicate() {
|
||||
return new Predicate<ContributedLibraryReleases>() {
|
||||
@Override
|
||||
public boolean test(ContributedLibraryReleases lib) {
|
||||
Optional<ContributedLibrary> mayInstalled = lib.getInstalled();
|
||||
if (!mayInstalled.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
return !lib.getLatest().equals(mayInstalled.get());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,9 +59,4 @@ public class DropdownUpdatableLibrariesItem implements DropdownItem<ContributedL
|
||||
return tr("Updatable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DropdownUpdatableLibrariesItem;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
package cc.arduino.contributions.libraries.ui;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.packages.ContributedPlatform;
|
||||
import cc.arduino.contributions.ui.FilteredAbstractTableModel;
|
||||
import processing.app.BaseNoGui;
|
||||
@ -38,11 +39,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class LibrariesIndexTableModel
|
||||
extends FilteredAbstractTableModel<ContributedLibrary> {
|
||||
extends FilteredAbstractTableModel<ContributedLibraryReleases> {
|
||||
|
||||
private final List<ContributedLibraryReleases> contributions = new ArrayList<>();
|
||||
|
||||
@ -50,12 +50,12 @@ public class LibrariesIndexTableModel
|
||||
|
||||
private final Class<?>[] columnTypes = { ContributedPlatform.class };
|
||||
|
||||
Predicate<ContributedLibrary> selectedCategoryFilter = null;
|
||||
Predicate<ContributedLibraryReleases> selectedCategoryFilter = null;
|
||||
String selectedFilters[] = null;
|
||||
|
||||
public void updateIndexFilter(String filters[],
|
||||
Stream<Predicate<ContributedLibrary>> additionalFilters) {
|
||||
selectedCategoryFilter = additionalFilters.reduce(Predicate::and).get();
|
||||
Predicate<ContributedLibraryReleases> additionalFilter) {
|
||||
selectedCategoryFilter = additionalFilter;
|
||||
selectedFilters = filters;
|
||||
update();
|
||||
}
|
||||
@ -87,17 +87,6 @@ public class LibrariesIndexTableModel
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addContribution(ContributedLibrary lib) {
|
||||
for (ContributedLibraryReleases contribution : contributions) {
|
||||
if (!contribution.shouldContain(lib))
|
||||
continue;
|
||||
contribution.add(lib);
|
||||
return;
|
||||
}
|
||||
|
||||
contributions.add(new ContributedLibraryReleases(lib));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnNames.length;
|
||||
@ -150,17 +139,23 @@ public class LibrariesIndexTableModel
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
private void applyFilterToLibrary(ContributedLibrary lib) {
|
||||
private boolean filterCondition(ContributedLibraryReleases lib) {
|
||||
if (selectedCategoryFilter != null && !selectedCategoryFilter.test(lib)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
String compoundTargetSearchText = lib.getName() + "\n" + lib.getParagraph()
|
||||
+ "\n" + lib.getSentence();
|
||||
if (!stringContainsAll(compoundTargetSearchText, selectedFilters)) {
|
||||
return;
|
||||
ContributedLibrary latest = lib.getLatest();
|
||||
String compoundTargetSearchText = latest.getName() + " "
|
||||
+ latest.getParagraph() + " "
|
||||
+ latest.getSentence();
|
||||
if (latest.getProvidesIncludes() != null) {
|
||||
compoundTargetSearchText += " " + latest.getProvidesIncludes();
|
||||
}
|
||||
addContribution(lib);
|
||||
if (!stringContainsAll(compoundTargetSearchText, selectedFilters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void updateLibrary(ContributedLibrary lib) {
|
||||
@ -190,12 +185,26 @@ public class LibrariesIndexTableModel
|
||||
fireTableRowsDeleted(row, row);
|
||||
}
|
||||
|
||||
private List<ContributedLibraryReleases> rebuildContributionsFromIndex() {
|
||||
List<ContributedLibraryReleases> res = new ArrayList<>();
|
||||
BaseNoGui.librariesIndexer.getIndex().getLibraries(). //
|
||||
forEach(lib -> {
|
||||
for (ContributedLibraryReleases contribution : res) {
|
||||
if (!contribution.shouldContain(lib))
|
||||
continue;
|
||||
contribution.add(lib);
|
||||
return;
|
||||
}
|
||||
|
||||
res.add(new ContributedLibraryReleases(lib));
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
private void updateContributions() {
|
||||
List<ContributedLibraryReleases> all = rebuildContributionsFromIndex();
|
||||
contributions.clear();
|
||||
BaseNoGui.librariesIndexer.getIndex().getLibraries()
|
||||
.forEach(this::applyFilterToLibrary);
|
||||
BaseNoGui.librariesIndexer.getInstalledLibraries()
|
||||
.forEach(this::applyFilterToLibrary);
|
||||
all.stream().filter(this::filterCondition).forEach(contributions::add);
|
||||
Collections.sort(contributions,
|
||||
new ContributedLibraryReleasesComparator("Arduino"));
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ import java.awt.event.ActionListener;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.swing.Box;
|
||||
@ -46,11 +48,11 @@ import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
import cc.arduino.contributions.DownloadableContribution;
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.ContributedLibraryReleases;
|
||||
import cc.arduino.contributions.libraries.LibraryInstaller;
|
||||
import cc.arduino.contributions.libraries.LibraryTypeComparator;
|
||||
import cc.arduino.contributions.ui.DropdownAllItem;
|
||||
import cc.arduino.contributions.libraries.ui.MultiLibraryInstallDialog.Result;
|
||||
import cc.arduino.contributions.ui.DropdownItem;
|
||||
import cc.arduino.contributions.ui.FilteredAbstractTableModel;
|
||||
import cc.arduino.contributions.ui.InstallerJDialog;
|
||||
@ -60,11 +62,11 @@ import cc.arduino.utils.Progress;
|
||||
import processing.app.BaseNoGui;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
|
||||
public class LibraryManagerUI extends InstallerJDialog<ContributedLibraryReleases> {
|
||||
|
||||
private final JComboBox typeChooser;
|
||||
private final LibraryInstaller installer;
|
||||
private Predicate<ContributedLibrary> typeFilter;
|
||||
private Predicate<ContributedLibraryReleases> typeFilter;
|
||||
|
||||
@Override
|
||||
protected FilteredAbstractTableModel createContribModel() {
|
||||
@ -80,11 +82,11 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
|
||||
protected InstallerTableCell createCellEditor() {
|
||||
return new ContributedLibraryTableCellEditor() {
|
||||
@Override
|
||||
protected void onInstall(ContributedLibrary selectedLibrary, ContributedLibrary installedLibrary) {
|
||||
if (selectedLibrary.isReadOnly()) {
|
||||
onRemovePressed(installedLibrary);
|
||||
protected void onInstall(ContributedLibrary selectedLibrary, Optional<ContributedLibrary> mayInstalledLibrary) {
|
||||
if (mayInstalledLibrary.isPresent() && selectedLibrary.isIDEBuiltIn()) {
|
||||
onRemovePressed(mayInstalledLibrary.get());
|
||||
} else {
|
||||
onInstallPressed(selectedLibrary, installedLibrary);
|
||||
onInstallPressed(selectedLibrary);
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,37 +118,29 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
DropdownItem<ContributedLibrary> selected = (DropdownItem<ContributedLibrary>) typeChooser.getSelectedItem();
|
||||
if (typeFilter == null || !typeFilter.equals(selected)) {
|
||||
DropdownItem<ContributedLibraryReleases> selected = (DropdownItem<ContributedLibraryReleases>) typeChooser.getSelectedItem();
|
||||
previousRowAtPoint = -1;
|
||||
if (selected != null && typeFilter != selected.getFilterPredicate()) {
|
||||
typeFilter = selected.getFilterPredicate();
|
||||
if (contribTable.getCellEditor() != null) {
|
||||
contribTable.getCellEditor().stopCellEditing();
|
||||
}
|
||||
updateIndexFilter(filters, categoryFilter, typeFilter);
|
||||
updateIndexFilter(filters, categoryFilter.and(typeFilter));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void updateIndexFilter(String[] filters, Predicate<ContributedLibrary>... additionalFilters) {
|
||||
if (additionalFilters.length == 1) {
|
||||
additionalFilters = new Predicate[]{additionalFilters[0], typeFilter};
|
||||
}
|
||||
super.updateIndexFilter(filters, additionalFilters);
|
||||
}
|
||||
|
||||
public void updateUI() {
|
||||
DropdownItem<DownloadableContribution> previouslySelectedCategory = (DropdownItem<DownloadableContribution>) categoryChooser.getSelectedItem();
|
||||
DropdownItem<DownloadableContribution> previouslySelectedType = (DropdownItem<DownloadableContribution>) typeChooser.getSelectedItem();
|
||||
DropdownItem<ContributedLibraryReleases> previouslySelectedCategory = (DropdownItem<ContributedLibraryReleases>) categoryChooser.getSelectedItem();
|
||||
DropdownItem<ContributedLibraryReleases> previouslySelectedType = (DropdownItem<ContributedLibraryReleases>) typeChooser.getSelectedItem();
|
||||
|
||||
categoryChooser.removeActionListener(categoryChooserActionListener);
|
||||
typeChooser.removeActionListener(typeChooserActionListener);
|
||||
|
||||
categoryFilter = null;
|
||||
categoryChooser.removeAllItems();
|
||||
|
||||
// Load categories
|
||||
categoryChooser.addItem(new DropdownAllItem());
|
||||
categoryFilter = x -> true;
|
||||
categoryChooser.removeAllItems();
|
||||
categoryChooser.addItem(new DropdownAllLibraries());
|
||||
Collection<String> categories = BaseNoGui.librariesIndexer.getIndex().getCategories();
|
||||
for (String category : categories) {
|
||||
categoryChooser.addItem(new DropdownLibraryOfCategoryItem(category));
|
||||
@ -161,12 +155,12 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
|
||||
categoryChooser.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
typeFilter = null;
|
||||
typeFilter = x -> true;
|
||||
typeChooser.removeAllItems();
|
||||
typeChooser.addItem(new DropdownAllItem());
|
||||
typeChooser.addItem(new DropdownAllLibraries());
|
||||
typeChooser.addItem(new DropdownUpdatableLibrariesItem());
|
||||
typeChooser.addItem(new DropdownInstalledLibraryItem());
|
||||
java.util.List<String> types = new LinkedList<>(BaseNoGui.librariesIndexer.getIndex().getTypes());
|
||||
List<String> types = new LinkedList<>(BaseNoGui.librariesIndexer.getIndex().getTypes());
|
||||
Collections.sort(types, new LibraryTypeComparator());
|
||||
for (String type : types) {
|
||||
typeChooser.addItem(new DropdownLibraryOfTypeItem(type));
|
||||
@ -207,6 +201,7 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
|
||||
try {
|
||||
setProgressVisible(true, "");
|
||||
installer.updateIndex(this::setProgress);
|
||||
((LibrariesIndexTableModel) contribModel).update();
|
||||
onIndexesUpdated();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -219,14 +214,36 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
|
||||
installerThread.start();
|
||||
}
|
||||
|
||||
public void onInstallPressed(final ContributedLibrary lib, final ContributedLibrary replaced) {
|
||||
public void onInstallPressed(final ContributedLibrary lib) {
|
||||
List<ContributedLibrary> deps = BaseNoGui.librariesIndexer.getIndex().resolveDependeciesOf(lib);
|
||||
boolean depsInstalled = deps.stream().allMatch(l -> l.getInstalledLibrary().isPresent() || l.getName().equals(lib.getName()));
|
||||
Result installDeps;
|
||||
if (!depsInstalled) {
|
||||
MultiLibraryInstallDialog dialog;
|
||||
dialog = new MultiLibraryInstallDialog(this, lib, deps);
|
||||
dialog.setLocationRelativeTo(this);
|
||||
dialog.setVisible(true);
|
||||
installDeps = dialog.getInstallDepsResult();
|
||||
if (installDeps == Result.CANCEL)
|
||||
return;
|
||||
} else {
|
||||
installDeps = Result.NONE;
|
||||
}
|
||||
clearErrorMessage();
|
||||
installerThread = new Thread(() -> {
|
||||
try {
|
||||
setProgressVisible(true, tr("Installing..."));
|
||||
installer.install(lib, replaced, this::setProgress);
|
||||
onIndexesUpdated(); // TODO: Do a better job in refreshing only the needed element
|
||||
//getContribModel().updateLibrary(lib);
|
||||
if (installDeps == Result.ALL) {
|
||||
installer.install(deps, this::setProgress);
|
||||
} else {
|
||||
installer.install(lib, this::setProgress);
|
||||
}
|
||||
// TODO: Do a better job in refreshing only the needed element
|
||||
if (contribTable.getCellEditor() != null) {
|
||||
contribTable.getCellEditor().stopCellEditing();
|
||||
}
|
||||
((LibrariesIndexTableModel) contribModel).update();
|
||||
onIndexesUpdated();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
@ -253,8 +270,12 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
|
||||
try {
|
||||
setProgressVisible(true, tr("Removing..."));
|
||||
installer.remove(lib, this::setProgress);
|
||||
onIndexesUpdated(); // TODO: Do a better job in refreshing only the needed element
|
||||
//getContribModel().updateLibrary(lib);
|
||||
// TODO: Do a better job in refreshing only the needed element
|
||||
if (contribTable.getCellEditor() != null) {
|
||||
contribTable.getCellEditor().stopCellEditing();
|
||||
}
|
||||
((LibrariesIndexTableModel) contribModel).update();
|
||||
onIndexesUpdated();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
@ -269,5 +290,4 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
|
||||
protected void onIndexesUpdated() throws Exception {
|
||||
// Empty
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2017 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
|
||||
package cc.arduino.contributions.libraries.ui;
|
||||
|
||||
import static processing.app.I18n.format;
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.WindowConstants;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.html.HTMLDocument;
|
||||
import javax.swing.text.html.StyleSheet;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.UnavailableContributedLibrary;
|
||||
import processing.app.Base;
|
||||
import processing.app.Theme;
|
||||
|
||||
public class MultiLibraryInstallDialog extends JDialog {
|
||||
|
||||
enum Result {
|
||||
ALL, NONE, CANCEL
|
||||
}
|
||||
|
||||
private Result result = Result.CANCEL;
|
||||
|
||||
public MultiLibraryInstallDialog(Window parent, ContributedLibrary lib,
|
||||
List<ContributedLibrary> dependencies) {
|
||||
super(parent, format(tr("Dependencies for library {0}:{1}"), lib.getName(),
|
||||
lib.getParsedVersion()),
|
||||
ModalityType.APPLICATION_MODAL);
|
||||
Container pane = getContentPane();
|
||||
pane.setLayout(new BorderLayout());
|
||||
|
||||
pane.add(Box.createHorizontalStrut(10), BorderLayout.WEST);
|
||||
pane.add(Box.createHorizontalStrut(10), BorderLayout.EAST);
|
||||
|
||||
{
|
||||
JButton cancel = new JButton(tr("Cancel"));
|
||||
cancel.addActionListener(ev -> {
|
||||
result = Result.CANCEL;
|
||||
setVisible(false);
|
||||
});
|
||||
|
||||
JButton all = new JButton(tr("Install all"));
|
||||
all.addActionListener(ev -> {
|
||||
result = Result.ALL;
|
||||
setVisible(false);
|
||||
});
|
||||
|
||||
JButton none = new JButton(format(tr("Install '{0}' only"), lib.getName()));
|
||||
none.addActionListener(ev -> {
|
||||
result = Result.NONE;
|
||||
setVisible(false);
|
||||
});
|
||||
|
||||
Box buttonsBox = Box.createHorizontalBox();
|
||||
buttonsBox.add(all);
|
||||
buttonsBox.add(Box.createHorizontalStrut(5));
|
||||
buttonsBox.add(none);
|
||||
buttonsBox.add(Box.createHorizontalStrut(5));
|
||||
buttonsBox.add(cancel);
|
||||
|
||||
JPanel buttonsPanel = new JPanel();
|
||||
buttonsPanel.setBorder(new EmptyBorder(7, 10, 7, 10));
|
||||
buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.Y_AXIS));
|
||||
buttonsPanel.add(buttonsBox);
|
||||
|
||||
pane.add(buttonsPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
{
|
||||
String libName = format("<b>{0}:{1}</b>", lib.getName(),
|
||||
lib.getParsedVersion());
|
||||
String desc = format(tr("The library {0} needs some other library<br />dependencies currently not installed:"),
|
||||
libName);
|
||||
desc += "<br/><br/>";
|
||||
for (ContributedLibrary l : dependencies) {
|
||||
if (l.getName().equals(lib.getName()))
|
||||
continue;
|
||||
if (l.getInstalledLibrary().isPresent())
|
||||
continue;
|
||||
if (l instanceof UnavailableContributedLibrary)
|
||||
continue;
|
||||
desc += format("- <b>{0}</b><br/>", l.getName());
|
||||
}
|
||||
desc += "<br/>";
|
||||
desc += tr("Would you like to install also all the missing dependencies?");
|
||||
|
||||
JTextPane textArea = makeNewDescription();
|
||||
textArea.setContentType("text/html");
|
||||
textArea.setText(desc);
|
||||
|
||||
JPanel libsList = new JPanel();
|
||||
libsList.setLayout(new BoxLayout(libsList, BoxLayout.Y_AXIS));
|
||||
libsList.add(textArea);
|
||||
libsList.setBorder(new EmptyBorder(7, 7, 7, 7));
|
||||
pane.add(libsList, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
pack();
|
||||
setResizable(false);
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
|
||||
WindowEvent closing = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
|
||||
Base.registerWindowCloseKeys(getRootPane(), e -> dispatchEvent(closing));
|
||||
}
|
||||
|
||||
// TODO Make this a method of Theme
|
||||
private JTextPane makeNewDescription() {
|
||||
JTextPane description = new JTextPane();
|
||||
description.setInheritsPopupMenu(true);
|
||||
Insets margin = description.getMargin();
|
||||
margin.bottom = 0;
|
||||
description.setMargin(margin);
|
||||
description.setContentType("text/html");
|
||||
Document doc = description.getDocument();
|
||||
if (doc instanceof HTMLDocument) {
|
||||
HTMLDocument html = (HTMLDocument) doc;
|
||||
StyleSheet s = html.getStyleSheet();
|
||||
s.addRule("body { margin: 0; padding: 0;"
|
||||
+ "font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;"
|
||||
+ "color: black;" + "font-size: " + 15 * Theme.getScale() / 100
|
||||
+ "; }");
|
||||
}
|
||||
description.setOpaque(false);
|
||||
description.setBorder(new EmptyBorder(4, 7, 7, 7));
|
||||
description.setHighlighter(null);
|
||||
description.setEditable(false);
|
||||
add(description, 0);
|
||||
return description;
|
||||
}
|
||||
|
||||
public Result getInstallDepsResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
@ -46,9 +46,4 @@ public class CategoryPredicate implements Predicate<ContributedPlatform> {
|
||||
return input.getCategory() != null && category.equals(input.getCategory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof CategoryPredicate && ((CategoryPredicate) obj).category.equals(category);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,12 +38,6 @@ import java.util.function.Predicate;
|
||||
|
||||
public class UpdatablePlatformPredicate implements Predicate<ContributedPlatform> {
|
||||
|
||||
private final VersionComparator versionComparator;
|
||||
|
||||
public UpdatablePlatformPredicate() {
|
||||
this.versionComparator = new VersionComparator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(ContributedPlatform contributedPlatform) {
|
||||
String packageName = contributedPlatform.getParentPackage().getName();
|
||||
@ -56,7 +50,7 @@ public class UpdatablePlatformPredicate implements Predicate<ContributedPlatform
|
||||
|
||||
List<ContributedPlatform> platforms = BaseNoGui.indexer.getIndex().findPlatforms(packageName, architecture);
|
||||
return platforms.stream()
|
||||
.filter(platform -> versionComparator.greaterThan(platform.getParsedVersion(), installed.getParsedVersion()))
|
||||
.filter(platform -> VersionComparator.greaterThan(platform.getParsedVersion(), installed.getParsedVersion()))
|
||||
.count() > 0;
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,6 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator;
|
||||
import cc.arduino.contributions.filters.InstalledPredicate;
|
||||
import cc.arduino.contributions.packages.ContributedPackage;
|
||||
import cc.arduino.contributions.packages.ContributedPlatform;
|
||||
|
||||
@ -72,10 +70,9 @@ public class ContributedPlatformReleases {
|
||||
|
||||
public ContributedPlatform getInstalled() {
|
||||
List<ContributedPlatform> installedReleases = releases.stream()
|
||||
.filter(new InstalledPredicate()).collect(Collectors.toList());
|
||||
Collections
|
||||
.sort(installedReleases,
|
||||
new DownloadableContributionBuiltInAtTheBottomComparator());
|
||||
.filter(p -> p.isInstalled()) //
|
||||
.collect(Collectors.toList());
|
||||
Collections.sort(installedReleases, ContributedPlatform.BUILTIN_AS_LAST);
|
||||
|
||||
if (installedReleases.isEmpty()) {
|
||||
return null;
|
||||
|
@ -42,8 +42,6 @@ import javax.swing.JTable;
|
||||
|
||||
import cc.arduino.contributions.DownloadableContributionVersionComparator;
|
||||
import cc.arduino.contributions.VersionComparator;
|
||||
import cc.arduino.contributions.filters.BuiltInPredicate;
|
||||
import cc.arduino.contributions.filters.InstalledPredicate;
|
||||
import cc.arduino.contributions.packages.ContributedPlatform;
|
||||
import cc.arduino.contributions.ui.InstallerTableCell;
|
||||
import cc.arduino.utils.ReverseComparator;
|
||||
@ -74,20 +72,25 @@ public class ContributedPlatformTableCellEditor extends InstallerTableCell {
|
||||
.getSelectedItem();
|
||||
onInstall(selected, value.getInstalled());
|
||||
});
|
||||
cell.versionToInstallChooser.addItemListener(e -> value
|
||||
.select((ContributedPlatform) cell.versionToInstallChooser
|
||||
.getSelectedItem()));
|
||||
cell.versionToInstallChooser.addActionListener(e -> {
|
||||
value.select((ContributedPlatform) cell.versionToInstallChooser.getSelectedItem());
|
||||
if (cell.versionToInstallChooser.getSelectedIndex() != 0) {
|
||||
InstallerTableCell.dropdownSelected(true);
|
||||
}
|
||||
});
|
||||
|
||||
setEnabled(true);
|
||||
|
||||
final ContributedPlatform installed = value.getInstalled();
|
||||
|
||||
List<ContributedPlatform> releases = new LinkedList<>(value.releases);
|
||||
List<ContributedPlatform> uninstalledReleases = releases.stream()
|
||||
.filter(new InstalledPredicate().negate()).collect(Collectors.toList());
|
||||
List<ContributedPlatform> uninstalledReleases = releases.stream() //
|
||||
.filter(p -> !p.isInstalled()) //
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<ContributedPlatform> installedBuiltIn = releases.stream()
|
||||
.filter(new InstalledPredicate()).filter(new BuiltInPredicate())
|
||||
List<ContributedPlatform> installedBuiltIn = releases.stream() //
|
||||
.filter(p -> p.isInstalled()) //
|
||||
.filter(p -> p.isBuiltIn()) //
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (installed != null && !installedBuiltIn.contains(installed)) {
|
||||
@ -103,10 +106,9 @@ public class ContributedPlatformTableCellEditor extends InstallerTableCell {
|
||||
final List<ContributedPlatform> uninstalledPreviousReleases = new LinkedList<>();
|
||||
final List<ContributedPlatform> uninstalledNewerReleases = new LinkedList<>();
|
||||
|
||||
final VersionComparator versionComparator = new VersionComparator();
|
||||
uninstalledReleases.stream().forEach(input -> {
|
||||
if (installed == null
|
||||
|| versionComparator.greaterThan(installed.getParsedVersion(),
|
||||
|| VersionComparator.greaterThan(installed.getParsedVersion(),
|
||||
input.getParsedVersion())) {
|
||||
uninstalledPreviousReleases.add(input);
|
||||
} else {
|
||||
@ -127,7 +129,8 @@ public class ContributedPlatformTableCellEditor extends InstallerTableCell {
|
||||
cell.versionToInstallChooser
|
||||
.setVisible(installed == null && uninstalledReleases.size() > 1);
|
||||
|
||||
cell.update(table, _value, true, !installedBuiltIn.isEmpty());
|
||||
cell.update(table, _value, !installedBuiltIn.isEmpty());
|
||||
cell.setForeground(Color.BLACK);
|
||||
cell.setBackground(new Color(218, 227, 227)); // #dae3e3
|
||||
return cell;
|
||||
}
|
||||
|
@ -32,20 +32,11 @@ package cc.arduino.contributions.packages.ui;
|
||||
import static processing.app.I18n.format;
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Insets;
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.html.HTMLDocument;
|
||||
@ -57,11 +48,14 @@ import cc.arduino.contributions.packages.ContributedHelp;
|
||||
import cc.arduino.contributions.packages.ContributedPlatform;
|
||||
import cc.arduino.contributions.ui.InstallerTableCell;
|
||||
import processing.app.Base;
|
||||
import processing.app.PreferencesData;
|
||||
import processing.app.Theme;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
|
||||
final JButton moreInfoButton;
|
||||
final JButton onlineHelpButton;
|
||||
final JButton installButton;
|
||||
final JButton removeButton;
|
||||
final Component removeButtonPlaceholder;
|
||||
@ -72,13 +66,26 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
final JPanel buttonsPanel;
|
||||
final JPanel inactiveButtonsPanel;
|
||||
final JLabel statusLabel;
|
||||
final JTextPane description;
|
||||
final TitledBorder titledBorder;
|
||||
private final String moreInfoLbl = tr("More Info");
|
||||
private final String onlineHelpLbl = tr("Online Help");
|
||||
|
||||
public ContributedPlatformTableCellJPanel() {
|
||||
super();
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
|
||||
// Actual title set by update()
|
||||
titledBorder = BorderFactory.createTitledBorder("");
|
||||
titledBorder.setTitleFont(getFont().deriveFont(Font.BOLD));
|
||||
setBorder(titledBorder);
|
||||
|
||||
{
|
||||
installButton = new JButton(tr("Install"));
|
||||
moreInfoButton = new JButton(moreInfoLbl);
|
||||
moreInfoButton.setVisible(false);
|
||||
onlineHelpButton = new JButton(onlineHelpLbl);
|
||||
onlineHelpButton.setVisible(false);
|
||||
int width = installButton.getPreferredSize().width;
|
||||
installButtonPlaceholder = Box.createRigidArea(new Dimension(width, 1));
|
||||
}
|
||||
@ -98,6 +105,9 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
Object selectVersionItem = downgradeChooser.getItemAt(0);
|
||||
boolean disableDowngrade = (e.getItem() == selectVersionItem);
|
||||
downgradeButton.setEnabled(!disableDowngrade);
|
||||
if (!disableDowngrade) {
|
||||
InstallerTableCell.dropdownSelected(true);
|
||||
}
|
||||
});
|
||||
|
||||
versionToInstallChooser = new JComboBox();
|
||||
@ -105,13 +115,21 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
versionToInstallChooser
|
||||
.setMaximumSize(versionToInstallChooser.getPreferredSize());
|
||||
|
||||
makeNewDescription();
|
||||
description = makeNewDescription();
|
||||
add(description);
|
||||
|
||||
buttonsPanel = new JPanel();
|
||||
buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.X_AXIS));
|
||||
buttonsPanel.setOpaque(false);
|
||||
|
||||
buttonsPanel.add(Box.createHorizontalStrut(7));
|
||||
if (PreferencesData.getBoolean("ide.accessible")) { // only add the buttons if needed
|
||||
buttonsPanel.add(onlineHelpButton);
|
||||
buttonsPanel.add(Box.createHorizontalStrut(5));
|
||||
buttonsPanel.add(moreInfoButton);
|
||||
buttonsPanel.add(Box.createHorizontalStrut(5));
|
||||
buttonsPanel.add(Box.createHorizontalStrut(15));
|
||||
}
|
||||
buttonsPanel.add(downgradeChooser);
|
||||
buttonsPanel.add(Box.createHorizontalStrut(5));
|
||||
buttonsPanel.add(downgradeButton);
|
||||
@ -146,11 +164,27 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
add(Box.createVerticalStrut(15));
|
||||
}
|
||||
|
||||
void update(JTable parentTable, Object value, boolean isSelected,
|
||||
boolean hasBuiltInRelease) {
|
||||
ContributedPlatformReleases releases = (ContributedPlatformReleases) value;
|
||||
// same function as in ContributedLibraryTableCellJPanel - is there a utils file this can move to?
|
||||
private String setButtonOrLink(JButton button, String desc, String label, String url) {
|
||||
boolean accessibleIDE = PreferencesData.getBoolean("ide.accessible");
|
||||
String retString = desc;
|
||||
|
||||
JTextPane description = makeNewDescription();
|
||||
if (accessibleIDE) {
|
||||
button.setVisible(true);
|
||||
button.addActionListener(e -> {
|
||||
Base.openURL(url);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// if not accessible IDE, keep link the same EXCEPT that now the link text is translated!
|
||||
retString += " " + format("<a href=\"{0}\">{1}</a><br/>", url, label);
|
||||
}
|
||||
|
||||
return retString;
|
||||
}
|
||||
|
||||
void update(JTable parentTable, Object value, boolean hasBuiltInRelease) {
|
||||
ContributedPlatformReleases releases = (ContributedPlatformReleases) value;
|
||||
|
||||
// FIXME: happens on macosx, don't know why
|
||||
if (releases == null) {
|
||||
@ -158,6 +192,7 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
}
|
||||
|
||||
ContributedPlatform selected = releases.getSelected();
|
||||
titledBorder.setTitle(selected.getName());
|
||||
ContributedPlatform installed = releases.getInstalled();
|
||||
|
||||
boolean removable, installable, upgradable;
|
||||
@ -167,7 +202,7 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
upgradable = false;
|
||||
} else {
|
||||
installable = false;
|
||||
removable = !installed.isReadOnly() && !hasBuiltInRelease;
|
||||
removable = !installed.isBuiltIn() && !hasBuiltInRelease;
|
||||
upgradable = new DownloadableContributionVersionComparator()
|
||||
.compare(selected, installed) > 0;
|
||||
}
|
||||
@ -183,8 +218,8 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
removeButtonPlaceholder.setVisible(!removable);
|
||||
|
||||
String desc = "<html><body>";
|
||||
desc += "<b>" + selected.getName() + "</b>";
|
||||
if (installed != null && installed.isReadOnly()) {
|
||||
// desc += "<b>" + selected.getName() + "</b>";
|
||||
if (installed != null && installed.isBuiltIn()) {
|
||||
desc += " Built-In ";
|
||||
}
|
||||
|
||||
@ -213,21 +248,23 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
} else if (selected.getParentPackage().getHelp() != null) {
|
||||
help = selected.getParentPackage().getHelp();
|
||||
}
|
||||
|
||||
if (help != null) {
|
||||
String url = help.getOnline();
|
||||
if (url != null && !url.isEmpty()) {
|
||||
desc += " " + format("<a href=\"{0}\">Online help</a><br/>", url);
|
||||
desc = setButtonOrLink(onlineHelpButton, desc, onlineHelpLbl, url);
|
||||
}
|
||||
}
|
||||
|
||||
String url = selected.getParentPackage().getWebsiteURL();
|
||||
if (url != null && !url.isEmpty()) {
|
||||
desc += " " + format("<a href=\"{0}\">More info</a>", url);
|
||||
desc = setButtonOrLink(moreInfoButton, desc, moreInfoLbl, url);
|
||||
}
|
||||
|
||||
desc += "</body></html>";
|
||||
description.setText(desc);
|
||||
description.setBackground(Color.WHITE);
|
||||
// copy description to accessibility context for screen readers to use
|
||||
description.getAccessibleContext().setAccessibleDescription(desc);
|
||||
|
||||
// for modelToView to work, the text area has to be sized. It doesn't
|
||||
// matter if it's visible or not.
|
||||
@ -237,20 +274,9 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
int width = parentTable.getBounds().width;
|
||||
InstallerTableCell.setJTextPaneDimensionToFitContainedText(description,
|
||||
width);
|
||||
|
||||
if (isSelected) {
|
||||
setBackground(parentTable.getSelectionBackground());
|
||||
setForeground(parentTable.getSelectionForeground());
|
||||
} else {
|
||||
setBackground(parentTable.getBackground());
|
||||
setForeground(parentTable.getForeground());
|
||||
}
|
||||
}
|
||||
|
||||
private JTextPane makeNewDescription() {
|
||||
if (getComponentCount() > 0) {
|
||||
remove(0);
|
||||
}
|
||||
JTextPane description = new JTextPane();
|
||||
description.setInheritsPopupMenu(true);
|
||||
Insets margin = description.getMargin();
|
||||
@ -274,7 +300,6 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
Base.openURL(e.getDescription());
|
||||
}
|
||||
});
|
||||
add(description, 0);
|
||||
return description;
|
||||
}
|
||||
|
||||
@ -285,4 +310,12 @@ public class ContributedPlatformTableCellJPanel extends JPanel {
|
||||
inactiveButtonsPanel.setVisible(!enabled);
|
||||
}
|
||||
|
||||
public void setForeground(Color c) {
|
||||
super.setForeground(c);
|
||||
// The description is not opaque, so copy our foreground color to it.
|
||||
if (description != null)
|
||||
description.setForeground(c);
|
||||
if (titledBorder != null)
|
||||
titledBorder.setTitleColor(c);
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,9 @@ public class ContributedPlatformTableCellRenderer implements TableCellRenderer {
|
||||
int column) {
|
||||
ContributedPlatformTableCellJPanel cell = new ContributedPlatformTableCellJPanel();
|
||||
cell.setButtonsVisible(false);
|
||||
cell.update(table, value, isSelected, false);
|
||||
cell.update(table, value, false);
|
||||
|
||||
cell.setForeground(Color.BLACK);
|
||||
if (row % 2 == 0) {
|
||||
cell.setBackground(new Color(236, 241, 241)); // #ecf1f1
|
||||
} else {
|
||||
|
@ -39,7 +39,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ContributionIndexTableModel
|
||||
@ -50,10 +49,8 @@ public class ContributionIndexTableModel
|
||||
private final Class<?>[] columnTypes = { ContributedPlatform.class };
|
||||
|
||||
public void updateIndexFilter(String[] filters,
|
||||
Stream<Predicate<ContributedPlatform>> additionalFilters) {
|
||||
Predicate<ContributedPlatform> filter) {
|
||||
contributions.clear();
|
||||
Predicate<ContributedPlatform> filter = additionalFilters
|
||||
.reduce(Predicate::and).get();
|
||||
for (ContributedPackage pack : BaseNoGui.indexer.getPackages()) {
|
||||
for (ContributedPlatform platform : pack.getPlatforms()) {
|
||||
String compoundTargetSearchText = platform.getName() + "\n"
|
||||
|
@ -72,7 +72,7 @@ public class ContributionManagerUI extends InstallerJDialog {
|
||||
@Override
|
||||
protected void onInstall(ContributedPlatform selected,
|
||||
ContributedPlatform installed) {
|
||||
if (selected.isReadOnly()) {
|
||||
if (selected.isBuiltIn()) {
|
||||
onRemovePressed(installed, false);
|
||||
} else {
|
||||
onInstallPressed(selected, installed);
|
||||
@ -98,14 +98,13 @@ public class ContributionManagerUI extends InstallerJDialog {
|
||||
|
||||
categoryChooser.removeActionListener(categoryChooserActionListener);
|
||||
|
||||
categoryFilter = null;
|
||||
categoryChooser.removeAllItems();
|
||||
|
||||
filterField.setEnabled(getContribModel().getRowCount() > 0);
|
||||
|
||||
categoryChooser.addActionListener(categoryChooserActionListener);
|
||||
|
||||
// Enable categories combo only if there are two or more choices
|
||||
categoryFilter = x -> true;
|
||||
categoryChooser.removeAllItems();
|
||||
categoryChooser.addItem(new DropdownAllCoresItem());
|
||||
categoryChooser.addItem(new DropdownUpdatableCoresItem());
|
||||
Collection<String> categories = BaseNoGui.indexer.getCategories();
|
||||
@ -167,7 +166,7 @@ public class ContributionManagerUI extends InstallerJDialog {
|
||||
List<String> errors = new LinkedList<>();
|
||||
try {
|
||||
setProgressVisible(true, tr("Installing..."));
|
||||
if (platformToRemove != null && !platformToRemove.isReadOnly()) {
|
||||
if (platformToRemove != null && !platformToRemove.isBuiltIn()) {
|
||||
errors.addAll(installer.remove(platformToRemove));
|
||||
}
|
||||
errors.addAll(installer.install(platformToInstall, this::setProgress));
|
||||
|
@ -29,7 +29,6 @@
|
||||
|
||||
package cc.arduino.contributions.packages.ui;
|
||||
|
||||
import cc.arduino.contributions.filters.NoopPredicate;
|
||||
import cc.arduino.contributions.packages.ContributedPlatform;
|
||||
import cc.arduino.contributions.ui.DropdownItem;
|
||||
|
||||
@ -45,11 +44,7 @@ public class DropdownAllCoresItem implements DropdownItem<ContributedPlatform> {
|
||||
|
||||
@Override
|
||||
public Predicate<ContributedPlatform> getFilterPredicate() {
|
||||
return new NoopPredicate<>();
|
||||
return x -> true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DropdownAllCoresItem;
|
||||
}
|
||||
}
|
||||
|
@ -54,9 +54,4 @@ public class DropdownCoreOfCategoryItem implements DropdownItem<ContributedPlatf
|
||||
return new CategoryPredicate(category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DropdownCoreOfCategoryItem && ((DropdownCoreOfCategoryItem) obj).category.equals(category);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,8 +49,4 @@ public class DropdownUpdatableCoresItem implements DropdownItem<ContributedPlatf
|
||||
return tr("Updatable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DropdownUpdatableCoresItem;
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ public class FilterJTextField extends JTextField {
|
||||
private final String filterHint;
|
||||
|
||||
private boolean showingHint;
|
||||
private Timer timer;
|
||||
|
||||
public FilterJTextField(String hint) {
|
||||
super(hint);
|
||||
@ -48,6 +49,10 @@ public class FilterJTextField extends JTextField {
|
||||
|
||||
showingHint = true;
|
||||
updateStyle();
|
||||
timer = new Timer(1000, e -> {
|
||||
applyFilter();
|
||||
timer.stop();
|
||||
});
|
||||
|
||||
addFocusListener(new FocusListener() {
|
||||
public void focusLost(FocusEvent focusEvent) {
|
||||
@ -68,33 +73,40 @@ public class FilterJTextField extends JTextField {
|
||||
|
||||
getDocument().addDocumentListener(new DocumentListener() {
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
applyFilter();
|
||||
spawnTimer();
|
||||
}
|
||||
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
applyFilter();
|
||||
spawnTimer();
|
||||
}
|
||||
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
applyFilter();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
addActionListener(e -> {
|
||||
if (timer.isRunning()) {
|
||||
timer.stop();
|
||||
}
|
||||
applyFilter();
|
||||
});
|
||||
}
|
||||
|
||||
private String lastFilter = "";
|
||||
private void spawnTimer() {
|
||||
if (timer.isRunning()) {
|
||||
timer.stop();
|
||||
}
|
||||
timer.start();
|
||||
}
|
||||
|
||||
private void applyFilter() {
|
||||
public void applyFilter() {
|
||||
String filter = showingHint ? "" : getText();
|
||||
filter = filter.toLowerCase();
|
||||
|
||||
// Replace anything but 0-9, a-z, or : with a space
|
||||
filter = filter.replaceAll("[^\\x30-\\x39^\\x61-\\x7a^\\x3a]", " ");
|
||||
|
||||
// Fire event only if the filter is changed
|
||||
if (filter.equals(lastFilter))
|
||||
return;
|
||||
|
||||
lastFilter = filter;
|
||||
onFilter(filter.split(" "));
|
||||
}
|
||||
|
||||
@ -112,4 +124,23 @@ public class FilterJTextField extends JTextField {
|
||||
setFont(getFont().deriveFont(Font.PLAIN));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paste() {
|
||||
|
||||
// Same precondition check as JTextComponent#paste().
|
||||
if (!isEditable() || !isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable hint to prevent the focus handler from clearing the pasted text.
|
||||
if (showingHint) {
|
||||
showingHint = false;
|
||||
setText("");
|
||||
updateStyle();
|
||||
}
|
||||
|
||||
// Perform the paste.
|
||||
super.paste();
|
||||
}
|
||||
}
|
||||
|
@ -37,11 +37,10 @@ import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class FilteredAbstractTableModel<T> extends AbstractTableModel {
|
||||
|
||||
abstract public void updateIndexFilter(String[] filters, Stream<Predicate<T>> additionalFilters);
|
||||
abstract public void updateIndexFilter(String[] filters, Predicate<T> additionalFilter);
|
||||
|
||||
public static <T extends DownloadableContribution> T getLatestOf(List<T> contribs) {
|
||||
contribs = new LinkedList<>(contribs);
|
||||
|
@ -43,9 +43,12 @@ import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
@ -53,6 +56,7 @@ import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
@ -63,6 +67,7 @@ import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
|
||||
import cc.arduino.contributions.ui.listeners.AbstractKeyListener;
|
||||
import processing.app.Base;
|
||||
@ -88,6 +93,8 @@ public abstract class InstallerJDialog<T> extends JDialog {
|
||||
private final JButton closeButton;
|
||||
private final JButton dismissErrorMessageButton;
|
||||
|
||||
protected int previousRowAtPoint = -1;
|
||||
|
||||
abstract protected FilteredAbstractTableModel<T> createContribModel();
|
||||
|
||||
abstract protected TableCellRenderer createCellRenderer();
|
||||
@ -118,17 +125,40 @@ public abstract class InstallerJDialog<T> extends JDialog {
|
||||
filterField = new FilterJTextField(tr("Filter your search...")) {
|
||||
@Override
|
||||
protected void onFilter(String[] _filters) {
|
||||
previousRowAtPoint = -1;
|
||||
filters = _filters;
|
||||
if (contribTable.getCellEditor() != null) {
|
||||
contribTable.getCellEditor().stopCellEditing();
|
||||
}
|
||||
updateIndexFilter(filters, categoryFilter);
|
||||
if (contribModel.getRowCount() == 1) {
|
||||
// TODO: understand why it doesn't work
|
||||
//contribTable.addRowSelectionInterval(0, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
filterField.getAccessibleContext().setAccessibleDescription(tr("Search Filter"));
|
||||
|
||||
// Add cut/copy/paste contextual menu to the search filter input field.
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
Action cut = new DefaultEditorKit.CutAction();
|
||||
cut.putValue(Action.NAME, tr("Cut"));
|
||||
menu.add(cut);
|
||||
|
||||
Action copy = new DefaultEditorKit.CopyAction();
|
||||
copy.putValue(Action.NAME, tr("Copy"));
|
||||
menu.add(copy);
|
||||
|
||||
Action paste = new DefaultEditorKit.PasteAction();
|
||||
paste.putValue(Action.NAME, tr("Paste"));
|
||||
menu.add(paste);
|
||||
|
||||
filterField.setComponentPopupMenu(menu);
|
||||
|
||||
// Focus the filter field when the window opens.
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
filterField.requestFocus();
|
||||
}
|
||||
});
|
||||
|
||||
filtersContainer = new JPanel();
|
||||
filtersContainer.setLayout(new BoxLayout(filtersContainer, BoxLayout.X_AXIS));
|
||||
@ -150,7 +180,6 @@ public abstract class InstallerJDialog<T> extends JDialog {
|
||||
contribTable.setDragEnabled(false);
|
||||
contribTable.setIntercellSpacing(new Dimension(0, 1));
|
||||
contribTable.setShowVerticalLines(false);
|
||||
contribTable.setSelectionBackground(Theme.getColor("status.notice.bgcolor"));
|
||||
contribTable.addKeyListener(new AbstractKeyListener() {
|
||||
|
||||
@Override
|
||||
@ -167,6 +196,25 @@ public abstract class InstallerJDialog<T> extends JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
contribTable.addMouseMotionListener(new MouseMotionListener() {
|
||||
|
||||
public void mouseDragged(MouseEvent e) {}
|
||||
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
// avoid firing edits events until the mouse changes cell or the user is back on the cell after selecting a dropdown
|
||||
int rowAtPoint = contribTable.rowAtPoint(e.getPoint());
|
||||
if (!InstallerTableCell.isDropdownSelected() && rowAtPoint != previousRowAtPoint) {
|
||||
contribTable.editCellAt(rowAtPoint, 0);
|
||||
previousRowAtPoint = rowAtPoint;
|
||||
InstallerTableCell.dropdownSelected(false);
|
||||
}
|
||||
if (InstallerTableCell.isDropdownSelected() && rowAtPoint == previousRowAtPoint) {
|
||||
// back to the original cell, can drop dropdown selector lock
|
||||
InstallerTableCell.dropdownSelected(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
TableColumnModel tcm = contribTable.getColumnModel();
|
||||
TableColumn col = tcm.getColumn(0);
|
||||
@ -241,9 +289,8 @@ public abstract class InstallerJDialog<T> extends JDialog {
|
||||
SwingUtilities.invokeLater(InstallerJDialog.this::onUpdatePressed);
|
||||
}
|
||||
|
||||
public void updateIndexFilter(String[] filters, Predicate<T>... additionalFilters) {
|
||||
Stream<Predicate<T>> notNullAdditionalFilters = Stream.of(additionalFilters).filter(filter -> filter != null);
|
||||
contribModel.updateIndexFilter(filters, notNullAdditionalFilters);
|
||||
public void updateIndexFilter(String[] filters, Predicate<T> additionalFilter) {
|
||||
contribModel.updateIndexFilter(filters, additionalFilter);
|
||||
}
|
||||
|
||||
public void setErrorMessage(String message) {
|
||||
@ -286,7 +333,8 @@ public abstract class InstallerJDialog<T> extends JDialog {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
DropdownItem<T> selected = (DropdownItem<T>) categoryChooser.getSelectedItem();
|
||||
if (categoryFilter == null || !categoryFilter.equals(selected)) {
|
||||
previousRowAtPoint = -1;
|
||||
if (selected != null && categoryFilter != selected.getFilterPredicate()) {
|
||||
categoryFilter = selected.getFilterPredicate();
|
||||
if (contribTable.getCellEditor() != null) {
|
||||
contribTable.getCellEditor().stopCellEditing();
|
||||
@ -301,6 +349,7 @@ public abstract class InstallerJDialog<T> extends JDialog {
|
||||
listener.focusGained(new FocusEvent(filterField, FocusEvent.FOCUS_GAINED));
|
||||
}
|
||||
filterField.setText(filterText);
|
||||
filterField.applyFilter();
|
||||
}
|
||||
|
||||
public void selectDropdownItemByClassName(String dropdownItem) {
|
||||
|
@ -41,6 +41,16 @@ public abstract class InstallerTableCell extends AbstractCellEditor implements T
|
||||
|
||||
abstract public void setEnabled(boolean b);
|
||||
|
||||
private static boolean dropdownSelected = false;
|
||||
|
||||
public static boolean isDropdownSelected( ) {
|
||||
return dropdownSelected;
|
||||
}
|
||||
|
||||
public static void dropdownSelected(boolean b) {
|
||||
dropdownSelected = b;
|
||||
}
|
||||
|
||||
abstract public void setStatus(String s);
|
||||
|
||||
public static void setJTextPaneDimensionToFitContainedText(JTextPane jTextPane, int width) {
|
||||
|
@ -38,8 +38,13 @@ public class ProgressJProgressBar extends JProgressBar {
|
||||
|
||||
public void setValue(Progress p) {
|
||||
setValue((int) p.getProgress());
|
||||
if (p.getStatus() != null)
|
||||
if (p.getStatus() != null) {
|
||||
setString(p.getStatus());
|
||||
// copy status to accessibility context for screen readers to use
|
||||
getAccessibleContext().setAccessibleDescription(p.getStatus());
|
||||
// make status focusable so screen readers can get to it
|
||||
setFocusable(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,12 +36,7 @@ import java.awt.FlowLayout;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Image;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.*;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
@ -55,22 +50,46 @@ import javax.swing.border.LineBorder;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
|
||||
import cc.arduino.Constants;
|
||||
import processing.app.PreferencesData;
|
||||
import processing.app.Theme;
|
||||
|
||||
public class NotificationPopup extends JDialog {
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
public class NotificationPopup extends JDialog {
|
||||
private Timer autoCloseTimer = new Timer(false);
|
||||
private boolean autoClose = true;
|
||||
private OptionalButtonCallbacks optionalButtonCallbacks;
|
||||
|
||||
public interface OptionalButtonCallbacks {
|
||||
void onOptionalButton1Callback();
|
||||
void onOptionalButton2Callback();
|
||||
}
|
||||
|
||||
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
|
||||
String message) {
|
||||
this(parent, hyperlinkListener, message, true);
|
||||
this(parent, hyperlinkListener, message, true, null, null, null);
|
||||
}
|
||||
|
||||
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
|
||||
String message, boolean _autoClose) {
|
||||
this(parent, hyperlinkListener, message, _autoClose, null, null, null);
|
||||
}
|
||||
|
||||
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
|
||||
String message, boolean _autoClose, OptionalButtonCallbacks listener, String button1Name, String button2Name) {
|
||||
super(parent, false);
|
||||
|
||||
if (!PreferencesData.getBoolean("ide.accessible")) {
|
||||
// often auto-close is too fast for users of screen readers, so don't allow it.
|
||||
autoClose = _autoClose;
|
||||
}
|
||||
else {
|
||||
autoClose = false;
|
||||
}
|
||||
optionalButtonCallbacks = listener;
|
||||
|
||||
setLayout(new FlowLayout());
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setUndecorated(true);
|
||||
@ -90,6 +109,74 @@ public class NotificationPopup extends JDialog {
|
||||
text.addHyperlinkListener(hyperlinkListener);
|
||||
add(text);
|
||||
|
||||
if (button1Name != null) {
|
||||
JButton optionalButton1 = new JButton(tr(button1Name));
|
||||
MouseAdapter button1Action = new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (optionalButtonCallbacks != null) {
|
||||
optionalButtonCallbacks.onOptionalButton1Callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
optionalButton1.addMouseListener(button1Action);
|
||||
|
||||
KeyListener button1Key = new KeyListener() {
|
||||
// Ignore when the key is typed - only act once the key is released
|
||||
public void keyTyped(KeyEvent e) {
|
||||
// do nothing here, wait until the key is released
|
||||
}
|
||||
|
||||
// Ignore when the key is pressed - only act once the key is released
|
||||
public void keyPressed(KeyEvent e) {
|
||||
// do nothing here, wait until the key is released
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
int key = e.getKeyCode();
|
||||
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
|
||||
optionalButtonCallbacks.onOptionalButton1Callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
optionalButton1.addKeyListener(button1Key);
|
||||
add(optionalButton1);
|
||||
}
|
||||
|
||||
if (button2Name != null) {
|
||||
JButton optionalButton2 = new JButton(tr(button2Name));
|
||||
MouseAdapter button2Action = new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (optionalButtonCallbacks != null) {
|
||||
optionalButtonCallbacks.onOptionalButton2Callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
optionalButton2.addMouseListener(button2Action);
|
||||
|
||||
KeyListener button2Key = new KeyListener() {
|
||||
// Ignore when the key is typed - only act once the key is released
|
||||
public void keyTyped(KeyEvent e) {
|
||||
// do nothing here, wait until the key is released
|
||||
}
|
||||
|
||||
// Ignore when the key is pressed - only act once the key is released
|
||||
public void keyPressed(KeyEvent e) {
|
||||
// do nothing here, wait until the key is released
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
int key = e.getKeyCode();
|
||||
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
|
||||
optionalButtonCallbacks.onOptionalButton2Callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
optionalButton2.addKeyListener(button2Key);
|
||||
add(optionalButton2);
|
||||
}
|
||||
|
||||
Image close = Theme.getThemeImage("close", this, scale(22), scale(22));
|
||||
JButton closeButton = new JButton(new ImageIcon(close));
|
||||
closeButton.setBorder(null);
|
||||
@ -97,6 +184,26 @@ public class NotificationPopup extends JDialog {
|
||||
closeButton.setHideActionText(true);
|
||||
closeButton.setOpaque(false);
|
||||
closeButton.setBackground(new Color(0, 0, 0, 0));
|
||||
closeButton.getAccessibleContext().setAccessibleDescription(tr("Close"));
|
||||
KeyListener closeKey = new KeyListener() {
|
||||
// Ignore when the key is typed - only act once the key is released
|
||||
public void keyTyped(KeyEvent e) {
|
||||
// do nothing here, wait until the key is released
|
||||
}
|
||||
|
||||
// Ignore when the key is pressed - only act once the key is released
|
||||
public void keyPressed(KeyEvent e) {
|
||||
// do nothing here, wait until the key is released
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
int key = e.getKeyCode();
|
||||
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
};
|
||||
closeButton.addKeyListener(closeKey);
|
||||
add(closeButton);
|
||||
|
||||
MouseAdapter closeOnClick = new MouseAdapter() {
|
||||
@ -145,6 +252,7 @@ public class NotificationPopup extends JDialog {
|
||||
if (autoClose) {
|
||||
autoCloseTimer.cancel();
|
||||
}
|
||||
setModal(false);
|
||||
dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
|
||||
}
|
||||
|
||||
@ -158,5 +266,9 @@ public class NotificationPopup extends JDialog {
|
||||
}, Constants.NOTIFICATION_POPUP_AUTOCLOSE_DELAY);
|
||||
}
|
||||
setVisible(true);
|
||||
if (PreferencesData.getBoolean("ide.accessible")) {
|
||||
requestFocus();
|
||||
setModal(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ import processing.app.helpers.OSUtils;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -161,6 +164,22 @@ public class FindReplace extends javax.swing.JFrame {
|
||||
|
||||
searchAllFilesBox.setText(tr("Search all Sketch Tabs"));
|
||||
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
Action cut = new DefaultEditorKit.CutAction();
|
||||
cut.putValue(Action.NAME, tr("Cut"));
|
||||
menu.add( cut );
|
||||
|
||||
Action copy = new DefaultEditorKit.CopyAction();
|
||||
copy.putValue(Action.NAME, tr("Copy"));
|
||||
menu.add( copy );
|
||||
|
||||
Action paste = new DefaultEditorKit.PasteAction();
|
||||
paste.putValue(Action.NAME, tr("Paste"));
|
||||
menu.add( paste );
|
||||
|
||||
findField.setComponentPopupMenu( menu );
|
||||
replaceField.setComponentPopupMenu( menu );
|
||||
|
||||
findButton.setText(tr("Find"));
|
||||
findButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
|
@ -38,6 +38,7 @@ import processing.app.Editor;
|
||||
import processing.app.I18n;
|
||||
import processing.app.PreferencesData;
|
||||
import processing.app.Theme;
|
||||
import processing.app.Theme.ZippedTheme;
|
||||
import processing.app.helpers.FileUtils;
|
||||
import processing.app.legacy.PApplet;
|
||||
|
||||
@ -46,6 +47,7 @@ import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
@ -89,7 +91,7 @@ public class Preferences extends javax.swing.JDialog {
|
||||
|
||||
Base.registerWindowCloseKeys(getRootPane(), this::cancelButtonActionPerformed);
|
||||
|
||||
showPrerefencesData();
|
||||
showPreferencesData();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,10 +132,9 @@ public class Preferences extends javax.swing.JDialog {
|
||||
enableCodeFoldingBox = new javax.swing.JCheckBox();
|
||||
verifyUploadBox = new javax.swing.JCheckBox();
|
||||
externalEditorBox = new javax.swing.JCheckBox();
|
||||
cacheCompiledCore = new javax.swing.JCheckBox();
|
||||
checkUpdatesBox = new javax.swing.JCheckBox();
|
||||
updateExtensionBox = new javax.swing.JCheckBox();
|
||||
saveVerifyUploadBox = new javax.swing.JCheckBox();
|
||||
accessibleIDEBox = new javax.swing.JCheckBox();
|
||||
jLabel1 = new javax.swing.JLabel();
|
||||
jLabel2 = new javax.swing.JLabel();
|
||||
scaleSpinner = new javax.swing.JSpinner();
|
||||
@ -159,6 +160,9 @@ public class Preferences extends javax.swing.JDialog {
|
||||
autoProxyUsername = new javax.swing.JTextField();
|
||||
autoProxyPassword = new javax.swing.JPasswordField();
|
||||
autoProxyPasswordLabel = new javax.swing.JLabel();
|
||||
comboThemeLabel = new javax.swing.JLabel();
|
||||
comboTheme = new JComboBox();
|
||||
requiresRestartLabel2 = new javax.swing.JLabel();
|
||||
javax.swing.JPanel jPanel3 = new javax.swing.JPanel();
|
||||
javax.swing.JButton okButton = new javax.swing.JButton();
|
||||
javax.swing.JButton cancelButton = new javax.swing.JButton();
|
||||
@ -243,7 +247,7 @@ public class Preferences extends javax.swing.JDialog {
|
||||
arduinoNotRunningLabel.setForeground(Color.GRAY);
|
||||
arduinoNotRunningLabel.setText(tr("(edit only when Arduino is not running)"));
|
||||
|
||||
checkboxesContainer.setLayout(new javax.swing.BoxLayout(checkboxesContainer, javax.swing.BoxLayout.Y_AXIS));
|
||||
checkboxesContainer.setLayout(new GridLayout(0,2));
|
||||
|
||||
displayLineNumbersBox.setText(tr("Display line numbers"));
|
||||
checkboxesContainer.add(displayLineNumbersBox);
|
||||
@ -272,18 +276,15 @@ public class Preferences extends javax.swing.JDialog {
|
||||
|
||||
checkboxesContainer.add(externalEditorBox);
|
||||
|
||||
cacheCompiledCore.setText(tr("Aggressively cache compiled core"));
|
||||
checkboxesContainer.add(cacheCompiledCore);
|
||||
|
||||
checkUpdatesBox.setText(tr("Check for updates on startup"));
|
||||
checkboxesContainer.add(checkUpdatesBox);
|
||||
|
||||
updateExtensionBox.setText(tr("Update sketch files to new extension on save (.pde -> .ino)"));
|
||||
checkboxesContainer.add(updateExtensionBox);
|
||||
|
||||
saveVerifyUploadBox.setText(tr("Save when verifying or uploading"));
|
||||
checkboxesContainer.add(saveVerifyUploadBox);
|
||||
|
||||
accessibleIDEBox.setText(tr("Use accessibility features"));
|
||||
checkboxesContainer.add(accessibleIDEBox);
|
||||
|
||||
jLabel1.setText(tr("Interface scale:"));
|
||||
|
||||
jLabel2.setText(tr(" (requires restart of Arduino)"));
|
||||
@ -303,6 +304,12 @@ public class Preferences extends javax.swing.JDialog {
|
||||
|
||||
jLabel3.setText("%");
|
||||
|
||||
comboThemeLabel.setText(tr("Theme: "));
|
||||
|
||||
comboTheme.getAccessibleContext().setAccessibleName("Theme (requires restart of Arduino)");
|
||||
|
||||
requiresRestartLabel2.setText(tr(" (requires restart of Arduino)"));
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
@ -311,7 +318,7 @@ public class Preferences extends javax.swing.JDialog {
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(sketchbookLocationField, javax.swing.GroupLayout.DEFAULT_SIZE, 553, Short.MAX_VALUE)
|
||||
.addComponent(sketchbookLocationField, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(browseButton))
|
||||
.addComponent(checkboxesContainer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
@ -341,9 +348,14 @@ public class Preferences extends javax.swing.JDialog {
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(comboLanguageLabel)
|
||||
.addComponent(fontSizeLabel))
|
||||
.addComponent(fontSizeLabel)
|
||||
.addComponent(comboThemeLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(comboTheme, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(requiresRestartLabel2))
|
||||
.addComponent(fontSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(comboLanguage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
@ -363,7 +375,7 @@ public class Preferences extends javax.swing.JDialog {
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {comboLanguageLabel, comboWarningsLabel, fontSizeLabel, jLabel1, showVerboseLabel});
|
||||
jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {comboLanguageLabel, comboWarningsLabel, fontSizeLabel, jLabel1, showVerboseLabel, comboThemeLabel});
|
||||
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@ -391,6 +403,11 @@ public class Preferences extends javax.swing.JDialog {
|
||||
.addComponent(autoScaleCheckBox)
|
||||
.addComponent(jLabel3))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(comboThemeLabel)
|
||||
.addComponent(comboTheme, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(requiresRestartLabel2))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(showVerboseLabel)
|
||||
.addComponent(verboseCompilationBox)
|
||||
@ -610,13 +627,13 @@ public class Preferences extends javax.swing.JDialog {
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 691, Short.MAX_VALUE)
|
||||
.addGap(0, 800, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 580, Short.MAX_VALUE)
|
||||
.addGap(0, 400, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
@ -700,6 +717,7 @@ public class Preferences extends javax.swing.JDialog {
|
||||
private javax.swing.JCheckBox autoScaleCheckBox;
|
||||
private javax.swing.JButton browseButton;
|
||||
private javax.swing.JCheckBox checkUpdatesBox;
|
||||
private javax.swing.JCheckBox accessibleIDEBox;
|
||||
private javax.swing.JPanel checkboxesContainer;
|
||||
private javax.swing.JComboBox comboLanguage;
|
||||
private javax.swing.JLabel comboLanguageLabel;
|
||||
@ -709,7 +727,6 @@ public class Preferences extends javax.swing.JDialog {
|
||||
private javax.swing.JCheckBox enableCodeFoldingBox;
|
||||
private javax.swing.JButton extendedAdditionalUrlFieldWindow;
|
||||
private javax.swing.JCheckBox externalEditorBox;
|
||||
private javax.swing.JCheckBox cacheCompiledCore;
|
||||
private javax.swing.JTextField fontSizeField;
|
||||
private javax.swing.JLabel fontSizeLabel;
|
||||
private javax.swing.JLabel jLabel1;
|
||||
@ -738,10 +755,12 @@ public class Preferences extends javax.swing.JDialog {
|
||||
private javax.swing.JLabel showVerboseLabel;
|
||||
private javax.swing.JTextField sketchbookLocationField;
|
||||
private javax.swing.JLabel sketchbookLocationLabel;
|
||||
private javax.swing.JCheckBox updateExtensionBox;
|
||||
private javax.swing.JCheckBox verboseCompilationBox;
|
||||
private javax.swing.JCheckBox verboseUploadBox;
|
||||
private javax.swing.JCheckBox verifyUploadBox;
|
||||
private javax.swing.JComboBox comboTheme;
|
||||
private javax.swing.JLabel comboThemeLabel;
|
||||
private javax.swing.JLabel requiresRestartLabel2;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
private java.util.List<String> validateData() {
|
||||
@ -770,6 +789,12 @@ public class Preferences extends javax.swing.JDialog {
|
||||
Language newLanguage = (Language) comboLanguage.getSelectedItem();
|
||||
PreferencesData.set("editor.languages.current", newLanguage.getIsoCode());
|
||||
|
||||
if (comboTheme.getSelectedIndex() == 0) {
|
||||
PreferencesData.set("theme.file", "");
|
||||
} else {
|
||||
PreferencesData.set("theme.file", ((ZippedTheme) comboTheme.getSelectedItem()).getKey());
|
||||
}
|
||||
|
||||
String newSizeText = fontSizeField.getText();
|
||||
try {
|
||||
int newSize = Integer.parseInt(newSizeText.trim());
|
||||
@ -804,13 +829,9 @@ public class Preferences extends javax.swing.JDialog {
|
||||
|
||||
PreferencesData.setBoolean("editor.external", externalEditorBox.isSelected());
|
||||
|
||||
PreferencesData.setBoolean("compiler.cache_core", cacheCompiledCore.isSelected());
|
||||
|
||||
PreferencesData.setBoolean("update.check", checkUpdatesBox.isSelected());
|
||||
|
||||
PreferencesData.setBoolean("editor.update_extension", updateExtensionBox.isSelected());
|
||||
|
||||
PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected());
|
||||
PreferencesData.setBoolean("ide.accessible", accessibleIDEBox.isSelected());
|
||||
|
||||
PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ","));
|
||||
|
||||
@ -819,13 +840,17 @@ public class Preferences extends javax.swing.JDialog {
|
||||
PreferencesData.set(Constants.PREF_PROXY_MANUAL_TYPE, manualProxyTypeButtonGroup.getSelection().getActionCommand());
|
||||
PreferencesData.set(Constants.PREF_PROXY_MANUAL_HOSTNAME, manualProxyHostName.getText());
|
||||
PreferencesData.set(Constants.PREF_PROXY_MANUAL_PORT, manualProxyPort.getText());
|
||||
PreferencesData.set(Constants.PREF_PROXY_MANUAL_USERNAME, manualProxyUsername.getText());
|
||||
PreferencesData.set(Constants.PREF_PROXY_MANUAL_PASSWORD, String.valueOf(manualProxyPassword.getPassword()));
|
||||
PreferencesData.set(Constants.PREF_PROXY_AUTO_USERNAME, autoProxyUsername.getText());
|
||||
PreferencesData.set(Constants.PREF_PROXY_AUTO_PASSWORD, String.valueOf(autoProxyPassword.getPassword()));
|
||||
if (PreferencesData.get(Constants.PREF_PROXY_TYPE).equals(Constants.PROXY_TYPE_MANUAL)) {
|
||||
PreferencesData.set(Constants.PREF_PROXY_USERNAME, manualProxyUsername.getText());
|
||||
PreferencesData.set(Constants.PREF_PROXY_PASSWORD, String.valueOf(manualProxyPassword.getPassword()));
|
||||
}
|
||||
if (PreferencesData.get(Constants.PREF_PROXY_TYPE).equals(Constants.PROXY_TYPE_AUTO)) {
|
||||
PreferencesData.set(Constants.PREF_PROXY_USERNAME, autoProxyUsername.getText());
|
||||
PreferencesData.set(Constants.PREF_PROXY_PASSWORD, String.valueOf(autoProxyPassword.getPassword()));
|
||||
}
|
||||
}
|
||||
|
||||
private void showPrerefencesData() {
|
||||
private void showPreferencesData() {
|
||||
sketchbookLocationField.setText(PreferencesData.get("sketchbook.path"));
|
||||
|
||||
String currentLanguageISOCode = PreferencesData.get("editor.languages.current");
|
||||
@ -835,6 +860,16 @@ public class Preferences extends javax.swing.JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
String selectedTheme = PreferencesData.get("theme.file", "");
|
||||
Collection<ZippedTheme> availablethemes = Theme.getAvailablethemes();
|
||||
comboTheme.addItem(tr("Default theme"));
|
||||
for (ZippedTheme theme : availablethemes) {
|
||||
comboTheme.addItem(theme);
|
||||
if (theme.getKey().equals(selectedTheme)) {
|
||||
comboTheme.setSelectedItem(theme);
|
||||
}
|
||||
}
|
||||
|
||||
Font editorFont = PreferencesData.getFont("editor.font");
|
||||
fontSizeField.setText(String.valueOf(editorFont.getSize()));
|
||||
|
||||
@ -866,11 +901,17 @@ public class Preferences extends javax.swing.JDialog {
|
||||
|
||||
externalEditorBox.setSelected(PreferencesData.getBoolean("editor.external"));
|
||||
|
||||
cacheCompiledCore.setSelected(PreferencesData.get("compiler.cache_core") == null || PreferencesData.getBoolean("compiler.cache_core"));
|
||||
if (PreferencesData.get("compiler.cache_core") == null) {
|
||||
PreferencesData.setBoolean("compiler.cache_core", true);
|
||||
}
|
||||
|
||||
checkUpdatesBox.setSelected(PreferencesData.getBoolean("update.check"));
|
||||
|
||||
updateExtensionBox.setSelected(PreferencesData.get("editor.update_extension") == null || PreferencesData.getBoolean("editor.update_extension"));
|
||||
if (PreferencesData.get("editor.update_extension") == null) {
|
||||
PreferencesData.setBoolean("editor.update_extension", true);
|
||||
}
|
||||
|
||||
accessibleIDEBox.setSelected(PreferencesData.getBoolean("ide.accessible"));
|
||||
|
||||
saveVerifyUploadBox.setSelected(PreferencesData.getBoolean("editor.save_on_verify"));
|
||||
|
||||
@ -887,16 +928,16 @@ public class Preferences extends javax.swing.JDialog {
|
||||
if (!PreferencesData.get(Constants.PREF_PROXY_PAC_URL, "").isEmpty()) {
|
||||
autoProxyUsePAC.setSelected(true);
|
||||
autoProxyPACURL.setText(PreferencesData.get(Constants.PREF_PROXY_PAC_URL));
|
||||
autoProxyUsername.setText(PreferencesData.get(Constants.PREF_PROXY_AUTO_USERNAME));
|
||||
autoProxyPassword.setText(PreferencesData.get(Constants.PREF_PROXY_AUTO_PASSWORD));
|
||||
autoProxyUsername.setText(PreferencesData.get(Constants.PREF_PROXY_USERNAME));
|
||||
autoProxyPassword.setText(PreferencesData.get(Constants.PREF_PROXY_PASSWORD));
|
||||
}
|
||||
} else {
|
||||
manualProxy.setSelected(true);
|
||||
manualProxyFieldsSetEnabled(true);
|
||||
manualProxyHostName.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_HOSTNAME));
|
||||
manualProxyPort.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_PORT));
|
||||
manualProxyUsername.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_USERNAME));
|
||||
manualProxyPassword.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_PASSWORD));
|
||||
manualProxyUsername.setText(PreferencesData.get(Constants.PREF_PROXY_USERNAME));
|
||||
manualProxyPassword.setText(PreferencesData.get(Constants.PREF_PROXY_PASSWORD));
|
||||
}
|
||||
|
||||
String selectedManualProxyType = PreferencesData.get(Constants.PREF_PROXY_MANUAL_TYPE, Constants.PROXY_MANUAL_TYPE_HTTP);
|
||||
|
29
app/src/log4j2.xml
Normal file
29
app/src/log4j2.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="warn" name="Arduino" packages="cc.arduino">
|
||||
<Appenders>
|
||||
|
||||
<!-- Console Appender -->
|
||||
<Console name="Console" target="SYSTEM_ERR">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}{UTC} %p %c{1.}:%L [%t] %m%n"/>
|
||||
</Console>
|
||||
|
||||
<!-- Rolling File Appender -->
|
||||
<RollingFile name="RollingFile" fileName="${sys:log4j.dir}/logs/application.log"
|
||||
filePattern="${sys:log4j.dir}/logs/application-%d{MM-dd-yyyy}-%i.log.gz"
|
||||
ignoreExceptions="false">
|
||||
<PatternLayout>
|
||||
<Pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}{UTC} %p %c{1.}:%L [%t] %m%n</Pattern>
|
||||
</PatternLayout>
|
||||
<Policies>
|
||||
<SizeBasedTriggeringPolicy size="50 MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="20"/>
|
||||
</RollingFile>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="debug">
|
||||
<AppenderRef ref="Console" level="info" />
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
@ -17,6 +17,7 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
|
||||
|
||||
private StringBuffer updateBuffer;
|
||||
private Timer updateTimer;
|
||||
private Timer portExistsTimer;
|
||||
|
||||
private BoardPort boardPort;
|
||||
|
||||
@ -27,6 +28,7 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
|
||||
this.boardPort = boardPort;
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent event) {
|
||||
try {
|
||||
closed = true;
|
||||
@ -41,6 +43,7 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
|
||||
KeyStroke wc = Editor.WINDOW_CLOSE_KEYSTROKE;
|
||||
getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(wc, "close");
|
||||
getRootPane().getActionMap().put("close", (new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
try {
|
||||
close();
|
||||
@ -71,6 +74,26 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
|
||||
updateTimer = new Timer(33, this); // redraw serial monitor at 30 Hz
|
||||
updateTimer.start();
|
||||
|
||||
ActionListener portExists = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
try {
|
||||
if (Base.getDiscoveryManager().find(boardPort.getAddress()) == null) {
|
||||
if (!closed) {
|
||||
suspend();
|
||||
}
|
||||
} else {
|
||||
if (closed && !Editor.isUploading()) {
|
||||
resume(boardPort);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
};
|
||||
|
||||
portExistsTimer = new Timer(1000, portExists); // check if the port is still there every second
|
||||
portExistsTimer.start();
|
||||
|
||||
closed = false;
|
||||
}
|
||||
|
||||
@ -90,6 +113,11 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
|
||||
close();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
portExistsTimer.stop();
|
||||
}
|
||||
|
||||
public void resume(BoardPort boardPort) throws Exception {
|
||||
setBoardPort(boardPort);
|
||||
|
||||
@ -165,6 +193,7 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String s = consumeUpdateBuffer();
|
||||
if (s.isEmpty()) {
|
||||
@ -173,4 +202,13 @@ public abstract class AbstractMonitor extends JFrame implements ActionListener {
|
||||
message(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and apply new values from the preferences, either because
|
||||
* the app is just starting up, or the user just finished messing
|
||||
* with things in the Preferences window.
|
||||
*/
|
||||
public void applyPreferences() {
|
||||
// Empty.
|
||||
};
|
||||
}
|
||||
|
@ -3,14 +3,21 @@ package processing.app;
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseWheelListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
@ -18,11 +25,13 @@ import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
|
||||
import cc.arduino.packages.BoardPort;
|
||||
|
||||
@ -36,25 +45,36 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
|
||||
protected JButton sendButton;
|
||||
protected JButton clearButton;
|
||||
protected JCheckBox autoscrollBox;
|
||||
protected JComboBox lineEndings;
|
||||
protected JComboBox serialRates;
|
||||
protected JCheckBox addTimeStampBox;
|
||||
protected JComboBox<String> lineEndings;
|
||||
protected JComboBox<String> serialRates;
|
||||
|
||||
public AbstractTextMonitor(BoardPort boardPort) {
|
||||
super(boardPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addMouseWheelListener(MouseWheelListener l) {
|
||||
super.addMouseWheelListener(l);
|
||||
textArea.addMouseWheelListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addKeyListener(KeyListener l) {
|
||||
super.addKeyListener(l);
|
||||
textArea.addKeyListener(l);
|
||||
textField.addKeyListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreateWindow(Container mainPane) {
|
||||
Font consoleFont = Theme.getFont("console.font");
|
||||
Font editorFont = PreferencesData.getFont("editor.font");
|
||||
Font font = Theme.scale(new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize()));
|
||||
|
||||
mainPane.setLayout(new BorderLayout());
|
||||
|
||||
textArea = new TextAreaFIFO(8000000);
|
||||
textArea = new TextAreaFIFO(8_000_000);
|
||||
textArea.setRows(16);
|
||||
textArea.setColumns(40);
|
||||
textArea.setEditable(false);
|
||||
textArea.setFont(font);
|
||||
|
||||
// don't automatically update the caret. that way we can manually decide
|
||||
// whether or not to do so based on the autoscroll checkbox.
|
||||
@ -71,11 +91,29 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
|
||||
textField = new JTextField(40);
|
||||
// textField is selected every time the window is focused
|
||||
addWindowFocusListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent e) {
|
||||
textField.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// Add cut/copy/paste contextual menu to the text input field.
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
Action cut = new DefaultEditorKit.CutAction();
|
||||
cut.putValue(Action.NAME, tr("Cut"));
|
||||
menu.add(cut);
|
||||
|
||||
Action copy = new DefaultEditorKit.CopyAction();
|
||||
copy.putValue(Action.NAME, tr("Copy"));
|
||||
menu.add(copy);
|
||||
|
||||
Action paste = new DefaultEditorKit.PasteAction();
|
||||
paste.putValue(Action.NAME, tr("Paste"));
|
||||
menu.add(paste);
|
||||
|
||||
textField.setComponentPopupMenu(menu);
|
||||
|
||||
sendButton = new JButton(tr("Send"));
|
||||
clearButton = new JButton(tr("Clear output"));
|
||||
|
||||
@ -90,6 +128,7 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
|
||||
pane.setBorder(new EmptyBorder(4, 4, 4, 4));
|
||||
|
||||
autoscrollBox = new JCheckBox(tr("Autoscroll"), true);
|
||||
addTimeStampBox = new JCheckBox(tr("Show timestamp"), false);
|
||||
|
||||
noLineEndingAlert = new JLabel(I18n.format(tr("You've pressed {0} but nothing was sent. Should you select a line ending?"), tr("Send")));
|
||||
noLineEndingAlert.setToolTipText(noLineEndingAlert.getText());
|
||||
@ -98,19 +137,17 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
|
||||
minimumSize.setSize(minimumSize.getWidth() / 3, minimumSize.getHeight());
|
||||
noLineEndingAlert.setMinimumSize(minimumSize);
|
||||
|
||||
lineEndings = new JComboBox(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
|
||||
lineEndings.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
lineEndings = new JComboBox<>(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
|
||||
lineEndings.addActionListener((ActionEvent event) -> {
|
||||
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
|
||||
noLineEndingAlert.setForeground(pane.getBackground());
|
||||
}
|
||||
});
|
||||
if (PreferencesData.get("serial.line_ending") != null) {
|
||||
lineEndings.setSelectedIndex(PreferencesData.getInteger("serial.line_ending"));
|
||||
}
|
||||
addTimeStampBox.addActionListener((ActionEvent event) ->
|
||||
PreferencesData.setBoolean("serial.show_timestamp", addTimeStampBox.isSelected()));
|
||||
|
||||
lineEndings.setMaximumSize(lineEndings.getMinimumSize());
|
||||
|
||||
serialRates = new JComboBox();
|
||||
serialRates = new JComboBox<>();
|
||||
for (String rate : serialRateStrings) {
|
||||
serialRates.addItem(rate + " " + tr("baud"));
|
||||
}
|
||||
@ -118,6 +155,7 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
|
||||
serialRates.setMaximumSize(serialRates.getMinimumSize());
|
||||
|
||||
pane.add(autoscrollBox);
|
||||
pane.add(addTimeStampBox);
|
||||
pane.add(Box.createHorizontalGlue());
|
||||
pane.add(noLineEndingAlert);
|
||||
pane.add(Box.createRigidArea(new Dimension(8, 0)));
|
||||
@ -127,19 +165,34 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
|
||||
pane.add(Box.createRigidArea(new Dimension(8, 0)));
|
||||
pane.add(clearButton);
|
||||
|
||||
applyPreferences();
|
||||
|
||||
mainPane.add(pane, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
protected void onEnableWindow(boolean enable)
|
||||
{
|
||||
textArea.setEnabled(enable);
|
||||
clearButton.setEnabled(enable);
|
||||
@Override
|
||||
protected void onEnableWindow(boolean enable) {
|
||||
// never actually disable textArea, so people can
|
||||
// still select & copy text, even when the port
|
||||
// is closed or disconnected
|
||||
textArea.setEnabled(true);
|
||||
if (enable) {
|
||||
// setting these to null for system default
|
||||
// gives a wrong gray background on Windows
|
||||
// so assume black text on white background
|
||||
textArea.setForeground(Color.BLACK);
|
||||
textArea.setBackground(Color.WHITE);
|
||||
} else {
|
||||
// In theory, UIManager.getDefaults() should
|
||||
// give us the system's colors for disabled
|
||||
// windows. But it doesn't seem to work. :(
|
||||
textArea.setForeground(new Color(64, 64, 64));
|
||||
textArea.setBackground(new Color(238, 238, 238));
|
||||
}
|
||||
textArea.invalidate();
|
||||
scrollPane.setEnabled(enable);
|
||||
textField.setEnabled(enable);
|
||||
sendButton.setEnabled(enable);
|
||||
autoscrollBox.setEnabled(enable);
|
||||
lineEndings.setEnabled(enable);
|
||||
serialRates.setEnabled(enable);
|
||||
}
|
||||
|
||||
public void onSendCommand(ActionListener listener) {
|
||||
@ -155,14 +208,58 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
|
||||
serialRates.addActionListener(listener);
|
||||
}
|
||||
|
||||
public void message(final String s) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
textArea.append(s);
|
||||
@Override
|
||||
public void message(String msg) {
|
||||
SwingUtilities.invokeLater(() -> updateTextArea(msg));
|
||||
}
|
||||
|
||||
private static final String LINE_SEPARATOR = "\n";
|
||||
private boolean isStartingLine = true;
|
||||
|
||||
protected void updateTextArea(String msg) {
|
||||
if (addTimeStampBox.isSelected()) {
|
||||
textArea.append(addTimestamps(msg));
|
||||
} else {
|
||||
textArea.append(msg);
|
||||
}
|
||||
if (autoscrollBox.isSelected()) {
|
||||
textArea.setCaretPosition(textArea.getDocument().getLength());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public void applyPreferences() {
|
||||
|
||||
// Apply font.
|
||||
Font consoleFont = Theme.getFont("console.font");
|
||||
Font editorFont = PreferencesData.getFont("editor.font");
|
||||
textArea.setFont(Theme.scale(new Font(
|
||||
consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize())));
|
||||
|
||||
// Apply line endings.
|
||||
if (PreferencesData.get("serial.line_ending") != null) {
|
||||
lineEndings.setSelectedIndex(PreferencesData.getInteger("serial.line_ending"));
|
||||
}
|
||||
|
||||
// Apply timestamp visibility.
|
||||
if (PreferencesData.get("serial.show_timestamp") != null) {
|
||||
addTimeStampBox.setSelected(PreferencesData.getBoolean("serial.show_timestamp"));
|
||||
}
|
||||
}
|
||||
|
||||
private String addTimestamps(String text) {
|
||||
String now = new SimpleDateFormat("HH:mm:ss.SSS -> ").format(new Date());
|
||||
final StringBuilder sb = new StringBuilder(text.length() + now.length());
|
||||
StringTokenizer tokenizer = new StringTokenizer(text, LINE_SEPARATOR, true);
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
if (isStartingLine) {
|
||||
sb.append(now);
|
||||
}
|
||||
String token = tokenizer.nextToken();
|
||||
sb.append(token);
|
||||
// tokenizer returns "\n" as a single token
|
||||
isStartingLine = token.equals(LINE_SEPARATOR);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,10 @@ import cc.arduino.Constants;
|
||||
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
|
||||
import cc.arduino.UploaderUtils;
|
||||
import cc.arduino.contributions.*;
|
||||
import cc.arduino.contributions.libraries.*;
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.LibrariesIndexer;
|
||||
import cc.arduino.contributions.libraries.LibraryInstaller;
|
||||
import cc.arduino.contributions.libraries.LibraryOfSameTypeComparator;
|
||||
import cc.arduino.contributions.libraries.ui.LibraryManagerUI;
|
||||
import cc.arduino.contributions.packages.ContributedPlatform;
|
||||
import cc.arduino.contributions.packages.ContributionInstaller;
|
||||
@ -35,10 +38,11 @@ import cc.arduino.contributions.packages.ContributionsIndexer;
|
||||
import cc.arduino.contributions.packages.ui.ContributionManagerUI;
|
||||
import cc.arduino.files.DeleteFilesOnShutdown;
|
||||
import cc.arduino.packages.DiscoveryManager;
|
||||
import cc.arduino.packages.Uploader;
|
||||
import cc.arduino.view.Event;
|
||||
import cc.arduino.view.JMenuUtils;
|
||||
import cc.arduino.view.SplashScreenHelper;
|
||||
|
||||
import com.github.zafarkhaja.semver.Version;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import processing.app.debug.TargetBoard;
|
||||
@ -52,6 +56,7 @@ import processing.app.legacy.PApplet;
|
||||
import processing.app.macosx.ThinkDifferent;
|
||||
import processing.app.packages.LibraryList;
|
||||
import processing.app.packages.UserLibrary;
|
||||
import processing.app.packages.UserLibraryFolder.Location;
|
||||
import processing.app.syntax.PdeKeywords;
|
||||
import processing.app.syntax.SketchTextAreaDefaultInputMap;
|
||||
import processing.app.tools.MenuScroller;
|
||||
@ -61,15 +66,16 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.*;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static processing.app.I18n.format;
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
|
||||
@ -135,7 +141,7 @@ public class Base {
|
||||
if (OSUtils.isMacOS()) {
|
||||
System.setProperty("apple.laf.useScreenMenuBar",
|
||||
String.valueOf(!System.getProperty("os.version").startsWith("10.13")
|
||||
|| com.apple.eawt.Application.getApplication().isAboutMenuItemPresent()));
|
||||
|| isMacOsAboutMenuItemPresent()));
|
||||
|
||||
ThinkDifferent.init();
|
||||
}
|
||||
@ -148,6 +154,11 @@ public class Base {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static boolean isMacOsAboutMenuItemPresent() {
|
||||
return com.apple.eawt.Application.getApplication().isAboutMenuItemPresent();
|
||||
}
|
||||
|
||||
static public void initLogger() {
|
||||
Handler consoleHandler = new ConsoleLogger();
|
||||
consoleHandler.setLevel(Level.ALL);
|
||||
@ -209,6 +220,26 @@ public class Base {
|
||||
parser.parseArgumentsPhase1();
|
||||
commandLine = !parser.isGuiMode();
|
||||
|
||||
// This configure the logs root folder
|
||||
if (parser.isGuiMode()) {
|
||||
System.out.println("Set log4j store directory " + BaseNoGui.getSettingsFolder().getAbsolutePath());
|
||||
}
|
||||
System.setProperty("log4j.dir", BaseNoGui.getSettingsFolder().getAbsolutePath());
|
||||
|
||||
BaseNoGui.checkInstallationFolder();
|
||||
|
||||
// If no path is set, get the default sketchbook folder for this platform
|
||||
if (BaseNoGui.getSketchbookPath() == null) {
|
||||
File defaultFolder = getDefaultSketchbookFolderOrPromptForIt();
|
||||
if (BaseNoGui.getPortableFolder() != null)
|
||||
PreferencesData.set("sketchbook.path", BaseNoGui.getPortableSketchbookFolder());
|
||||
else
|
||||
PreferencesData.set("sketchbook.path", defaultFolder.getAbsolutePath());
|
||||
if (!defaultFolder.exists()) {
|
||||
defaultFolder.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
SplashScreenHelper splash;
|
||||
if (parser.isGuiMode()) {
|
||||
// Setup all notification widgets
|
||||
@ -243,23 +274,11 @@ public class Base {
|
||||
untitledFolder = FileUtils.createTempFolder("untitled" + new Random().nextInt(Integer.MAX_VALUE), ".tmp");
|
||||
DeleteFilesOnShutdown.add(untitledFolder);
|
||||
|
||||
BaseNoGui.checkInstallationFolder();
|
||||
|
||||
// If no path is set, get the default sketchbook folder for this platform
|
||||
if (BaseNoGui.getSketchbookPath() == null) {
|
||||
File defaultFolder = getDefaultSketchbookFolderOrPromptForIt();
|
||||
if (BaseNoGui.getPortableFolder() != null)
|
||||
PreferencesData.set("sketchbook.path", BaseNoGui.getPortableSketchbookFolder());
|
||||
else
|
||||
PreferencesData.set("sketchbook.path", defaultFolder.getAbsolutePath());
|
||||
if (!defaultFolder.exists()) {
|
||||
defaultFolder.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
splash.splashText(tr("Initializing packages..."));
|
||||
BaseNoGui.initPackages();
|
||||
|
||||
parser.getUploadPort().ifPresent(BaseNoGui::selectSerialPort);
|
||||
|
||||
splash.splashText(tr("Preparing boards..."));
|
||||
|
||||
if (!isCommandLine()) {
|
||||
@ -277,8 +296,9 @@ public class Base {
|
||||
pdeKeywords = new PdeKeywords();
|
||||
pdeKeywords.reload();
|
||||
|
||||
contributionInstaller = new ContributionInstaller(BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier());
|
||||
libraryInstaller = new LibraryInstaller(BaseNoGui.getPlatform());
|
||||
final GPGDetachedSignatureVerifier gpgDetachedSignatureVerifier = new GPGDetachedSignatureVerifier();
|
||||
contributionInstaller = new ContributionInstaller(BaseNoGui.getPlatform(), gpgDetachedSignatureVerifier);
|
||||
libraryInstaller = new LibraryInstaller(BaseNoGui.getPlatform(), gpgDetachedSignatureVerifier);
|
||||
|
||||
parser.parseArgumentsPhase2();
|
||||
|
||||
@ -292,7 +312,7 @@ public class Base {
|
||||
if (parser.isInstallBoard()) {
|
||||
ContributionsIndexer indexer = new ContributionsIndexer(
|
||||
BaseNoGui.getSettingsFolder(), BaseNoGui.getHardwareFolder(),
|
||||
BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier());
|
||||
BaseNoGui.getPlatform(), gpgDetachedSignatureVerifier);
|
||||
ProgressListener progressListener = new ConsoleProgressListener();
|
||||
|
||||
List<String> downloadedPackageIndexFiles = contributionInstaller.updateIndex(progressListener);
|
||||
@ -304,7 +324,12 @@ public class Base {
|
||||
|
||||
ContributedPlatform selected = null;
|
||||
if (boardToInstallParts.length == 3) {
|
||||
selected = indexer.getIndex().findPlatform(boardToInstallParts[0], boardToInstallParts[1], VersionHelper.valueOf(boardToInstallParts[2]).toString());
|
||||
Optional<Version> version = VersionHelper.valueOf(boardToInstallParts[2]);
|
||||
if (!version.isPresent()) {
|
||||
System.out.println(format(tr("Invalid version {0}"), boardToInstallParts[2]));
|
||||
System.exit(1);
|
||||
}
|
||||
selected = indexer.getIndex().findPlatform(boardToInstallParts[0], boardToInstallParts[1], version.get().toString());
|
||||
} else if (boardToInstallParts.length == 2) {
|
||||
List<ContributedPlatform> platformsByName = indexer.getIndex().findPlatforms(boardToInstallParts[0], boardToInstallParts[1]);
|
||||
Collections.sort(platformsByName, new DownloadableContributionVersionComparator());
|
||||
@ -319,11 +344,11 @@ public class Base {
|
||||
|
||||
ContributedPlatform installed = indexer.getInstalled(boardToInstallParts[0], boardToInstallParts[1]);
|
||||
|
||||
if (!selected.isReadOnly()) {
|
||||
if (!selected.isBuiltIn()) {
|
||||
contributionInstaller.install(selected, progressListener);
|
||||
}
|
||||
|
||||
if (installed != null && !installed.isReadOnly()) {
|
||||
if (installed != null && !installed.isBuiltIn()) {
|
||||
contributionInstaller.remove(installed);
|
||||
}
|
||||
|
||||
@ -337,15 +362,20 @@ public class Base {
|
||||
|
||||
LibrariesIndexer indexer = new LibrariesIndexer(BaseNoGui.getSettingsFolder());
|
||||
indexer.parseIndex();
|
||||
indexer.setSketchbookLibrariesFolder(BaseNoGui.getSketchbookLibrariesFolder());
|
||||
indexer.setLibrariesFolders(BaseNoGui.getLibrariesPath());
|
||||
indexer.setLibrariesFolders(BaseNoGui.getLibrariesFolders());
|
||||
indexer.rescanLibraries();
|
||||
|
||||
for (String library : parser.getLibraryToInstall().split(",")) {
|
||||
String[] libraryToInstallParts = library.split(":");
|
||||
|
||||
ContributedLibrary selected = null;
|
||||
if (libraryToInstallParts.length == 2) {
|
||||
selected = indexer.getIndex().find(libraryToInstallParts[0], VersionHelper.valueOf(libraryToInstallParts[1]).toString());
|
||||
Optional<Version> version = VersionHelper.valueOf(libraryToInstallParts[1]);
|
||||
if (!version.isPresent()) {
|
||||
System.out.println(format(tr("Invalid version {0}"), libraryToInstallParts[1]));
|
||||
System.exit(1);
|
||||
}
|
||||
selected = indexer.getIndex().find(libraryToInstallParts[0], version.get().toString());
|
||||
} else if (libraryToInstallParts.length == 1) {
|
||||
List<ContributedLibrary> librariesByName = indexer.getIndex().find(libraryToInstallParts[0]);
|
||||
Collections.sort(librariesByName, new DownloadableContributionVersionComparator());
|
||||
@ -358,11 +388,14 @@ public class Base {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
ContributedLibrary installed = indexer.getIndex().getInstalled(libraryToInstallParts[0]);
|
||||
if (selected.isReadOnly()) {
|
||||
libraryInstaller.remove(installed, progressListener);
|
||||
Optional<ContributedLibrary> mayInstalled = indexer.getIndex().getInstalled(libraryToInstallParts[0]);
|
||||
if (mayInstalled.isPresent() && selected.isIDEBuiltIn()) {
|
||||
System.out.println(tr(I18n
|
||||
.format("Library {0} is available as built-in in the IDE.\nRemoving the other version {1} installed in the sketchbook...",
|
||||
library, mayInstalled.get().getParsedVersion())));
|
||||
libraryInstaller.remove(mayInstalled.get(), progressListener);
|
||||
} else {
|
||||
libraryInstaller.install(selected, installed, progressListener);
|
||||
libraryInstaller.install(selected, progressListener);
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,7 +479,7 @@ public class Base {
|
||||
if (!parser.isForceSavePrefs())
|
||||
PreferencesData.setDoSave(true);
|
||||
if (handleOpen(file, retrieveSketchLocation(".default"), false) == null) {
|
||||
String mess = I18n.format(tr("Failed to open sketch: \"{0}\""), path);
|
||||
String mess = format(tr("Failed to open sketch: \"{0}\""), path);
|
||||
// Open failure is fatal in upload/verify mode
|
||||
if (parser.isVerifyOrUploadMode())
|
||||
showError(null, mess, 2);
|
||||
@ -483,6 +516,9 @@ public class Base {
|
||||
System.exit(0);
|
||||
} else if (parser.isGetPrefMode()) {
|
||||
BaseNoGui.dumpPrefs(parser);
|
||||
} else if (parser.isVersionMode()) {
|
||||
System.out.println("Arduino: " + BaseNoGui.VERSION_NAME_LONG);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -590,7 +626,12 @@ public class Base {
|
||||
.get("last.sketch" + index + ".location");
|
||||
if (locationStr == null)
|
||||
return defaultEditorLocation();
|
||||
return PApplet.parseInt(PApplet.split(locationStr, ','));
|
||||
|
||||
int location[] = PApplet.parseInt(PApplet.split(locationStr, ','));
|
||||
if (location[0] > screen.width || location[1] > screen.height)
|
||||
return defaultEditorLocation();
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
protected void storeRecentSketches(SketchController sketch) {
|
||||
@ -629,9 +670,6 @@ public class Base {
|
||||
System.err.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
// set the current window to be the console that's getting output
|
||||
EditorConsole.setCurrentEditorConsole(activeEditor.console);
|
||||
}
|
||||
|
||||
protected int[] defaultEditorLocation() {
|
||||
@ -742,7 +780,20 @@ public class Base {
|
||||
if (!newbieFile.createNewFile()) {
|
||||
throw new IOException();
|
||||
}
|
||||
FileUtils.copyFile(new File(getContentFile("examples"), "01.Basics" + File.separator + "BareMinimum" + File.separator + "BareMinimum.ino"), newbieFile);
|
||||
|
||||
// Initialize the pde file with the BareMinimum sketch.
|
||||
// Apply user-defined tab settings.
|
||||
String sketch = FileUtils.readFileToString(
|
||||
new File(getContentFile("examples"), "01.Basics" + File.separator
|
||||
+ "BareMinimum" + File.separator + "BareMinimum.ino"));
|
||||
String currentTab = " ";
|
||||
String newTab = (PreferencesData.getBoolean("editor.tabs.expand")
|
||||
? StringUtils.repeat(" ",
|
||||
PreferencesData.getInteger("editor.tabs.size"))
|
||||
: "\t");
|
||||
sketch = sketch.replaceAll(
|
||||
"(?<=(^|\n)(" + currentTab + "){0,50})" + currentTab, newTab);
|
||||
FileUtils.writeStringToFile(newbieFile, sketch);
|
||||
return newbieFile;
|
||||
}
|
||||
|
||||
@ -906,46 +957,23 @@ public class Base {
|
||||
* @return true if succeeded in closing, false if canceled.
|
||||
*/
|
||||
public boolean handleClose(Editor editor) {
|
||||
// Check if modified
|
||||
// boolean immediate = editors.size() == 1;
|
||||
if (!editor.checkModified()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (editors.size() == 1) {
|
||||
storeScreenDimensions();
|
||||
storeSketches();
|
||||
|
||||
// This will store the sketch count as zero
|
||||
editors.remove(editor);
|
||||
try {
|
||||
Editor.serialMonitor.close();
|
||||
} catch (Exception e) {
|
||||
//ignore
|
||||
if (!handleQuit()) {
|
||||
return false;
|
||||
}
|
||||
rebuildRecentSketchesMenuItems();
|
||||
|
||||
// Save out the current prefs state
|
||||
PreferencesData.save();
|
||||
|
||||
// Since this wasn't an actual Quit event, call System.exit()
|
||||
System.exit(0);
|
||||
|
||||
// Everything called after handleQuit will only affect OSX
|
||||
editor.setVisible(false);
|
||||
editors.remove(editor);
|
||||
} else {
|
||||
// More than one editor window open,
|
||||
// proceed with closing the current window.
|
||||
// Check if modified
|
||||
if (!editor.checkModified()) {
|
||||
return false;
|
||||
}
|
||||
editor.setVisible(false);
|
||||
editor.dispose();
|
||||
// for (int i = 0; i < editorCount; i++) {
|
||||
// if (editor == editors[i]) {
|
||||
// for (int j = i; j < editorCount-1; j++) {
|
||||
// editors[j] = editors[j+1];
|
||||
// }
|
||||
// editorCount--;
|
||||
// // Set to null so that garbage collection occurs
|
||||
// editors[editorCount] = null;
|
||||
// }
|
||||
// }
|
||||
editors.remove(editor);
|
||||
}
|
||||
return true;
|
||||
@ -968,11 +996,19 @@ public class Base {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// kill uploader (if still alive)
|
||||
UploaderUtils uploaderInstance = new UploaderUtils();
|
||||
Uploader uploader = uploaderInstance.getUploaderByPreferences(false);
|
||||
if (uploader != null && Uploader.programmerPid != null && Uploader.programmerPid.isAlive()) {
|
||||
// kill the stuck programmer
|
||||
Uploader.programmerPid.destroyForcibly();
|
||||
}
|
||||
|
||||
if (handleQuitEach()) {
|
||||
// Save out the current prefs state
|
||||
PreferencesData.save();
|
||||
|
||||
if (!OSUtils.hasMacOSStyleMenus()) {
|
||||
if (!OSUtils.isMacOS()) {
|
||||
// If this was fired from the menu or an AppleEvent (the Finder),
|
||||
// then Mac OS X will send the terminate signal itself.
|
||||
System.exit(0);
|
||||
@ -1062,9 +1098,8 @@ public class Base {
|
||||
}
|
||||
}
|
||||
|
||||
private List<ContributedLibrary> getSortedLibraries() {
|
||||
List<ContributedLibrary> installedLibraries = new LinkedList<>(BaseNoGui.librariesIndexer.getInstalledLibraries());
|
||||
Collections.sort(installedLibraries, new LibraryByTypeComparator());
|
||||
private LibraryList getSortedLibraries() {
|
||||
LibraryList installedLibraries = BaseNoGui.librariesIndexer.getInstalledLibraries();
|
||||
Collections.sort(installedLibraries, new LibraryOfSameTypeComparator());
|
||||
return installedLibraries;
|
||||
}
|
||||
@ -1087,6 +1122,7 @@ public class Base {
|
||||
addLibraryMenuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.this.handleAddLibrary();
|
||||
BaseNoGui.librariesIndexer.rescanLibraries();
|
||||
Base.this.onBoardOrPortChange();
|
||||
Base.this.rebuildImportMenu(Editor.importMenu);
|
||||
Base.this.rebuildExamplesMenu(Editor.examplesMenu);
|
||||
@ -1099,16 +1135,16 @@ public class Base {
|
||||
TargetPlatform targetPlatform = BaseNoGui.getTargetPlatform();
|
||||
|
||||
if (targetPlatform != null) {
|
||||
List<ContributedLibrary> libs = getSortedLibraries();
|
||||
LibraryList libs = getSortedLibraries();
|
||||
String lastLibType = null;
|
||||
for (ContributedLibrary lib : libs) {
|
||||
for (UserLibrary lib : libs) {
|
||||
String libType = lib.getTypes().get(0);
|
||||
if (!libType.equals(lastLibType)) {
|
||||
if (lastLibType != null) {
|
||||
importMenu.addSeparator();
|
||||
}
|
||||
lastLibType = libType;
|
||||
JMenuItem platformItem = new JMenuItem(I18n.format(tr("{0} libraries"), tr(lastLibType)));
|
||||
JMenuItem platformItem = new JMenuItem(format(tr("{0} libraries"), tr(lastLibType)));
|
||||
platformItem.setEnabled(false);
|
||||
importMenu.add(platformItem);
|
||||
}
|
||||
@ -1119,7 +1155,7 @@ public class Base {
|
||||
try {
|
||||
activeEditor.getSketchController().importLibrary(l);
|
||||
} catch (IOException e) {
|
||||
showWarning(tr("Error"), I18n.format("Unable to list header files in {0}", l.getSrcFolder()), e);
|
||||
showWarning(tr("Error"), format("Unable to list header files in {0}", l.getSrcFolder()), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1150,10 +1186,6 @@ public class Base {
|
||||
}
|
||||
|
||||
// Libraries can come from 4 locations: collect info about all four
|
||||
File ideLibraryPath = BaseNoGui.getContentFile("libraries");
|
||||
File sketchbookLibraryPath = BaseNoGui.getSketchbookLibrariesFolder();
|
||||
File platformLibraryPath = null;
|
||||
File referencedPlatformLibraryPath = null;
|
||||
String boardId = null;
|
||||
String referencedPlatformName = null;
|
||||
String myArch = null;
|
||||
@ -1161,14 +1193,12 @@ public class Base {
|
||||
if (targetPlatform != null) {
|
||||
myArch = targetPlatform.getId();
|
||||
boardId = BaseNoGui.getTargetBoard().getName();
|
||||
platformLibraryPath = new File(targetPlatform.getFolder(), "libraries");
|
||||
String core = BaseNoGui.getBoardPreferences().get("build.core", "arduino");
|
||||
if (core.contains(":")) {
|
||||
String refcore = core.split(":")[0];
|
||||
TargetPlatform referencedPlatform = BaseNoGui.getTargetPlatform(refcore, myArch);
|
||||
if (referencedPlatform != null) {
|
||||
referencedPlatformName = referencedPlatform.getPreferences().get("name");
|
||||
referencedPlatformLibraryPath = new File(referencedPlatform.getFolder(), "libraries");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1188,7 +1218,7 @@ public class Base {
|
||||
LibraryList otherLibs = new LibraryList();
|
||||
for (UserLibrary lib : allLibraries) {
|
||||
// Get the library's location - used for sorting into categories
|
||||
File libraryLocation = lib.getInstalledFolder().getParentFile();
|
||||
Location location = lib.getLocation();
|
||||
// Is this library compatible?
|
||||
List<String> arch = lib.getArchitectures();
|
||||
boolean compatible;
|
||||
@ -1198,31 +1228,28 @@ public class Base {
|
||||
compatible = arch.contains(myArch);
|
||||
}
|
||||
// IDE Libaries (including retired)
|
||||
if (libraryLocation.equals(ideLibraryPath)) {
|
||||
if (location == Location.IDE_BUILTIN) {
|
||||
if (compatible) {
|
||||
// only compatible IDE libs are shown
|
||||
boolean retired = false;
|
||||
List<String> types = lib.getTypes();
|
||||
if (types != null) retired = types.contains("Retired");
|
||||
if (retired) {
|
||||
if (lib.getTypes().contains("Retired")) {
|
||||
retiredIdeLibs.add(lib);
|
||||
} else {
|
||||
ideLibs.add(lib);
|
||||
}
|
||||
}
|
||||
// Platform Libraries
|
||||
} else if (libraryLocation.equals(platformLibraryPath)) {
|
||||
} else if (location == Location.CORE) {
|
||||
// all platform libs are assumed to be compatible
|
||||
platformLibs.add(lib);
|
||||
// Referenced Platform Libraries
|
||||
} else if (libraryLocation.equals(referencedPlatformLibraryPath)) {
|
||||
} else if (location == Location.REFERENCED_CORE) {
|
||||
// all referenced platform libs are assumed to be compatible
|
||||
referencedPlatformLibs.add(lib);
|
||||
// Sketchbook Libraries (including incompatible)
|
||||
} else if (libraryLocation.equals(sketchbookLibraryPath)) {
|
||||
} else if (location == Location.SKETCHBOOK) {
|
||||
if (compatible) {
|
||||
// libraries promoted from sketchbook (behave as builtin)
|
||||
if (lib.getTypes() != null && lib.getTypes().contains("Arduino")
|
||||
if (!lib.getTypes().isEmpty() && lib.getTypes().contains("Arduino")
|
||||
&& lib.getArchitectures().contains("*")) {
|
||||
ideLibs.add(lib);
|
||||
} else {
|
||||
@ -1260,7 +1287,7 @@ public class Base {
|
||||
if (!platformLibs.isEmpty()) {
|
||||
menu.addSeparator();
|
||||
platformLibs.sort();
|
||||
label = new JMenuItem(I18n.format(tr("Examples for {0}"), boardId));
|
||||
label = new JMenuItem(format(tr("Examples for {0}"), boardId));
|
||||
label.setEnabled(false);
|
||||
menu.add(label);
|
||||
for (UserLibrary lib : platformLibs) {
|
||||
@ -1271,7 +1298,7 @@ public class Base {
|
||||
if (!referencedPlatformLibs.isEmpty()) {
|
||||
menu.addSeparator();
|
||||
referencedPlatformLibs.sort();
|
||||
label = new JMenuItem(I18n.format(tr("Examples for {0}"), referencedPlatformName));
|
||||
label = new JMenuItem(format(tr("Examples for {0}"), referencedPlatformName));
|
||||
label.setEnabled(false);
|
||||
menu.add(label);
|
||||
for (UserLibrary lib : referencedPlatformLibs) {
|
||||
@ -1293,6 +1320,7 @@ public class Base {
|
||||
if (!sketchbookIncompatibleLibs.isEmpty()) {
|
||||
sketchbookIncompatibleLibs.sort();
|
||||
JMenu incompatible = new JMenu(tr("INCOMPATIBLE"));
|
||||
MenuScroller.setScrollerFor(incompatible);
|
||||
menu.add(incompatible);
|
||||
for (UserLibrary lib : sketchbookIncompatibleLibs) {
|
||||
addSketchesSubmenu(incompatible, lib);
|
||||
@ -1416,8 +1444,9 @@ public class Base {
|
||||
String filterText = "";
|
||||
String dropdownItem = "";
|
||||
if (actionevent instanceof Event) {
|
||||
filterText = ((Event) actionevent).getPayload().get("filterText").toString();
|
||||
dropdownItem = ((Event) actionevent).getPayload().get("dropdownItem").toString();
|
||||
Event e = ((Event) actionevent);
|
||||
filterText = e.getPayload().get("filterText").toString();
|
||||
dropdownItem = e.getPayload().get("dropdownItem").toString();
|
||||
}
|
||||
try {
|
||||
openBoardsManager(filterText, dropdownItem);
|
||||
@ -1437,41 +1466,37 @@ public class Base {
|
||||
boardMenu.add(new JSeparator());
|
||||
|
||||
// Generate custom menus for all platforms
|
||||
Set<String> customMenusTitles = new HashSet<>();
|
||||
for (TargetPackage targetPackage : BaseNoGui.packages.values()) {
|
||||
for (TargetPlatform targetPlatform : targetPackage.platforms()) {
|
||||
customMenusTitles.addAll(targetPlatform.getCustomMenus().values());
|
||||
}
|
||||
}
|
||||
for (String customMenuTitle : customMenusTitles) {
|
||||
for (String customMenuTitle : targetPlatform.getCustomMenus().values()) {
|
||||
JMenu customMenu = new JMenu(tr(customMenuTitle));
|
||||
customMenu.putClientProperty("platform", getPlatformUniqueId(targetPlatform));
|
||||
customMenu.putClientProperty("removeOnWindowDeactivation", true);
|
||||
boardsCustomMenus.add(customMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<JMenuItem> menuItemsToClickAfterStartup = new LinkedList<>();
|
||||
|
||||
ButtonGroup boardsButtonGroup = new ButtonGroup();
|
||||
Map<String, ButtonGroup> buttonGroupsMap = new HashMap<>();
|
||||
|
||||
List<JMenu> platformMenus = new ArrayList<>();
|
||||
|
||||
// Cycle through all packages
|
||||
boolean first = true;
|
||||
for (TargetPackage targetPackage : BaseNoGui.packages.values()) {
|
||||
// For every package cycle through all platform
|
||||
for (TargetPlatform targetPlatform : targetPackage.platforms()) {
|
||||
|
||||
// Add a separator from the previous platform
|
||||
if (!first)
|
||||
boardMenu.add(new JSeparator());
|
||||
first = false;
|
||||
|
||||
// Add a title for each platform
|
||||
String platformLabel = targetPlatform.getPreferences().get("name");
|
||||
if (platformLabel != null && !targetPlatform.getBoards().isEmpty()) {
|
||||
JMenuItem menuLabel = new JMenuItem(tr(platformLabel));
|
||||
menuLabel.setEnabled(false);
|
||||
boardMenu.add(menuLabel);
|
||||
}
|
||||
if (platformLabel == null)
|
||||
platformLabel = targetPackage.getId() + "-" + targetPlatform.getId();
|
||||
|
||||
JMenu platformBoardsMenu = new JMenu(platformLabel);
|
||||
MenuScroller.setScrollerFor(platformBoardsMenu);
|
||||
platformMenus.add(platformBoardsMenu);
|
||||
|
||||
// Cycle through all boards of this platform
|
||||
for (TargetBoard board : targetPlatform.getBoards().values()) {
|
||||
@ -1480,14 +1505,40 @@ public class Base {
|
||||
JMenuItem item = createBoardMenusAndCustomMenus(boardsCustomMenus, menuItemsToClickAfterStartup,
|
||||
buttonGroupsMap,
|
||||
board, targetPlatform, targetPackage);
|
||||
boardMenu.add(item);
|
||||
platformBoardsMenu.add(item);
|
||||
boardsButtonGroup.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
platformMenus.sort((x,y) -> x.getText().compareToIgnoreCase(y.getText()));
|
||||
|
||||
JMenuItem firstBoardItem = null;
|
||||
if (platformMenus.size() == 1) {
|
||||
// When just one platform exists, add the board items directly,
|
||||
// rather than using a submenu
|
||||
for (Component boardItem : platformMenus.get(0).getMenuComponents()) {
|
||||
boardMenu.add(boardItem);
|
||||
if (firstBoardItem == null)
|
||||
firstBoardItem = (JMenuItem)boardItem;
|
||||
}
|
||||
} else {
|
||||
// For multiple platforms, use submenus
|
||||
for (JMenu platformMenu : platformMenus) {
|
||||
if (firstBoardItem == null && platformMenu.getItemCount() > 0)
|
||||
firstBoardItem = platformMenu.getItem(0);
|
||||
boardMenu.add(platformMenu);
|
||||
}
|
||||
}
|
||||
|
||||
if (firstBoardItem == null) {
|
||||
throw new IllegalStateException("No available boards");
|
||||
}
|
||||
|
||||
// If there is no current board yet (first startup, or selected
|
||||
// board no longer defined), select first available board.
|
||||
if (menuItemsToClickAfterStartup.isEmpty()) {
|
||||
menuItemsToClickAfterStartup.add(selectFirstEnabledMenuItem(boardMenu));
|
||||
menuItemsToClickAfterStartup.add(firstBoardItem);
|
||||
}
|
||||
|
||||
for (JMenuItem menuItemToClick : menuItemsToClickAfterStartup) {
|
||||
@ -1496,6 +1547,10 @@ public class Base {
|
||||
}
|
||||
}
|
||||
|
||||
private String getPlatformUniqueId(TargetPlatform platform) {
|
||||
return platform.getId() + "_" + platform.getFolder();
|
||||
}
|
||||
|
||||
private JRadioButtonMenuItem createBoardMenusAndCustomMenus(
|
||||
final List<JMenu> boardsCustomMenus, List<JMenuItem> menuItemsToClickAfterStartup,
|
||||
Map<String, ButtonGroup> buttonGroupsMap,
|
||||
@ -1519,6 +1574,7 @@ public class Base {
|
||||
onBoardOrPortChange();
|
||||
rebuildImportMenu(Editor.importMenu);
|
||||
rebuildExamplesMenu(Editor.examplesMenu);
|
||||
rebuildProgrammerMenu();
|
||||
}
|
||||
};
|
||||
action.putValue("b", board);
|
||||
@ -1533,7 +1589,7 @@ public class Base {
|
||||
PreferencesMap customMenus = targetPlatform.getCustomMenus();
|
||||
for (final String menuId : customMenus.keySet()) {
|
||||
String title = customMenus.get(menuId);
|
||||
JMenu menu = getBoardCustomMenu(tr(title));
|
||||
JMenu menu = getBoardCustomMenu(tr(title), getPlatformUniqueId(targetPlatform));
|
||||
|
||||
if (board.hasMenu(menuId)) {
|
||||
PreferencesMap boardCustomMenu = board.getMenuLabels(menuId);
|
||||
@ -1541,11 +1597,16 @@ public class Base {
|
||||
@SuppressWarnings("serial")
|
||||
Action subAction = new AbstractAction(tr(boardCustomMenu.get(customMenuOption))) {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
PreferencesData.set("custom_" + menuId, ((TargetBoard) getValue("board")).getId() + "_" + getValue("custom_menu_option"));
|
||||
PreferencesData.set("custom_" + menuId, ((List<TargetBoard>) getValue("board")).get(0).getId() + "_" + getValue("custom_menu_option"));
|
||||
onBoardOrPortChange();
|
||||
}
|
||||
};
|
||||
subAction.putValue("board", board);
|
||||
List<TargetBoard> boards = (List<TargetBoard>) subAction.getValue("board");
|
||||
if (boards == null) {
|
||||
boards = new ArrayList<>();
|
||||
}
|
||||
boards.add(board);
|
||||
subAction.putValue("board", boards);
|
||||
subAction.putValue("custom_menu_option", customMenuOption);
|
||||
|
||||
if (!buttonGroupsMap.containsKey(menuId)) {
|
||||
@ -1573,7 +1634,9 @@ public class Base {
|
||||
JMenu menu = boardsCustomMenus.get(i);
|
||||
for (int m = 0; m < menu.getItemCount(); m++) {
|
||||
JMenuItem menuItem = menu.getItem(m);
|
||||
menuItem.setVisible(menuItem.getAction().getValue("board").equals(board));
|
||||
for (TargetBoard t_board : (List<TargetBoard>)menuItem.getAction().getValue("board")) {
|
||||
menuItem.setVisible(t_board.equals(board));
|
||||
}
|
||||
}
|
||||
menu.setVisible(ifThereAreVisibleItemsOn(menu));
|
||||
|
||||
@ -1596,9 +1659,9 @@ public class Base {
|
||||
return false;
|
||||
}
|
||||
|
||||
private JMenu getBoardCustomMenu(String label) throws Exception {
|
||||
private JMenu getBoardCustomMenu(String label, String platformUniqueId) throws Exception {
|
||||
for (JMenu menu : boardsCustomMenus) {
|
||||
if (label.equals(menu.getText())) {
|
||||
if (label.equals(menu.getText()) && menu.getClientProperty("platform").equals(platformUniqueId)) {
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
@ -1630,27 +1693,37 @@ public class Base {
|
||||
throw new IllegalStateException("Menu has no enabled items");
|
||||
}
|
||||
|
||||
private static JMenuItem selectFirstEnabledMenuItem(JMenu menu) {
|
||||
for (int i = 1; i < menu.getItemCount(); i++) {
|
||||
JMenuItem item = menu.getItem(i);
|
||||
if (item != null && item.isEnabled()) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Menu has no enabled items");
|
||||
}
|
||||
|
||||
public void rebuildProgrammerMenu() {
|
||||
programmerMenus = new LinkedList<>();
|
||||
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
for (TargetPackage targetPackage : BaseNoGui.packages.values()) {
|
||||
for (TargetPlatform targetPlatform : targetPackage.platforms()) {
|
||||
for (String programmer : targetPlatform.getProgrammers().keySet()) {
|
||||
String id = targetPackage.getId() + ":" + programmer;
|
||||
|
||||
TargetBoard board = BaseNoGui.getTargetBoard();
|
||||
TargetPlatform boardPlatform = board.getContainerPlatform();
|
||||
TargetPlatform corePlatform = null;
|
||||
|
||||
String core = board.getPreferences().get("build.core");
|
||||
if (core.contains(":")) {
|
||||
String[] split = core.split(":", 2);
|
||||
corePlatform = BaseNoGui.getCurrentTargetPlatformFromPackage(split[0]);
|
||||
}
|
||||
|
||||
addProgrammersForPlatform(boardPlatform, programmerMenus, group);
|
||||
if (corePlatform != null)
|
||||
addProgrammersForPlatform(corePlatform, programmerMenus, group);
|
||||
|
||||
if (programmerMenus.isEmpty()) {
|
||||
JMenuItem item = new JMenuItem(tr("No programmers available for this board"));
|
||||
item.setEnabled(false);
|
||||
programmerMenus.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void addProgrammersForPlatform(TargetPlatform platform, List<JMenuItem> menus, ButtonGroup group) {
|
||||
for (String programmer : platform.getProgrammers().keySet()) {
|
||||
String id = platform.getContainerPackage().getId() + ":" + programmer;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
AbstractAction action = new AbstractAction(targetPlatform.getProgrammer(programmer).get("name")) {
|
||||
AbstractAction action = new AbstractAction(platform.getProgrammer(programmer).get("name")) {
|
||||
public void actionPerformed(ActionEvent actionevent) {
|
||||
PreferencesData.set("programmer", "" + getValue("id"));
|
||||
}
|
||||
@ -1661,9 +1734,7 @@ public class Base {
|
||||
item.setSelected(true);
|
||||
}
|
||||
group.add(item);
|
||||
programmerMenus.add(item);
|
||||
}
|
||||
}
|
||||
menus.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1692,19 +1763,12 @@ public class Base {
|
||||
});
|
||||
|
||||
boolean ifound = false;
|
||||
|
||||
for (File subfolder : files) {
|
||||
if (FileUtils.isSCCSOrHiddenFile(subfolder)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!subfolder.isDirectory()) continue;
|
||||
|
||||
if (addSketchesSubmenu(menu, subfolder.getName(), subfolder)) {
|
||||
if (!FileUtils.isSCCSOrHiddenFile(subfolder) && subfolder.isDirectory()
|
||||
&& addSketchesSubmenu(menu, subfolder.getName(), subfolder)) {
|
||||
ifound = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ifound;
|
||||
}
|
||||
|
||||
@ -1789,7 +1853,7 @@ public class Base {
|
||||
try {
|
||||
activeEditor.getSketchController().importLibrary(l);
|
||||
} catch (IOException e) {
|
||||
showWarning(tr("Error"), I18n.format("Unable to list header files in {0}", l.getSrcFolder()), e);
|
||||
showWarning(tr("Error"), format("Unable to list header files in {0}", l.getSrcFolder()), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1878,6 +1942,75 @@ public class Base {
|
||||
getEditors().forEach(Editor::applyPreferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link MouseWheelListener} and {@link KeyListener} to the given
|
||||
* component that will make "CTRL scroll" and "CTRL +/-"
|
||||
* (with optional SHIFT for +) increase/decrease the editor text size.
|
||||
* This method is equivalent to calling
|
||||
* {@link #addEditorFontResizeMouseWheelListener(Component)} and
|
||||
* {@link #addEditorFontResizeKeyListener(Component)} on the given component.
|
||||
* Note that this also affects components that use the editor font settings.
|
||||
* @param comp - The component to add the listener to.
|
||||
*/
|
||||
public void addEditorFontResizeListeners(Component comp) {
|
||||
addEditorFontResizeMouseWheelListener(comp);
|
||||
addEditorFontResizeKeyListener(comp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link MouseWheelListener} to the given component that will
|
||||
* make "CTRL scroll" increase/decrease the editor text size.
|
||||
* When CTRL is not pressed while scrolling, mouse wheel events are passed
|
||||
* on to the parent of the given component.
|
||||
* Note that this also affects components that use the editor font settings.
|
||||
* @param comp - The component to add the listener to.
|
||||
*/
|
||||
public void addEditorFontResizeMouseWheelListener(Component comp) {
|
||||
comp.addMouseWheelListener(e -> {
|
||||
if (e.isControlDown()) {
|
||||
if (e.getWheelRotation() < 0) {
|
||||
this.handleFontSizeChange(1);
|
||||
} else {
|
||||
this.handleFontSizeChange(-1);
|
||||
}
|
||||
} else {
|
||||
if (e.getComponent() != null && e.getComponent().getParent() != null) {
|
||||
e.getComponent().getParent().dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link KeyListener} to the given component that will make "CTRL +/-"
|
||||
* (with optional SHIFT for +) increase/decrease the editor text size.
|
||||
* Note that this also affects components that use the editor font settings.
|
||||
* @param comp - The component to add the listener to.
|
||||
*/
|
||||
public void addEditorFontResizeKeyListener(Component comp) {
|
||||
comp.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK
|
||||
|| e.getModifiersEx() == (KeyEvent.CTRL_DOWN_MASK
|
||||
| KeyEvent.SHIFT_DOWN_MASK)) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_PLUS:
|
||||
case KeyEvent.VK_EQUALS:
|
||||
Base.this.handleFontSizeChange(1);
|
||||
break;
|
||||
case KeyEvent.VK_MINUS:
|
||||
if (!e.isShiftDown()) {
|
||||
Base.this.handleFontSizeChange(-1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<JMenu> getBoardsCustomMenus() {
|
||||
return boardsCustomMenus;
|
||||
}
|
||||
@ -1939,10 +2072,9 @@ public class Base {
|
||||
static public void openURL(String url) {
|
||||
try {
|
||||
BaseNoGui.getPlatform().openURL(url);
|
||||
|
||||
} catch (Exception e) {
|
||||
showWarning(tr("Problem Opening URL"),
|
||||
I18n.format(tr("Could not open the URL\n{0}"), url), e);
|
||||
format(tr("Could not open the URL\n{0}"), url), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1964,10 +2096,9 @@ public class Base {
|
||||
static public void openFolder(File file) {
|
||||
try {
|
||||
BaseNoGui.getPlatform().openFolder(file);
|
||||
|
||||
} catch (Exception e) {
|
||||
showWarning(tr("Problem Opening Folder"),
|
||||
I18n.format(tr("Could not open the folder\n{0}"), file.getAbsolutePath()), e);
|
||||
format(tr("Could not open the folder\n{0}"), file.getAbsolutePath()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2045,7 +2176,7 @@ public class Base {
|
||||
if(referenceFile.exists()){
|
||||
openURL(referenceFile.getAbsolutePath());
|
||||
}else{
|
||||
showWarning(tr("Problem Opening URL"), I18n.format(tr("Could not open the URL\n{0}"), referenceFile), null);
|
||||
showWarning(tr("Problem Opening URL"), format(tr("Could not open the URL\n{0}"), referenceFile), null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2301,7 +2432,7 @@ public class Base {
|
||||
|
||||
String libName = libFolder.getName();
|
||||
if (!BaseNoGui.isSanitaryName(libName)) {
|
||||
String mess = I18n.format(tr("The library \"{0}\" cannot be used.\n"
|
||||
String mess = format(tr("The library \"{0}\" cannot be used.\n"
|
||||
+ "Library names must contain only basic letters and numbers.\n"
|
||||
+ "(ASCII only and no spaces, and it cannot start with a number)"),
|
||||
libName);
|
||||
@ -2310,8 +2441,10 @@ public class Base {
|
||||
}
|
||||
|
||||
String[] headers;
|
||||
if (new File(libFolder, "library.properties").exists()) {
|
||||
headers = BaseNoGui.headerListFromIncludePath(UserLibrary.create(libFolder).getSrcFolder());
|
||||
File libProp = new File(libFolder, "library.properties");
|
||||
File srcFolder = new File(libFolder, "src");
|
||||
if (libProp.exists() && srcFolder.isDirectory()) {
|
||||
headers = BaseNoGui.headerListFromIncludePath(srcFolder);
|
||||
} else {
|
||||
headers = BaseNoGui.headerListFromIncludePath(libFolder);
|
||||
}
|
||||
@ -2321,9 +2454,9 @@ public class Base {
|
||||
}
|
||||
|
||||
// copy folder
|
||||
File destinationFolder = new File(BaseNoGui.getSketchbookLibrariesFolder(), sourceFile.getName());
|
||||
File destinationFolder = new File(BaseNoGui.getSketchbookLibrariesFolder().folder, sourceFile.getName());
|
||||
if (!destinationFolder.mkdir()) {
|
||||
activeEditor.statusError(I18n.format(tr("A library named {0} already exists"), sourceFile.getName()));
|
||||
activeEditor.statusError(format(tr("A library named {0} already exists"), sourceFile.getName()));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
167
app/src/processing/app/CommandHistory.java
Normal file
167
app/src/processing/app/CommandHistory.java
Normal file
@ -0,0 +1,167 @@
|
||||
package processing.app;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* Keeps track of command history in console-like applications.
|
||||
* @author P.J.S. Kools
|
||||
*/
|
||||
public class CommandHistory {
|
||||
|
||||
private final LinkedList<String> commandHistory = new LinkedList<String>();
|
||||
private final int maxHistorySize;
|
||||
private ListIterator<String> iterator = null;
|
||||
private boolean iteratorAsc;
|
||||
|
||||
/**
|
||||
* Create a new {@link CommandHistory}.
|
||||
* @param maxHistorySize - The max command history size.
|
||||
*/
|
||||
public CommandHistory(int maxHistorySize) {
|
||||
this.maxHistorySize = (maxHistorySize < 0 ? 0 : maxHistorySize);
|
||||
this.commandHistory.addLast(""); // Current command placeholder.
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given command to the history and resets the history traversal
|
||||
* position to the latest command. If the latest command in the history is
|
||||
* equal to the given command, it will not be added to the history.
|
||||
* If the max history size is exceeded, the oldest command will be removed
|
||||
* from the history.
|
||||
* @param command - The command to add.
|
||||
*/
|
||||
public void addCommand(String command) {
|
||||
if (this.maxHistorySize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove 'current' command.
|
||||
this.commandHistory.removeLast();
|
||||
|
||||
// Add new command if it differs from the latest command.
|
||||
if (this.commandHistory.isEmpty()
|
||||
|| !this.commandHistory.getLast().equals(command)) {
|
||||
|
||||
// Remove oldest command if max history size is exceeded.
|
||||
if (this.commandHistory.size() >= this.maxHistorySize) {
|
||||
this.commandHistory.removeFirst();
|
||||
}
|
||||
|
||||
// Add new command and reset 'current' command.
|
||||
this.commandHistory.addLast(command);
|
||||
}
|
||||
|
||||
// Re-add 'current' command and reset command iterator.
|
||||
this.commandHistory.addLast(""); // Current command placeholder.
|
||||
this.iterator = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether a next (more recent) command is available in the history.
|
||||
* @return {@code true} if a next command is available,
|
||||
* returns {@code false} otherwise.
|
||||
*/
|
||||
public boolean hasNextCommand() {
|
||||
if (this.iterator == null) {
|
||||
return false;
|
||||
}
|
||||
if (!this.iteratorAsc) {
|
||||
this.iterator.next(); // Current command, ascending.
|
||||
this.iteratorAsc = true;
|
||||
}
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next (more recent) command from the history.
|
||||
* @return The next command or {@code null} if no next command is available.
|
||||
*/
|
||||
public String getNextCommand() {
|
||||
|
||||
// Return null if there is no next command available.
|
||||
if (!this.hasNextCommand()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get next command.
|
||||
String next = this.iterator.next();
|
||||
|
||||
// Reset 'current' command when at the end of the list.
|
||||
if (this.iterator.nextIndex() == this.commandHistory.size()) {
|
||||
this.iterator.set(""); // Reset 'current' command.
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether a previous (older) command is available in the history.
|
||||
* @return {@code true} if a previous command is available,
|
||||
* returns {@code false} otherwise.
|
||||
*/
|
||||
public boolean hasPreviousCommand() {
|
||||
if (this.iterator == null) {
|
||||
return this.commandHistory.size() > 1;
|
||||
}
|
||||
if (this.iteratorAsc) {
|
||||
this.iterator.previous(); // Current command, descending.
|
||||
this.iteratorAsc = false;
|
||||
}
|
||||
return this.iterator.hasPrevious();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the previous (older) command from the history.
|
||||
* When this method is called while the most recent command in the history is
|
||||
* selected, this will store the current command as temporary latest command
|
||||
* so that {@link #getNextCommand()} will return it once. This temporary
|
||||
* latest command gets reset when this case occurs again or when
|
||||
* {@link #addCommand(String)} is invoked.
|
||||
* @param currentCommand - The current unexecuted command.
|
||||
* @return The previous command or {@code null} if no previous command is
|
||||
* available.
|
||||
*/
|
||||
public String getPreviousCommand(String currentCommand) {
|
||||
|
||||
// Return null if there is no previous command available.
|
||||
if (!this.hasPreviousCommand()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Store current unexecuted command and create iterator if not traversing.
|
||||
if (this.iterator == null) {
|
||||
this.iterator =
|
||||
this.commandHistory.listIterator(this.commandHistory.size());
|
||||
this.iterator.previous(); // Last element, descending.
|
||||
this.iteratorAsc = false;
|
||||
}
|
||||
|
||||
// Store current unexecuted command if on 'current' index.
|
||||
if (this.iterator.nextIndex() == this.commandHistory.size() - 1) {
|
||||
this.iterator.set(currentCommand == null ? "" : currentCommand);
|
||||
}
|
||||
|
||||
// Return the previous command.
|
||||
return this.iterator.previous();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the history location to the most recent command.
|
||||
* @returns The latest unexecuted command as stored by
|
||||
* {@link #getPreviousCommand(String)} or an empty string if no such command
|
||||
* was set.
|
||||
*/
|
||||
public String resetHistoryLocation() {
|
||||
this.iterator = null;
|
||||
return this.commandHistory.set(this.commandHistory.size() - 1, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the command history.
|
||||
*/
|
||||
public void clear() {
|
||||
this.iterator = null;
|
||||
this.commandHistory.clear();
|
||||
this.commandHistory.addLast(""); // Current command placeholder.
|
||||
}
|
||||
}
|
@ -22,6 +22,72 @@
|
||||
|
||||
package processing.app;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
import static processing.app.Theme.scale;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.print.PageFormat;
|
||||
import java.awt.print.PrinterException;
|
||||
import java.awt.print.PrinterJob;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.TransferHandler;
|
||||
import javax.swing.event.MenuEvent;
|
||||
import javax.swing.event.MenuListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.Element;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
|
||||
|
||||
import com.jcraft.jsch.JSchException;
|
||||
|
||||
import cc.arduino.CompilerProgressListener;
|
||||
import cc.arduino.packages.BoardPort;
|
||||
import cc.arduino.packages.MonitorFactory;
|
||||
import cc.arduino.packages.Uploader;
|
||||
@ -29,10 +95,9 @@ import cc.arduino.packages.uploaders.SerialUploader;
|
||||
import cc.arduino.view.GoToLineNumber;
|
||||
import cc.arduino.view.StubMenuListener;
|
||||
import cc.arduino.view.findreplace.FindReplace;
|
||||
import cc.arduino.CompilerProgressListener;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import jssc.SerialPortException;
|
||||
import processing.app.debug.RunnerException;
|
||||
import processing.app.debug.TargetBoard;
|
||||
import processing.app.forms.PasswordAuthorizationDialog;
|
||||
import processing.app.helpers.DocumentTextChangeListener;
|
||||
import processing.app.helpers.Keys;
|
||||
@ -45,33 +110,6 @@ import processing.app.syntax.SketchTextArea;
|
||||
import processing.app.tools.MenuScroller;
|
||||
import processing.app.tools.Tool;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.event.*;
|
||||
import java.awt.print.PageFormat;
|
||||
import java.awt.print.PrinterException;
|
||||
import java.awt.print.PrinterJob;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
import static processing.app.Theme.scale;
|
||||
|
||||
/**
|
||||
* Main editor panel for the Processing Development Environment.
|
||||
*/
|
||||
@ -94,24 +132,25 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
public boolean test(SketchController controller) {
|
||||
return PreferencesData.getBoolean("editor.save_on_verify")
|
||||
&& controller.getSketch().isModified()
|
||||
&& !controller.isReadOnly(
|
||||
BaseNoGui.librariesIndexer
|
||||
.getInstalledLibraries(),
|
||||
BaseNoGui.getExamplesPath());
|
||||
&& !controller.isReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ShouldSaveReadOnly implements Predicate<SketchController> {
|
||||
private static class CanExportInSketchFolder
|
||||
implements Predicate<SketchController> {
|
||||
|
||||
@Override
|
||||
public boolean test(SketchController sketch) {
|
||||
return sketch.isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath());
|
||||
public boolean test(SketchController controller) {
|
||||
if (controller.isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
if (controller.getSketch().isModified()) {
|
||||
return PreferencesData.getBoolean("editor.save_on_verify");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private final static List<String> BOARD_PROTOCOLS_ORDER = Arrays.asList("serial", "network");
|
||||
private final static List<String> BOARD_PROTOCOLS_ORDER_TRANSLATIONS = Arrays.asList(tr("Serial ports"), tr("Network ports"));
|
||||
|
||||
final Base base;
|
||||
|
||||
// otherwise, if the window is resized with the message label
|
||||
@ -144,7 +183,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
private int numTools = 0;
|
||||
|
||||
public boolean avoidMultipleOperations = false;
|
||||
static public boolean avoidMultipleOperations = false;
|
||||
|
||||
private final EditorToolbar toolbar;
|
||||
// these menus are shared so that they needn't be rebuilt for all windows
|
||||
@ -182,7 +221,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
private JMenuItem saveAsMenuItem;
|
||||
|
||||
//boolean presenting;
|
||||
private boolean uploading;
|
||||
static private boolean uploading;
|
||||
|
||||
// undo fellers
|
||||
private JMenuItem undoItem;
|
||||
@ -194,10 +233,12 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
Runnable presentHandler;
|
||||
private Runnable runAndSaveHandler;
|
||||
private Runnable presentAndSaveHandler;
|
||||
Runnable exportHandler;
|
||||
private Runnable exportAppHandler;
|
||||
private UploadHandler uploadHandler;
|
||||
private UploadHandler uploadUsingProgrammerHandler;
|
||||
private Runnable timeoutUploadHandler;
|
||||
|
||||
private Map<String, Tool> internalToolCache = new HashMap<String, Tool>();
|
||||
|
||||
public Editor(Base ibase, File file, int[] storedLocation, int[] defaultLocation, Platform platform) throws Exception {
|
||||
super("Arduino");
|
||||
this.base = ibase;
|
||||
@ -228,8 +269,6 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
// added for 1.0.5
|
||||
// http://dev.processing.org/bugs/show_bug.cgi?id=1260
|
||||
public void windowDeactivated(WindowEvent e) {
|
||||
fileMenu.remove(sketchbookMenu);
|
||||
fileMenu.remove(examplesMenu);
|
||||
List<Component> toolsMenuItemsToRemove = new LinkedList<>();
|
||||
for (Component menuItem : toolsMenu.getMenuComponents()) {
|
||||
if (menuItem instanceof JComponent) {
|
||||
@ -278,7 +317,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
status = new EditorStatus(this);
|
||||
consolePanel.add(status, BorderLayout.NORTH);
|
||||
|
||||
console = new EditorConsole();
|
||||
console = new EditorConsole(base);
|
||||
console.setName("console");
|
||||
// windows puts an ugly border on this guy
|
||||
console.setBorder(null);
|
||||
@ -339,6 +378,9 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
// Open the document that was passed in
|
||||
boolean loaded = handleOpenInternal(file);
|
||||
if (!loaded) sketchController = null;
|
||||
|
||||
// default the console output to the last opened editor
|
||||
EditorConsole.setCurrentEditorConsole(console);
|
||||
}
|
||||
|
||||
|
||||
@ -451,9 +493,14 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
boolean external = PreferencesData.getBoolean("editor.external");
|
||||
saveMenuItem.setEnabled(!external);
|
||||
saveAsMenuItem.setEnabled(!external);
|
||||
for (EditorTab tab: tabs)
|
||||
for (EditorTab tab: tabs) {
|
||||
tab.applyPreferences();
|
||||
}
|
||||
console.applyPreferences();
|
||||
if (serialMonitor != null) {
|
||||
serialMonitor.applyPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
@ -465,11 +512,11 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
fileMenu.addMenuListener(new StubMenuListener() {
|
||||
@Override
|
||||
public void menuSelected(MenuEvent e) {
|
||||
List<Component> components = Arrays.asList(fileMenu.getComponents());
|
||||
List<Component> components = Arrays.asList(fileMenu.getMenuComponents());
|
||||
if (!components.contains(sketchbookMenu)) {
|
||||
fileMenu.insert(sketchbookMenu, 3);
|
||||
}
|
||||
if (!components.contains(sketchbookMenu)) {
|
||||
if (!components.contains(examplesMenu)) {
|
||||
fileMenu.insert(examplesMenu, 4);
|
||||
}
|
||||
fileMenu.revalidate();
|
||||
@ -498,7 +545,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
toolsMenu.addMenuListener(new StubMenuListener() {
|
||||
@Override
|
||||
public void menuSelected(MenuEvent e) {
|
||||
List<Component> components = Arrays.asList(toolsMenu.getComponents());
|
||||
List<Component> components = Arrays.asList(toolsMenu.getMenuComponents());
|
||||
int offset = 0;
|
||||
for (JMenu menu : base.getBoardsCustomMenus()) {
|
||||
if (!components.contains(menu)) {
|
||||
@ -528,37 +575,28 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
fileMenu.setMnemonic(KeyEvent.VK_F);
|
||||
|
||||
item = newJMenuItem(tr("New"), 'N');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
item.addActionListener(event -> {
|
||||
try {
|
||||
base.handleNew();
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
fileMenu.add(item);
|
||||
|
||||
item = Editor.newJMenuItem(tr("Open..."), 'O');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
item.addActionListener(event -> {
|
||||
try {
|
||||
base.handleOpenPrompt();
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
fileMenu.add(item);
|
||||
|
||||
base.rebuildRecentSketchesMenuItems();
|
||||
recentSketchesMenu = new JMenu(tr("Open Recent"));
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
rebuildRecentSketchesMenu();
|
||||
}
|
||||
});
|
||||
SwingUtilities.invokeLater(() -> rebuildRecentSketchesMenu());
|
||||
fileMenu.add(recentSketchesMenu);
|
||||
|
||||
if (sketchbookMenu == null) {
|
||||
@ -576,45 +614,25 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
fileMenu.add(examplesMenu);
|
||||
|
||||
item = Editor.newJMenuItem(tr("Close"), 'W');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
base.handleClose(Editor.this);
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> base.handleClose(Editor.this));
|
||||
fileMenu.add(item);
|
||||
|
||||
saveMenuItem = newJMenuItem(tr("Save"), 'S');
|
||||
saveMenuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleSave(false);
|
||||
}
|
||||
});
|
||||
saveMenuItem.addActionListener(event -> handleSave(false));
|
||||
fileMenu.add(saveMenuItem);
|
||||
|
||||
saveAsMenuItem = newJMenuItemShift(tr("Save As..."), 'S');
|
||||
saveAsMenuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleSaveAs();
|
||||
}
|
||||
});
|
||||
saveAsMenuItem.addActionListener(event -> handleSaveAs());
|
||||
fileMenu.add(saveAsMenuItem);
|
||||
|
||||
fileMenu.addSeparator();
|
||||
|
||||
item = newJMenuItemShift(tr("Page Setup"), 'P');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handlePageSetup();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> handlePageSetup());
|
||||
fileMenu.add(item);
|
||||
|
||||
item = newJMenuItem(tr("Print"), 'P');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handlePrint();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> handlePrint());
|
||||
fileMenu.add(item);
|
||||
|
||||
// macosx already has its own preferences and quit menu
|
||||
@ -622,21 +640,13 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
fileMenu.addSeparator();
|
||||
|
||||
item = newJMenuItem(tr("Preferences"), ',');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
base.handlePrefs();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> base.handlePrefs());
|
||||
fileMenu.add(item);
|
||||
|
||||
fileMenu.addSeparator();
|
||||
|
||||
item = newJMenuItem(tr("Quit"), 'Q');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
base.handleQuit();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> base.handleQuit());
|
||||
fileMenu.add(item);
|
||||
}
|
||||
return fileMenu;
|
||||
@ -653,58 +663,36 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
sketchMenu.removeAll();
|
||||
|
||||
JMenuItem item = newJMenuItem(tr("Verify/Compile"), 'R');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleRun(false, Editor.this.presentHandler, Editor.this.runHandler);
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> handleRun(false, presentHandler, runHandler));
|
||||
sketchMenu.add(item);
|
||||
|
||||
item = newJMenuItem(tr("Upload"), 'U');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleExport(false);
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> handleExport(false));
|
||||
sketchMenu.add(item);
|
||||
|
||||
item = newJMenuItemShift(tr("Upload Using Programmer"), 'U');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleExport(true);
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> handleExport(true));
|
||||
sketchMenu.add(item);
|
||||
|
||||
|
||||
item = newJMenuItemAlt(tr("Export compiled Binary"), 'S');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (new ShouldSaveReadOnly().test(sketchController) && !handleSave(true)) {
|
||||
item.addActionListener(event -> {
|
||||
if (!(new CanExportInSketchFolder().test(sketchController))) {
|
||||
System.out.println(tr("Export canceled, changes must first be saved."));
|
||||
return;
|
||||
}
|
||||
handleRun(false, new ShouldSaveReadOnly(), Editor.this.presentAndSaveHandler, Editor.this.runAndSaveHandler);
|
||||
}
|
||||
handleRun(false, new CanExportInSketchFolder(), presentAndSaveHandler, runAndSaveHandler);
|
||||
|
||||
});
|
||||
sketchMenu.add(item);
|
||||
|
||||
// item = new JMenuItem("Stop");
|
||||
// item.addActionListener(new ActionListener() {
|
||||
// public void actionPerformed(ActionEvent e) {
|
||||
// handleStop();
|
||||
// }
|
||||
// });
|
||||
// item.addActionListener(event -> handleStop());
|
||||
// sketchMenu.add(item);
|
||||
|
||||
sketchMenu.addSeparator();
|
||||
|
||||
item = newJMenuItem(tr("Show Sketch Folder"), 'K');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openFolder(sketch.getFolder());
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> Base.openFolder(sketch.getFolder()));
|
||||
sketchMenu.add(item);
|
||||
item.setEnabled(Base.openFolderAvailable());
|
||||
|
||||
@ -716,11 +704,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
sketchMenu.add(importMenu);
|
||||
|
||||
item = new JMenuItem(tr("Add File..."));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
sketchController.handleAddFile();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> sketchController.handleAddFile());
|
||||
sketchMenu.add(item);
|
||||
}
|
||||
|
||||
@ -777,6 +761,20 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
toolsMenu.add(item);
|
||||
|
||||
toolsMenu.addMenuListener(new StubMenuListener() {
|
||||
public JMenuItem getSelectedItemRecursive(JMenu menu) {
|
||||
int count = menu.getItemCount();
|
||||
for (int i=0; i < count; i++) {
|
||||
JMenuItem item = menu.getItem(i);
|
||||
|
||||
if ((item instanceof JMenu))
|
||||
item = getSelectedItemRecursive((JMenu)item);
|
||||
|
||||
if (item != null && item.isSelected())
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void menuSelected(MenuEvent e) {
|
||||
//System.out.println("Tools menu selected.");
|
||||
populatePortMenu();
|
||||
@ -788,15 +786,9 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
String basename = name;
|
||||
int index = name.indexOf(':');
|
||||
if (index > 0) basename = name.substring(0, index);
|
||||
String sel = null;
|
||||
int count = menu.getItemCount();
|
||||
for (int i=0; i < count; i++) {
|
||||
JMenuItem item = menu.getItem(i);
|
||||
if (item != null && item.isSelected()) {
|
||||
sel = item.getText();
|
||||
if (sel != null) break;
|
||||
}
|
||||
}
|
||||
|
||||
JMenuItem item = getSelectedItemRecursive(menu);
|
||||
String sel = item != null ? item.getText() : null;
|
||||
if (sel == null) {
|
||||
if (!name.equals(basename)) menu.setText(basename);
|
||||
} else {
|
||||
@ -894,11 +886,9 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
String title = tool.getMenuTitle();
|
||||
JMenuItem item = new JMenuItem(title);
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
item.addActionListener(event -> {
|
||||
SwingUtilities.invokeLater(tool);
|
||||
//new Thread(tool).start();
|
||||
}
|
||||
});
|
||||
//menu.add(item);
|
||||
toolItems.put(title, item);
|
||||
@ -963,18 +953,13 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
JMenuItem createToolMenuItem(String className) {
|
||||
try {
|
||||
Class<?> toolClass = Class.forName(className);
|
||||
final Tool tool = (Tool) toolClass.newInstance();
|
||||
final Tool tool = getOrCreateToolInstance(className);
|
||||
|
||||
JMenuItem item = new JMenuItem(tool.getMenuTitle());
|
||||
|
||||
tool.init(Editor.this);
|
||||
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
SwingUtilities.invokeLater(tool);
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> SwingUtilities.invokeLater(tool));
|
||||
return item;
|
||||
|
||||
} catch (Exception e) {
|
||||
@ -983,6 +968,20 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
}
|
||||
}
|
||||
|
||||
private Tool getOrCreateToolInstance(String className) {
|
||||
Tool internalTool = internalToolCache.get(className);
|
||||
if (internalTool == null) {
|
||||
try {
|
||||
Class<?> toolClass = Class.forName(className);
|
||||
internalTool = (Tool) toolClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
internalToolCache.put(className, internalTool);
|
||||
}
|
||||
return internalTool;
|
||||
}
|
||||
|
||||
private void addInternalTools(JMenu menu) {
|
||||
JMenuItem item;
|
||||
@ -1003,21 +1002,6 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
}
|
||||
|
||||
|
||||
class SerialMenuListener implements ActionListener {
|
||||
|
||||
private final String serialPort;
|
||||
|
||||
public SerialMenuListener(String serialPort) {
|
||||
this.serialPort = serialPort;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
selectSerialPort(serialPort);
|
||||
base.onBoardOrPortChange();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void selectSerialPort(String name) {
|
||||
if(portMenu == null) {
|
||||
System.out.println(tr("serialMenu is null"));
|
||||
@ -1041,23 +1025,21 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
//System.out.println(item.getLabel());
|
||||
|
||||
BaseNoGui.selectSerialPort(name);
|
||||
try {
|
||||
boolean reopenMonitor = ((serialMonitor != null && serialMonitor.isVisible()) ||
|
||||
serialPlotter != null && serialPlotter.isVisible());
|
||||
if (serialMonitor != null) {
|
||||
try {
|
||||
serialMonitor.close();
|
||||
serialMonitor.setVisible(false);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (serialPlotter != null) {
|
||||
try {
|
||||
serialPlotter.close();
|
||||
serialPlotter.setVisible(false);
|
||||
}
|
||||
if (reopenMonitor) {
|
||||
handleSerial();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
onBoardOrPortChange();
|
||||
base.onBoardOrPortChange();
|
||||
@ -1065,8 +1047,34 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
//System.out.println("set to " + get("serial.port"));
|
||||
}
|
||||
|
||||
class BoardPortJCheckBoxMenuItem extends JCheckBoxMenuItem {
|
||||
private BoardPort port;
|
||||
|
||||
public BoardPortJCheckBoxMenuItem(BoardPort port) {
|
||||
super();
|
||||
this.port = port;
|
||||
setText(toString());
|
||||
addActionListener(e -> {
|
||||
selectSerialPort(port.getAddress());
|
||||
base.onBoardOrPortChange();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// This is required for serialPrompt()
|
||||
String label = port.getLabel();
|
||||
if (port.getBoardName() != null && !port.getBoardName().isEmpty()) {
|
||||
label += " (" + port.getBoardName() + ")";
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
private void populatePortMenu() {
|
||||
final List<String> PROTOCOLS_ORDER = Arrays.asList("serial", "network");
|
||||
final List<String> PROTOCOLS_LABELS = Arrays.asList(tr("Serial ports"), tr("Network ports"));
|
||||
|
||||
portMenu.removeAll();
|
||||
|
||||
String selectedPort = PreferencesData.get("serial.port");
|
||||
@ -1075,36 +1083,48 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
ports = platform.filterPorts(ports, PreferencesData.getBoolean("serial.ports.showall"));
|
||||
|
||||
Collections.sort(ports, new Comparator<BoardPort>() {
|
||||
@Override
|
||||
public int compare(BoardPort o1, BoardPort o2) {
|
||||
return BOARD_PROTOCOLS_ORDER.indexOf(o1.getProtocol()) - BOARD_PROTOCOLS_ORDER.indexOf(o2.getProtocol());
|
||||
ports.stream() //
|
||||
.filter(port -> port.getProtocolLabel() == null || port.getProtocolLabel().isEmpty())
|
||||
.forEach(port -> {
|
||||
int labelIdx = PROTOCOLS_ORDER.indexOf(port.getProtocol());
|
||||
if (labelIdx != -1) {
|
||||
port.setProtocolLabel(PROTOCOLS_LABELS.get(labelIdx));
|
||||
} else {
|
||||
port.setProtocolLabel(port.getProtocol());
|
||||
}
|
||||
});
|
||||
|
||||
String lastProtocol = null;
|
||||
String lastProtocolTranslated;
|
||||
Collections.sort(ports, (port1, port2) -> {
|
||||
String pr1 = port1.getProtocol();
|
||||
String pr2 = port2.getProtocol();
|
||||
int prIdx1 = PROTOCOLS_ORDER.contains(pr1) ? PROTOCOLS_ORDER.indexOf(pr1) : 999;
|
||||
int prIdx2 = PROTOCOLS_ORDER.contains(pr2) ? PROTOCOLS_ORDER.indexOf(pr2) : 999;
|
||||
int r = prIdx1 - prIdx2;
|
||||
if (r != 0)
|
||||
return r;
|
||||
r = port1.getProtocolLabel().compareTo(port2.getProtocolLabel());
|
||||
if (r != 0)
|
||||
return r;
|
||||
return port1.getAddress().compareTo(port2.getAddress());
|
||||
});
|
||||
|
||||
String lastProtocol = "";
|
||||
String lastProtocolLabel = "";
|
||||
for (BoardPort port : ports) {
|
||||
if (lastProtocol == null || !port.getProtocol().equals(lastProtocol)) {
|
||||
if (lastProtocol != null) {
|
||||
if (!port.getProtocol().equals(lastProtocol) || !port.getProtocolLabel().equals(lastProtocolLabel)) {
|
||||
if (!lastProtocol.isEmpty()) {
|
||||
portMenu.addSeparator();
|
||||
}
|
||||
lastProtocol = port.getProtocol();
|
||||
|
||||
if (BOARD_PROTOCOLS_ORDER.indexOf(port.getProtocol()) != -1) {
|
||||
lastProtocolTranslated = BOARD_PROTOCOLS_ORDER_TRANSLATIONS.get(BOARD_PROTOCOLS_ORDER.indexOf(port.getProtocol()));
|
||||
} else {
|
||||
lastProtocolTranslated = port.getProtocol();
|
||||
}
|
||||
JMenuItem lastProtocolMenuItem = new JMenuItem(tr(lastProtocolTranslated));
|
||||
lastProtocolMenuItem.setEnabled(false);
|
||||
portMenu.add(lastProtocolMenuItem);
|
||||
lastProtocolLabel = port.getProtocolLabel();
|
||||
JMenuItem item = new JMenuItem(tr(lastProtocolLabel));
|
||||
item.setEnabled(false);
|
||||
portMenu.add(item);
|
||||
}
|
||||
String address = port.getAddress();
|
||||
String label = port.getLabel();
|
||||
|
||||
JCheckBoxMenuItem item = new JCheckBoxMenuItem(label, address.equals(selectedPort));
|
||||
item.addActionListener(new SerialMenuListener(address));
|
||||
BoardPortJCheckBoxMenuItem item = new BoardPortJCheckBoxMenuItem(port);
|
||||
item.setSelected(address.equals(selectedPort));
|
||||
portMenu.add(item);
|
||||
}
|
||||
|
||||
@ -1113,153 +1133,44 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
|
||||
private JMenu buildHelpMenu() {
|
||||
// To deal with a Mac OS X 10.5 bug, add an extra space after the name
|
||||
// so that the OS doesn't try to insert its slow help menu.
|
||||
JMenu menu = new JMenu(tr("Help"));
|
||||
menu.setMnemonic(KeyEvent.VK_H);
|
||||
JMenuItem item;
|
||||
|
||||
/*
|
||||
// testing internal web server to serve up docs from a zip file
|
||||
item = new JMenuItem("Web Server Test");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
//WebServer ws = new WebServer();
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
int port = WebServer.launch("/Users/fry/coconut/processing/build/shared/reference.zip");
|
||||
Base.openURL("http://127.0.0.1:" + port + "/reference/setup_.html");
|
||||
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
*/
|
||||
|
||||
/*
|
||||
item = new JMenuItem("Browser Test");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
//Base.openURL("http://processing.org/learning/gettingstarted/");
|
||||
//JFrame browserFrame = new JFrame("Browser");
|
||||
BrowserStartup bs = new BrowserStartup("jar:file:/Users/fry/coconut/processing/build/shared/reference.zip!/reference/setup_.html");
|
||||
bs.initUI();
|
||||
bs.launch();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
*/
|
||||
|
||||
item = new JMenuItem(tr("Getting Started"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showArduinoGettingStarted();
|
||||
}
|
||||
});
|
||||
JMenuItem item = new JMenuItem(tr("Getting Started"));
|
||||
item.addActionListener(event -> Base.showArduinoGettingStarted());
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(tr("Environment"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showEnvironment();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> Base.showEnvironment());
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(tr("Troubleshooting"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showTroubleshooting();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> Base.showTroubleshooting());
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(tr("Reference"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showReference();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
item = new JMenuItem(tr("Galileo Help"));
|
||||
item.setEnabled(false);
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(tr("Getting Started"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showReference("reference/Galileo_help_files", "ArduinoIDE_guide_galileo");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
item = new JMenuItem(tr("Troubleshooting"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showReference("reference/Galileo_help_files", "Guide_Troubleshooting_Galileo");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
item = new JMenuItem(tr("Edison Help"));
|
||||
item.setEnabled(false);
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(tr("Getting Started"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showReference("reference/Edison_help_files", "ArduinoIDE_guide_edison");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
item = new JMenuItem(tr("Troubleshooting"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showReference("reference/Edison_help_files", "Guide_Troubleshooting_Edison");
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> Base.showReference());
|
||||
menu.add(item);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
item = newJMenuItemShift(tr("Find in Reference"), 'F');
|
||||
item.addActionListener(this::handleFindReference);
|
||||
item.addActionListener(event -> handleFindReference(event));
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(tr("Frequently Asked Questions"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.showFAQ();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> Base.showFAQ());
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem(tr("Visit Arduino.cc"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openURL(tr("http://www.arduino.cc/"));
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> Base.openURL(tr("http://www.arduino.cc/")));
|
||||
menu.add(item);
|
||||
|
||||
// macosx already has its own about menu
|
||||
if (!OSUtils.hasMacOSStyleMenus()) {
|
||||
menu.addSeparator();
|
||||
item = new JMenuItem(tr("About Arduino"));
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
base.handleAbout();
|
||||
}
|
||||
});
|
||||
item.addActionListener(event -> base.handleAbout());
|
||||
menu.add(item);
|
||||
}
|
||||
|
||||
@ -1274,7 +1185,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
undoItem = newJMenuItem(tr("Undo"), 'Z');
|
||||
undoItem.setName("menuEditUndo");
|
||||
undoItem.addActionListener(e -> getCurrentTab().handleUndo());
|
||||
undoItem.addActionListener(event -> getCurrentTab().handleUndo());
|
||||
menu.add(undoItem);
|
||||
|
||||
if (!OSUtils.isMacOS()) {
|
||||
@ -1283,61 +1194,37 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
redoItem = newJMenuItemShift(tr("Redo"), 'Z');
|
||||
}
|
||||
redoItem.setName("menuEditRedo");
|
||||
redoItem.addActionListener(e -> getCurrentTab().handleRedo());
|
||||
redoItem.addActionListener(event -> getCurrentTab().handleRedo());
|
||||
menu.add(redoItem);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
JMenuItem cutItem = newJMenuItem(tr("Cut"), 'X');
|
||||
cutItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().handleCut();
|
||||
}
|
||||
});
|
||||
cutItem.addActionListener(event -> getCurrentTab().handleCut());
|
||||
menu.add(cutItem);
|
||||
|
||||
JMenuItem copyItem = newJMenuItem(tr("Copy"), 'C');
|
||||
copyItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().getTextArea().copy();
|
||||
}
|
||||
});
|
||||
copyItem.addActionListener(event -> getCurrentTab().getTextArea().copy());
|
||||
menu.add(copyItem);
|
||||
|
||||
JMenuItem copyForumItem = newJMenuItemShift(tr("Copy for Forum"), 'C');
|
||||
copyForumItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().handleDiscourseCopy();
|
||||
}
|
||||
});
|
||||
copyForumItem.addActionListener(event -> getCurrentTab().handleDiscourseCopy());
|
||||
menu.add(copyForumItem);
|
||||
|
||||
JMenuItem copyHTMLItem = newJMenuItemAlt(tr("Copy as HTML"), 'C');
|
||||
copyHTMLItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().handleHTMLCopy();
|
||||
}
|
||||
});
|
||||
copyHTMLItem.addActionListener(event -> getCurrentTab().handleHTMLCopy());
|
||||
menu.add(copyHTMLItem);
|
||||
|
||||
JMenuItem pasteItem = newJMenuItem(tr("Paste"), 'V');
|
||||
pasteItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().handlePaste();
|
||||
}
|
||||
});
|
||||
pasteItem.addActionListener(event -> getCurrentTab().handlePaste());
|
||||
menu.add(pasteItem);
|
||||
|
||||
JMenuItem selectAllItem = newJMenuItem(tr("Select All"), 'A');
|
||||
selectAllItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().handleSelectAll();
|
||||
}
|
||||
});
|
||||
selectAllItem.addActionListener(event -> getCurrentTab().handleSelectAll());
|
||||
menu.add(selectAllItem);
|
||||
|
||||
JMenuItem gotoLine = newJMenuItem(tr("Go to line..."), 'L');
|
||||
gotoLine.addActionListener(e -> {
|
||||
gotoLine.addActionListener(event -> {
|
||||
GoToLineNumber goToLineNumber = new GoToLineNumber(Editor.this);
|
||||
goToLineNumber.setLocationRelativeTo(Editor.this);
|
||||
goToLineNumber.setVisible(true);
|
||||
@ -1347,55 +1234,46 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
menu.addSeparator();
|
||||
|
||||
JMenuItem commentItem = newJMenuItem(tr("Comment/Uncomment"), PreferencesData.get("editor.keys.shortcut_comment", "/").charAt(0));
|
||||
commentItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().handleCommentUncomment();
|
||||
}
|
||||
});
|
||||
commentItem.addActionListener(event -> getCurrentTab().handleCommentUncomment());
|
||||
menu.add(commentItem);
|
||||
|
||||
JMenuItem increaseIndentItem = new JMenuItem(tr("Increase Indent"));
|
||||
increaseIndentItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
|
||||
increaseIndentItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().handleIndentOutdent(true);
|
||||
}
|
||||
});
|
||||
increaseIndentItem.addActionListener(event -> getCurrentTab().handleIndentOutdent(true));
|
||||
menu.add(increaseIndentItem);
|
||||
|
||||
JMenuItem decreseIndentItem = new JMenuItem(tr("Decrease Indent"));
|
||||
decreseIndentItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
|
||||
decreseIndentItem.setName("menuDecreaseIndent");
|
||||
decreseIndentItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
getCurrentTab().handleIndentOutdent(false);
|
||||
}
|
||||
});
|
||||
decreseIndentItem.addActionListener(event -> getCurrentTab().handleIndentOutdent(false));
|
||||
menu.add(decreseIndentItem);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
JMenuItem increaseFontSizeItem = newJMenuItem(tr("Increase Font Size"), '+');
|
||||
increaseFontSizeItem.addActionListener(new ActionListener() {
|
||||
JMenuItem increaseFontSizeItem = newJMenuItem(tr("Increase Font Size"), KeyEvent.VK_PLUS);
|
||||
increaseFontSizeItem.addActionListener(event -> base.handleFontSizeChange(1));
|
||||
menu.add(increaseFontSizeItem);
|
||||
// Many keyboards have '+' and '=' on the same key. Allowing "CTRL +",
|
||||
// "CTRL SHIFT +" and "CTRL =" covers the generally expected behavior.
|
||||
KeyStroke ctrlShiftEq = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, SHORTCUT_KEY_MASK | ActionEvent.SHIFT_MASK);
|
||||
menu.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ctrlShiftEq, "IncreaseFontSize");
|
||||
KeyStroke ctrlEq = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, SHORTCUT_KEY_MASK);
|
||||
menu.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ctrlEq, "IncreaseFontSize");
|
||||
menu.getActionMap().put("IncreaseFontSize", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
base.handleFontSizeChange(1);
|
||||
}
|
||||
});
|
||||
menu.add(increaseFontSizeItem);
|
||||
|
||||
JMenuItem decreaseFontSizeItem = newJMenuItem(tr("Decrease Font Size"), '-');
|
||||
decreaseFontSizeItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
base.handleFontSizeChange(-1);
|
||||
}
|
||||
});
|
||||
JMenuItem decreaseFontSizeItem = newJMenuItem(tr("Decrease Font Size"), KeyEvent.VK_MINUS);
|
||||
decreaseFontSizeItem.addActionListener(event -> base.handleFontSizeChange(-1));
|
||||
menu.add(decreaseFontSizeItem);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
JMenuItem findItem = newJMenuItem(tr("Find..."), 'F');
|
||||
findItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
findItem.addActionListener(event -> {
|
||||
if (find == null) {
|
||||
find = new FindReplace(Editor.this, Base.FIND_DIALOG_STATE);
|
||||
}
|
||||
@ -1404,39 +1282,32 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
}
|
||||
find.setLocationRelativeTo(Editor.this);
|
||||
find.setVisible(true);
|
||||
}
|
||||
});
|
||||
menu.add(findItem);
|
||||
|
||||
JMenuItem findNextItem = newJMenuItem(tr("Find Next"), 'G');
|
||||
findNextItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
findNextItem.addActionListener(event -> {
|
||||
if (find != null) {
|
||||
find.findNext();
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(findNextItem);
|
||||
|
||||
JMenuItem findPreviousItem = newJMenuItemShift(tr("Find Previous"), 'G');
|
||||
findPreviousItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
findPreviousItem.addActionListener(event -> {
|
||||
if (find != null) {
|
||||
find.findPrevious();
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(findPreviousItem);
|
||||
|
||||
if (OSUtils.isMacOS()) {
|
||||
JMenuItem useSelectionForFindItem = newJMenuItem(tr("Use Selection For Find"), 'E');
|
||||
useSelectionForFindItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
useSelectionForFindItem.addActionListener(event -> {
|
||||
if (find == null) {
|
||||
find = new FindReplace(Editor.this, Base.FIND_DIALOG_STATE);
|
||||
}
|
||||
find.setFindText(getCurrentTab().getSelectedText());
|
||||
}
|
||||
});
|
||||
menu.add(useSelectionForFindItem);
|
||||
}
|
||||
@ -1519,8 +1390,10 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
presentHandler = new BuildHandler(true);
|
||||
runAndSaveHandler = new BuildHandler(false, true);
|
||||
presentAndSaveHandler = new BuildHandler(true, true);
|
||||
exportHandler = new DefaultExportHandler();
|
||||
exportAppHandler = new DefaultExportAppHandler();
|
||||
uploadHandler = new UploadHandler();
|
||||
uploadHandler.setUsingProgrammer(false);
|
||||
uploadUsingProgrammerHandler = new UploadHandler();
|
||||
uploadUsingProgrammerHandler.setUsingProgrammer(true);
|
||||
timeoutUploadHandler = new TimeoutUploadHandler();
|
||||
}
|
||||
|
||||
@ -1779,8 +1652,17 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
}
|
||||
|
||||
public void addLineHighlight(int line) throws BadLocationException {
|
||||
getCurrentTab().getTextArea().addLineHighlight(line, new Color(1, 0, 0, 0.2f));
|
||||
getCurrentTab().getTextArea().setCaretPosition(getCurrentTab().getTextArea().getLineStartOffset(line));
|
||||
SketchTextArea textArea = getCurrentTab().getTextArea();
|
||||
FoldManager foldManager = textArea.getFoldManager();
|
||||
if (foldManager.isLineHidden(line)) {
|
||||
for (int i = 0; i < foldManager.getFoldCount(); i++) {
|
||||
if (foldManager.getFold(i).containsLine(line)) {
|
||||
foldManager.getFold(i).setCollapsed(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
textArea.addLineHighlight(line, new Color(1, 0, 0, 0.2f));
|
||||
textArea.setCaretPosition(textArea.getLineStartOffset(line));
|
||||
}
|
||||
|
||||
|
||||
@ -1951,7 +1833,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateTitle() {
|
||||
public void updateTitle() {
|
||||
if (sketchController == null) {
|
||||
return;
|
||||
}
|
||||
@ -2016,7 +1898,12 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
statusNotice(tr("Saving..."));
|
||||
boolean saved = false;
|
||||
try {
|
||||
boolean wasReadOnly = sketchController.isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath());
|
||||
if (PreferencesData.getBoolean("editor.autoformat_currentfile_before_saving")) {
|
||||
Tool formatTool = getOrCreateToolInstance("cc.arduino.packages.formatter.AStyle");
|
||||
formatTool.run();
|
||||
}
|
||||
|
||||
boolean wasReadOnly = sketchController.isReadOnly();
|
||||
String previousMainFilePath = sketch.getMainFilePath();
|
||||
saved = sketchController.save();
|
||||
if (saved) {
|
||||
@ -2091,32 +1978,30 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
|
||||
|
||||
private boolean serialPrompt() {
|
||||
int count = portMenu.getItemCount();
|
||||
Object[] names = new Object[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
names[i] = portMenu.getItem(i).getText();
|
||||
List<BoardPortJCheckBoxMenuItem> items = new ArrayList<>();
|
||||
for (int i = 0; i < portMenu.getItemCount(); i++) {
|
||||
if (portMenu.getItem(i) instanceof BoardPortJCheckBoxMenuItem)
|
||||
items.add((BoardPortJCheckBoxMenuItem) portMenu.getItem(i));
|
||||
}
|
||||
|
||||
// FIXME: This is horribly unreadable
|
||||
String result = (String)
|
||||
JOptionPane.showInputDialog(this,
|
||||
I18n.format(
|
||||
tr("Serial port {0} not found.\n" +
|
||||
"Retry the upload with another serial port?"),
|
||||
PreferencesData.get("serial.port")
|
||||
),
|
||||
"Serial port not found",
|
||||
JOptionPane.PLAIN_MESSAGE,
|
||||
null,
|
||||
names,
|
||||
0);
|
||||
if (result == null) return false;
|
||||
selectSerialPort(result);
|
||||
String port = PreferencesData.get("serial.port");
|
||||
String title;
|
||||
if (port == null || port.isEmpty()) {
|
||||
title = tr("Serial port not selected.");
|
||||
} else {
|
||||
title = I18n.format(tr("Serial port {0} not found."), port);
|
||||
}
|
||||
String question = tr("Retry the upload with another serial port?");
|
||||
BoardPortJCheckBoxMenuItem result = (BoardPortJCheckBoxMenuItem) JOptionPane
|
||||
.showInputDialog(this, title + "\n" + question, title,
|
||||
JOptionPane.PLAIN_MESSAGE, null, items.toArray(), 0);
|
||||
if (result == null)
|
||||
return false;
|
||||
result.doClick();
|
||||
base.onBoardOrPortChange();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by Sketch → Export.
|
||||
* Handles calling the export() function on sketch, and
|
||||
@ -2134,11 +2019,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
*/
|
||||
synchronized public void handleExport(final boolean usingProgrammer) {
|
||||
if (PreferencesData.getBoolean("editor.save_on_verify")) {
|
||||
if (sketch.isModified()
|
||||
&& !sketchController.isReadOnly(
|
||||
BaseNoGui.librariesIndexer
|
||||
.getInstalledLibraries(),
|
||||
BaseNoGui.getExamplesPath())) {
|
||||
if (sketch.isModified() && !sketchController.isReadOnly()) {
|
||||
handleSave(true);
|
||||
}
|
||||
}
|
||||
@ -2149,14 +2030,20 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
avoidMultipleOperations = true;
|
||||
|
||||
new Thread(timeoutUploadHandler).start();
|
||||
new Thread(usingProgrammer ? exportAppHandler : exportHandler).start();
|
||||
new Thread(usingProgrammer ? uploadUsingProgrammerHandler : uploadHandler).start();
|
||||
}
|
||||
|
||||
// DAM: in Arduino, this is upload
|
||||
class DefaultExportHandler implements Runnable {
|
||||
public void run() {
|
||||
class UploadHandler implements Runnable {
|
||||
boolean usingProgrammer = false;
|
||||
|
||||
public void setUsingProgrammer(boolean usingProgrammer) {
|
||||
this.usingProgrammer = usingProgrammer;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
uploading = true;
|
||||
|
||||
removeAllLineHighlights();
|
||||
if (serialMonitor != null) {
|
||||
serialMonitor.suspend();
|
||||
@ -2165,16 +2052,20 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
serialPlotter.suspend();
|
||||
}
|
||||
|
||||
uploading = true;
|
||||
|
||||
boolean success = sketchController.exportApplet(false);
|
||||
boolean success = sketchController.exportApplet(usingProgrammer);
|
||||
if (success) {
|
||||
statusNotice(tr("Done uploading."));
|
||||
}
|
||||
} catch (SerialNotFoundException e) {
|
||||
if (portMenu.getItemCount() == 0) statusError(e);
|
||||
else if (serialPrompt()) run();
|
||||
else statusNotice(tr("Upload canceled."));
|
||||
if (portMenu.getItemCount() == 0) {
|
||||
statusError(tr("Serial port not selected."));
|
||||
} else {
|
||||
if (serialPrompt()) {
|
||||
run();
|
||||
} else {
|
||||
statusNotice(tr("Upload canceled."));
|
||||
}
|
||||
}
|
||||
} catch (PreferencesMapException e) {
|
||||
statusError(I18n.format(
|
||||
tr("Error while uploading: missing '{0}' configuration parameter"),
|
||||
@ -2201,9 +2092,18 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
}
|
||||
}
|
||||
|
||||
static public boolean isUploading() {
|
||||
return uploading;
|
||||
}
|
||||
|
||||
private void resumeOrCloseSerialMonitor() {
|
||||
// Return the serial monitor window to its initial state
|
||||
if (serialMonitor != null) {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
} catch (InterruptedException e) {
|
||||
// noop
|
||||
}
|
||||
BoardPort boardPort = BaseNoGui.getDiscoveryManager().find(PreferencesData.get("serial.port"));
|
||||
long sleptFor = 0;
|
||||
while (boardPort == null && sleptFor < MAX_TIME_AWAITING_FOR_RESUMING_SERIAL_MONITOR) {
|
||||
@ -2250,55 +2150,6 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
}
|
||||
}
|
||||
|
||||
// DAM: in Arduino, this is upload (with verbose output)
|
||||
class DefaultExportAppHandler implements Runnable {
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
if (serialMonitor != null) {
|
||||
serialMonitor.suspend();
|
||||
}
|
||||
if (serialPlotter != null) {
|
||||
serialPlotter.suspend();
|
||||
}
|
||||
|
||||
uploading = true;
|
||||
|
||||
boolean success = sketchController.exportApplet(true);
|
||||
if (success) {
|
||||
statusNotice(tr("Done uploading."));
|
||||
}
|
||||
} catch (SerialNotFoundException e) {
|
||||
if (portMenu.getItemCount() == 0) statusError(e);
|
||||
else if (serialPrompt()) run();
|
||||
else statusNotice(tr("Upload canceled."));
|
||||
} catch (PreferencesMapException e) {
|
||||
statusError(I18n.format(
|
||||
tr("Error while uploading: missing '{0}' configuration parameter"),
|
||||
e.getMessage()));
|
||||
} catch (RunnerException e) {
|
||||
//statusError("Error during upload.");
|
||||
//e.printStackTrace();
|
||||
status.unprogress();
|
||||
statusError(e);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
avoidMultipleOperations = false;
|
||||
populatePortMenu();
|
||||
}
|
||||
status.unprogress();
|
||||
uploading = false;
|
||||
//toolbar.clear();
|
||||
toolbar.deactivateExport();
|
||||
|
||||
resumeOrCloseSerialMonitor();
|
||||
resumeOrCloseSerialPlotter();
|
||||
|
||||
base.onBoardOrPortChange();
|
||||
}
|
||||
}
|
||||
|
||||
class TimeoutUploadHandler implements Runnable {
|
||||
|
||||
public void run() {
|
||||
@ -2361,6 +2212,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
return;
|
||||
}
|
||||
|
||||
base.addEditorFontResizeListeners(serialMonitor);
|
||||
Base.setIcon(serialMonitor);
|
||||
|
||||
// If currently uploading, disable the monitor (it will be later
|
||||
@ -2389,11 +2241,12 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
}
|
||||
|
||||
try {
|
||||
serialMonitor.setVisible(true);
|
||||
if (!avoidMultipleOperations) {
|
||||
serialMonitor.open();
|
||||
}
|
||||
serialMonitor.setVisible(true);
|
||||
success = true;
|
||||
statusEmpty();
|
||||
} catch (ConnectException e) {
|
||||
statusError(tr("Unable to connect: is the sketch using the bridge?"));
|
||||
} catch (JSchException e) {
|
||||
@ -2403,6 +2256,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
if (e.getCause() != null && e.getCause() instanceof SerialPortException) {
|
||||
errorMessage += " (" + ((SerialPortException) e.getCause()).getExceptionType() + ")";
|
||||
}
|
||||
serialMonitor = null;
|
||||
statusError(errorMessage);
|
||||
try {
|
||||
serialMonitor.close();
|
||||
@ -2412,12 +2266,12 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
} catch (Exception e) {
|
||||
statusError(e);
|
||||
} finally {
|
||||
if (serialMonitor.requiresAuthorization() && !success) {
|
||||
if (serialMonitor != null && serialMonitor.requiresAuthorization() && !success) {
|
||||
PreferencesData.remove(serialMonitor.getAuthorizationKey());
|
||||
}
|
||||
}
|
||||
|
||||
} while (serialMonitor.requiresAuthorization() && !success);
|
||||
} while (serialMonitor != null && serialMonitor.requiresAuthorization() && !success);
|
||||
|
||||
}
|
||||
|
||||
@ -2437,6 +2291,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
if (serialPlotter.isClosed()) {
|
||||
// If it's closed, clear the refrence to the existing
|
||||
// plotter and create a new one
|
||||
serialPlotter.dispose();
|
||||
serialPlotter = null;
|
||||
}
|
||||
else {
|
||||
@ -2490,6 +2345,7 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
serialPlotter.open();
|
||||
serialPlotter.setVisible(true);
|
||||
success = true;
|
||||
statusEmpty();
|
||||
} catch (ConnectException e) {
|
||||
statusError(tr("Unable to connect: is the sketch using the bridge?"));
|
||||
} catch (JSchException e) {
|
||||
@ -2500,20 +2356,22 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
errorMessage += " (" + ((SerialPortException) e.getCause()).getExceptionType() + ")";
|
||||
}
|
||||
statusError(errorMessage);
|
||||
serialPlotter = null;
|
||||
} catch (Exception e) {
|
||||
statusError(e);
|
||||
} finally {
|
||||
if (serialPlotter.requiresAuthorization() && !success) {
|
||||
if (serialPlotter != null && serialPlotter.requiresAuthorization() && !success) {
|
||||
PreferencesData.remove(serialPlotter.getAuthorizationKey());
|
||||
}
|
||||
}
|
||||
|
||||
} while (serialPlotter.requiresAuthorization() && !success);
|
||||
} while (serialPlotter != null && serialPlotter.requiresAuthorization() && !success);
|
||||
|
||||
}
|
||||
|
||||
private void handleBurnBootloader() {
|
||||
console.clear();
|
||||
EditorConsole.setCurrentEditorConsole(this.console);
|
||||
statusNotice(tr("Burning bootloader to I/O Board (this may take a minute)..."));
|
||||
new Thread(() -> {
|
||||
try {
|
||||
@ -2524,6 +2382,8 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
SwingUtilities.invokeLater(() -> statusError(tr("Error while burning bootloader.")));
|
||||
// error message will already be visible
|
||||
}
|
||||
} catch (SerialNotFoundException e) {
|
||||
SwingUtilities.invokeLater(() -> statusError(tr("Error while burning bootloader: please select a serial port.")));
|
||||
} catch (PreferencesMapException e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusError(I18n.format(
|
||||
@ -2555,9 +2415,9 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
for (BoardPort port : ports) {
|
||||
if (port.getAddress().equals(selectedPort)) {
|
||||
label = port.getBoardName();
|
||||
vid = port.getVID();
|
||||
pid = port.getPID();
|
||||
iserial = port.getISerial();
|
||||
vid = port.getPrefs().get("vid");
|
||||
pid = port.getPrefs().get("pid");
|
||||
iserial = port.getPrefs().get("iserial");
|
||||
protocol = port.getProtocol();
|
||||
found = true;
|
||||
break;
|
||||
@ -2727,12 +2587,12 @@ public class Editor extends JFrame implements RunnerListener {
|
||||
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
|
||||
protected void onBoardOrPortChange() {
|
||||
Map<String, String> boardPreferences = BaseNoGui.getBoardPreferences();
|
||||
if (boardPreferences != null)
|
||||
lineStatus.setBoardName(boardPreferences.get("name"));
|
||||
TargetBoard board = BaseNoGui.getTargetBoard();
|
||||
if (board != null)
|
||||
lineStatus.setBoardName(board.getName());
|
||||
else
|
||||
lineStatus.setBoardName("-");
|
||||
lineStatus.setSerialPort(PreferencesData.get("serial.port"));
|
||||
lineStatus.setPort(PreferencesData.get("serial.port"));
|
||||
lineStatus.repaint();
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,10 @@ public class EditorConsole extends JScrollPane {
|
||||
private final DefaultStyledDocument document;
|
||||
private final JTextPane consoleTextPane;
|
||||
|
||||
public EditorConsole() {
|
||||
private SimpleAttributeSet stdOutStyle;
|
||||
private SimpleAttributeSet stdErrStyle;
|
||||
|
||||
public EditorConsole(Base base) {
|
||||
document = new DefaultStyledDocument();
|
||||
|
||||
consoleTextPane = new JTextPane(document);
|
||||
@ -74,7 +77,7 @@ public class EditorConsole extends JScrollPane {
|
||||
Font editorFont = PreferencesData.getFont("editor.font");
|
||||
Font actualFont = new Font(consoleFont.getName(), consoleFont.getStyle(), scale(editorFont.getSize()));
|
||||
|
||||
SimpleAttributeSet stdOutStyle = new SimpleAttributeSet();
|
||||
stdOutStyle = new SimpleAttributeSet();
|
||||
StyleConstants.setForeground(stdOutStyle, Theme.getColor("console.output.color"));
|
||||
StyleConstants.setBackground(stdOutStyle, backgroundColour);
|
||||
StyleConstants.setFontSize(stdOutStyle, actualFont.getSize());
|
||||
@ -84,7 +87,7 @@ public class EditorConsole extends JScrollPane {
|
||||
|
||||
consoleTextPane.setParagraphAttributes(stdOutStyle, true);
|
||||
|
||||
SimpleAttributeSet stdErrStyle = new SimpleAttributeSet();
|
||||
stdErrStyle = new SimpleAttributeSet();
|
||||
StyleConstants.setForeground(stdErrStyle, Theme.getColor("console.error.color"));
|
||||
StyleConstants.setBackground(stdErrStyle, backgroundColour);
|
||||
StyleConstants.setFontSize(stdErrStyle, actualFont.getSize());
|
||||
@ -107,6 +110,55 @@ public class EditorConsole extends JScrollPane {
|
||||
setMinimumSize(new Dimension(100, (height * lines)));
|
||||
|
||||
EditorConsole.init(stdOutStyle, System.out, stdErrStyle, System.err);
|
||||
|
||||
// Add font size adjustment listeners.
|
||||
base.addEditorFontResizeListeners(consoleTextPane);
|
||||
}
|
||||
|
||||
public void applyPreferences() {
|
||||
|
||||
// Update the console text pane font from the preferences.
|
||||
Font consoleFont = Theme.getFont("console.font");
|
||||
Font editorFont = PreferencesData.getFont("editor.font");
|
||||
Font actualFont = new Font(consoleFont.getName(), consoleFont.getStyle(), scale(editorFont.getSize()));
|
||||
|
||||
AttributeSet stdOutStyleOld = stdOutStyle.copyAttributes();
|
||||
AttributeSet stdErrStyleOld = stdErrStyle.copyAttributes();
|
||||
StyleConstants.setFontSize(stdOutStyle, actualFont.getSize());
|
||||
StyleConstants.setFontSize(stdErrStyle, actualFont.getSize());
|
||||
|
||||
// Re-insert console text with the new preferences if there were changes.
|
||||
// This assumes that the document has single-child paragraphs (default).
|
||||
if (!stdOutStyle.isEqual(stdOutStyleOld) || !stdErrStyle.isEqual(stdOutStyleOld)) {
|
||||
out.setAttibutes(stdOutStyle);
|
||||
err.setAttibutes(stdErrStyle);
|
||||
|
||||
int start;
|
||||
for (int end = document.getLength() - 1; end >= 0; end = start - 1) {
|
||||
Element elem = document.getParagraphElement(end);
|
||||
start = elem.getStartOffset();
|
||||
AttributeSet attrs = elem.getElement(0).getAttributes();
|
||||
AttributeSet newAttrs;
|
||||
if (attrs.isEqual(stdErrStyleOld)) {
|
||||
newAttrs = stdErrStyle;
|
||||
} else if (attrs.isEqual(stdOutStyleOld)) {
|
||||
newAttrs = stdOutStyle;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
String text = document.getText(start, end - start);
|
||||
document.remove(start, end - start);
|
||||
document.insertString(start, text, newAttrs);
|
||||
} catch (BadLocationException e) {
|
||||
// Should only happen when text is async removed (through clear()).
|
||||
// Accept this case, but throw an error when text could mess up.
|
||||
if (document.getLength() != 0) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
|
@ -51,8 +51,7 @@ public class EditorLineStatus extends JComponent {
|
||||
|
||||
String text = "";
|
||||
String name = "";
|
||||
String serialport = "";
|
||||
String serialnumber = "";
|
||||
String port = "";
|
||||
|
||||
public EditorLineStatus() {
|
||||
background = Theme.getColor("linestatus.bgcolor");
|
||||
@ -92,13 +91,13 @@ public class EditorLineStatus extends JComponent {
|
||||
|
||||
public void paintComponent(Graphics graphics) {
|
||||
Graphics2D g = Theme.setupGraphics2D(graphics);
|
||||
if (name.isEmpty() && serialport.isEmpty()) {
|
||||
if (name.isEmpty() && port.isEmpty()) {
|
||||
PreferencesMap boardPreferences = BaseNoGui.getBoardPreferences();
|
||||
if (boardPreferences != null)
|
||||
setBoardName(boardPreferences.get("name"));
|
||||
else
|
||||
setBoardName("-");
|
||||
setSerialPort(PreferencesData.get("serial.port"));
|
||||
setPort(PreferencesData.get("serial.port"));
|
||||
}
|
||||
g.setColor(background);
|
||||
Dimension size = getSize();
|
||||
@ -110,11 +109,17 @@ public class EditorLineStatus extends JComponent {
|
||||
g.drawString(text, scale(6), baseline);
|
||||
|
||||
g.setColor(messageForeground);
|
||||
String tmp = I18n.format(tr("{0} on {1}"), name, serialport);
|
||||
|
||||
Rectangle2D bounds = g.getFontMetrics().getStringBounds(tmp, null);
|
||||
String statusText;
|
||||
if (port != null && !port.isEmpty()) {
|
||||
statusText = I18n.format(tr("{0} on {1}"), name, port);
|
||||
} else {
|
||||
statusText = name;
|
||||
}
|
||||
|
||||
g.drawString(tmp, size.width - (int) bounds.getWidth() - RESIZE_IMAGE_SIZE,
|
||||
Rectangle2D bounds = g.getFontMetrics().getStringBounds(statusText, null);
|
||||
|
||||
g.drawString(statusText, size.width - (int) bounds.getWidth() - RESIZE_IMAGE_SIZE,
|
||||
baseline);
|
||||
|
||||
if (OSUtils.isMacOS()) {
|
||||
@ -126,12 +131,8 @@ public class EditorLineStatus extends JComponent {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setSerialPort(String serialport) {
|
||||
this.serialport = serialport;
|
||||
}
|
||||
|
||||
public void setSerialNumber(String serialnumber) {
|
||||
this.serialnumber = serialnumber;
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public Dimension getPreferredSize() {
|
||||
|
@ -30,9 +30,8 @@ import java.awt.BorderLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseWheelListener;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.Action;
|
||||
@ -50,6 +49,7 @@ import javax.swing.text.PlainDocument;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
import javax.swing.text.Document;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
|
||||
@ -67,7 +67,7 @@ import processing.app.tools.DiscourseFormat;
|
||||
/**
|
||||
* Single tab, editing a single file, in the main window.
|
||||
*/
|
||||
public class EditorTab extends JPanel implements SketchFile.TextStorage, MouseWheelListener {
|
||||
public class EditorTab extends JPanel implements SketchFile.TextStorage {
|
||||
protected Editor editor;
|
||||
protected SketchTextArea textarea;
|
||||
protected RTextScrollPane scrollPane;
|
||||
@ -109,7 +109,7 @@ public class EditorTab extends JPanel implements SketchFile.TextStorage, MouseWh
|
||||
file.setStorage(this);
|
||||
applyPreferences();
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
textarea.addMouseWheelListener(this);
|
||||
editor.base.addEditorFontResizeMouseWheelListener(textarea);
|
||||
}
|
||||
|
||||
private RSyntaxDocument createDocument(String contents) {
|
||||
@ -176,24 +176,21 @@ public class EditorTab extends JPanel implements SketchFile.TextStorage, MouseWh
|
||||
|
||||
editor.lineStatus.set(lineStart, lineEnd);
|
||||
});
|
||||
textArea.addFocusListener(new FocusListener() {
|
||||
public void focusGained(FocusEvent e) {
|
||||
Element root = textArea.getDocument().getDefaultRootElement();
|
||||
int lineStart = root.getElementIndex(textArea.getCaret().getMark());
|
||||
int lineEnd = root.getElementIndex(textArea.getCaret().getDot());
|
||||
editor.lineStatus.set(lineStart, lineEnd);
|
||||
};
|
||||
public void focusLost(FocusEvent e) {};
|
||||
});
|
||||
ToolTipManager.sharedInstance().registerComponent(textArea);
|
||||
|
||||
configurePopupMenu(textArea);
|
||||
return textArea;
|
||||
}
|
||||
|
||||
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||
if (e.isControlDown()) {
|
||||
if (e.getWheelRotation() < 0) {
|
||||
editor.base.handleFontSizeChange(1);
|
||||
} else {
|
||||
editor.base.handleFontSizeChange(-1);
|
||||
}
|
||||
} else {
|
||||
e.getComponent().getParent().dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void configurePopupMenu(final SketchTextArea textarea){
|
||||
|
||||
JPopupMenu menu = textarea.getPopupMenu();
|
||||
@ -319,6 +316,20 @@ public class EditorTab extends JPanel implements SketchFile.TextStorage, MouseWh
|
||||
}
|
||||
// apply changes to the font size for the editor
|
||||
Font editorFont = scale(PreferencesData.getFont("editor.font"));
|
||||
|
||||
// check whether a theme-defined editor font is available
|
||||
Font themeFont = Theme.getFont("editor.font");
|
||||
if (themeFont != null)
|
||||
{
|
||||
// Apply theme font if the editor font has *not* been changed by the user,
|
||||
// This allows themes to specify an editor font which will only be applied
|
||||
// if the user hasn't already changed their editor font via preferences.txt
|
||||
String defaultFontName = StringUtils.defaultIfEmpty(PreferencesData.getDefault("editor.font"), "").split(",")[0];
|
||||
if (defaultFontName.equals(editorFont.getName())) {
|
||||
editorFont = new Font(themeFont.getName(), themeFont.getStyle(), editorFont.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
textarea.setFont(editorFont);
|
||||
scrollPane.getGutter().setLineNumberFont(editorFont);
|
||||
}
|
||||
@ -431,6 +442,9 @@ public class EditorTab extends JPanel implements SketchFile.TextStorage, MouseWh
|
||||
} finally {
|
||||
caret.setUpdatePolicy(policy);
|
||||
}
|
||||
// A trick to force textarea to recalculate the bracket matching rectangle.
|
||||
// In the worst case scenario, this should be ineffective.
|
||||
textarea.setLineWrap(textarea.getLineWrap());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,11 +23,25 @@
|
||||
|
||||
package processing.app;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.MouseInputListener;
|
||||
|
||||
import com.thizzer.jtouchbar.JTouchBar;
|
||||
import com.thizzer.jtouchbar.item.TouchBarItem;
|
||||
import com.thizzer.jtouchbar.item.view.TouchBarButton;
|
||||
|
||||
import cc.arduino.contributions.VersionComparator;
|
||||
import processing.app.helpers.OSUtils;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
import static processing.app.Theme.scale;
|
||||
@ -92,10 +106,13 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
|
||||
private final Color bgcolor;
|
||||
|
||||
private static Image[][] buttonImages;
|
||||
private static com.thizzer.jtouchbar.common.Image[][] touchBarImages;
|
||||
private int currentRollover;
|
||||
|
||||
private JPopupMenu popup;
|
||||
private final JMenu menu;
|
||||
private JTouchBar touchBar;
|
||||
private TouchBarButton[] touchBarButtons;
|
||||
|
||||
private int buttonCount;
|
||||
private int[] state = new int[BUTTON_COUNT];
|
||||
@ -133,11 +150,61 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
|
||||
statusFont = Theme.getFont("buttons.status.font");
|
||||
statusColor = Theme.getColor("buttons.status.color");
|
||||
|
||||
if (OSUtils.isMacOS() && VersionComparator.greaterThanOrEqual(OSUtils.version(), "10.12")) {
|
||||
editor.addWindowListener(new WindowAdapter() {
|
||||
public void windowActivated(WindowEvent e) {
|
||||
if (touchBar == null) {
|
||||
buildTouchBar();
|
||||
|
||||
touchBar.show(editor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addMouseListener(this);
|
||||
addMouseMotionListener(this);
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
|
||||
}
|
||||
|
||||
private void buildTouchBar() {
|
||||
if (touchBarImages == null) {
|
||||
loadTouchBarImages();
|
||||
}
|
||||
|
||||
touchBar = new JTouchBar();
|
||||
touchBarButtons = new TouchBarButton[BUTTON_COUNT];
|
||||
touchBar.setCustomizationIdentifier("Arduino");
|
||||
|
||||
for (int i = 0; i < BUTTON_COUNT; i++) {
|
||||
final int selection = i;
|
||||
|
||||
// add spacers before NEW and SERIAL buttons
|
||||
if (i == NEW) {
|
||||
touchBar.addItem(new TouchBarItem(TouchBarItem.NSTouchBarItemIdentifierFixedSpaceSmall));
|
||||
} else if (i == SERIAL) {
|
||||
touchBar.addItem(new TouchBarItem(TouchBarItem.NSTouchBarItemIdentifierFlexibleSpace));
|
||||
}
|
||||
|
||||
touchBarButtons[i] = new TouchBarButton();
|
||||
touchBarButtons[i].setImage(touchBarImages[i][ROLLOVER]);
|
||||
touchBarButtons[i].setAction(event -> {
|
||||
// Run event handler later to prevent hanging if a dialog needs to be open
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleSelectionPressed(selection);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
TouchBarItem touchBarItem = new TouchBarItem(title[i], touchBarButtons[i], true);
|
||||
touchBarItem.setCustomizationLabel(title[i]);
|
||||
|
||||
touchBar.addItem(touchBarItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadButtons() {
|
||||
Image allButtons = Theme.getThemeImage("buttons", this,
|
||||
BUTTON_IMAGE_SIZE * BUTTON_COUNT,
|
||||
@ -158,6 +225,36 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
|
||||
}
|
||||
}
|
||||
|
||||
private void loadTouchBarImages() {
|
||||
Image allButtonsRetina = Theme.getThemeImage("buttons", this,
|
||||
BUTTON_IMAGE_SIZE * BUTTON_COUNT * 2,
|
||||
BUTTON_IMAGE_SIZE * 3 * 2);
|
||||
touchBarImages = new com.thizzer.jtouchbar.common.Image[BUTTON_COUNT][3];
|
||||
|
||||
for (int i = 0; i < BUTTON_COUNT; i++) {
|
||||
for (int state = 0; state < 3; state++) {
|
||||
BufferedImage image = new BufferedImage(BUTTON_WIDTH * 2, BUTTON_HEIGHT * 2,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics g = image.getGraphics();
|
||||
|
||||
int offset = (BUTTON_IMAGE_SIZE * 2 - BUTTON_WIDTH * 2) / 2;
|
||||
g.drawImage(allButtonsRetina, -(i * BUTTON_IMAGE_SIZE * 2) - offset,
|
||||
(-2 + state) * BUTTON_IMAGE_SIZE * 2, null);
|
||||
|
||||
// convert the image to a PNG to display on the touch bar
|
||||
ByteArrayOutputStream pngStream = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
ImageIO.write(image, "PNG", pngStream);
|
||||
|
||||
touchBarImages[i][state] = new com.thizzer.jtouchbar.common.Image(pngStream.toByteArray());
|
||||
} catch (IOException e) {
|
||||
// ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics screen) {
|
||||
// this data is shared by all EditorToolbar instances
|
||||
@ -305,6 +402,15 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
|
||||
if (updateAfter) {
|
||||
repaint();
|
||||
}
|
||||
|
||||
if (touchBarButtons != null) {
|
||||
if (newState == INACTIVE) {
|
||||
// use ROLLOVER state when INACTIVE
|
||||
newState = ROLLOVER;
|
||||
}
|
||||
|
||||
touchBarButtons[slot].setImage(touchBarImages[slot][newState]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -339,6 +445,20 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
|
||||
if (sel == -1) return;
|
||||
currentRollover = -1;
|
||||
|
||||
handleSelectionPressed(sel, e.isShiftDown(), x, y);
|
||||
}
|
||||
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
}
|
||||
|
||||
private void handleSelectionPressed(int sel) {
|
||||
handleSelectionPressed(sel, false, 0, 0);
|
||||
}
|
||||
|
||||
private void handleSelectionPressed(int sel, boolean isShiftDown, int x, int y) {
|
||||
switch (sel) {
|
||||
case RUN:
|
||||
if (!editor.avoidMultipleOperations) {
|
||||
@ -365,7 +485,7 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
|
||||
break;
|
||||
|
||||
case SAVE:
|
||||
if (e.isShiftDown()) {
|
||||
if (isShiftDown) {
|
||||
editor.handleSaveAs();
|
||||
} else {
|
||||
editor.handleSave(false);
|
||||
@ -375,7 +495,7 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
|
||||
case EXPORT:
|
||||
// launch a timeout timer which can reenable to upload button functionality an
|
||||
if (!editor.avoidMultipleOperations) {
|
||||
editor.handleExport(e.isShiftDown());
|
||||
editor.handleExport(isShiftDown);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -388,15 +508,6 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a particular button to be active.
|
||||
*/
|
||||
|
@ -21,9 +21,10 @@ package processing.app;
|
||||
import cc.arduino.packages.BoardPort;
|
||||
import processing.app.legacy.PApplet;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
@ -33,17 +34,21 @@ public class SerialMonitor extends AbstractTextMonitor {
|
||||
private Serial serial;
|
||||
private int serialRate;
|
||||
|
||||
private static final int COMMAND_HISTORY_SIZE = 100;
|
||||
private final CommandHistory commandHistory =
|
||||
new CommandHistory(COMMAND_HISTORY_SIZE);
|
||||
|
||||
public SerialMonitor(BoardPort port) {
|
||||
super(port);
|
||||
|
||||
serialRate = PreferencesData.getInteger("serial.debug_rate");
|
||||
serialRates.setSelectedItem(serialRate + " " + tr("baud"));
|
||||
onSerialRateChange(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
onSerialRateChange((ActionEvent event) -> {
|
||||
String wholeString = (String) serialRates.getSelectedItem();
|
||||
String rateString = wholeString.substring(0, wholeString.indexOf(' '));
|
||||
serialRate = Integer.parseInt(rateString);
|
||||
PreferencesData.set("serial.debug_rate", rateString);
|
||||
if (serial != null) {
|
||||
try {
|
||||
close();
|
||||
Thread.sleep(100); // Wait for serial port to properly close
|
||||
@ -56,16 +61,41 @@ public class SerialMonitor extends AbstractTextMonitor {
|
||||
}
|
||||
});
|
||||
|
||||
onSendCommand(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
send(textField.getText());
|
||||
onSendCommand((ActionEvent event) -> {
|
||||
String command = textField.getText();
|
||||
send(command);
|
||||
commandHistory.addCommand(command);
|
||||
textField.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
onClearCommand(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
textArea.setText("");
|
||||
onClearCommand((ActionEvent event) -> textArea.setText(""));
|
||||
|
||||
// Add key listener to UP, DOWN, ESC keys for command history traversal.
|
||||
textField.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
|
||||
// Select previous command.
|
||||
case KeyEvent.VK_UP:
|
||||
if (commandHistory.hasPreviousCommand()) {
|
||||
textField.setText(
|
||||
commandHistory.getPreviousCommand(textField.getText()));
|
||||
}
|
||||
break;
|
||||
|
||||
// Select next command.
|
||||
case KeyEvent.VK_DOWN:
|
||||
if (commandHistory.hasNextCommand()) {
|
||||
textField.setText(commandHistory.getNextCommand());
|
||||
}
|
||||
break;
|
||||
|
||||
// Reset history location, restoring the last unexecuted command.
|
||||
case KeyEvent.VK_ESCAPE:
|
||||
textField.setText(commandHistory.resetHistoryLocation());
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -93,6 +123,7 @@ public class SerialMonitor extends AbstractTextMonitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws Exception {
|
||||
super.open();
|
||||
|
||||
@ -106,13 +137,13 @@ public class SerialMonitor extends AbstractTextMonitor {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
super.close();
|
||||
if (serial != null) {
|
||||
int[] location = getPlacement();
|
||||
String locationStr = PApplet.join(PApplet.str(location), ",");
|
||||
PreferencesData.set("last.serial.location", locationStr);
|
||||
textArea.setText("");
|
||||
serial.dispose();
|
||||
serial = null;
|
||||
}
|
||||
|
@ -26,8 +26,12 @@ import processing.app.legacy.PApplet;
|
||||
import java.util.ArrayList;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
@ -42,6 +46,11 @@ public class SerialPlotter extends AbstractMonitor {
|
||||
private Serial serial;
|
||||
private int serialRate, xCount;
|
||||
|
||||
private JLabel noLineEndingAlert;
|
||||
private JTextField textField;
|
||||
private JButton sendButton;
|
||||
private JComboBox<String> lineEndings;
|
||||
|
||||
private ArrayList<Graph> graphs;
|
||||
private final static int BUFFER_CAPACITY_DEFAULT = 500;
|
||||
private final static int BUFFER_CAPACITY_MAX = 5000;
|
||||
@ -51,6 +60,7 @@ public class SerialPlotter extends AbstractMonitor {
|
||||
private static class Graph {
|
||||
public CircularBuffer buffer;
|
||||
private Color color;
|
||||
public String label;
|
||||
|
||||
public Graph(int id, int capacity) {
|
||||
buffer = new CircularBuffer(capacity);
|
||||
@ -189,13 +199,25 @@ public class SerialPlotter extends AbstractMonitor {
|
||||
g.drawLine(xOffset, (int) transformY(zeroTick), bounds.width - xPadding, (int)transformY(zeroTick));
|
||||
|
||||
g.setTransform(AffineTransform.getTranslateInstance(xOffset, 0));
|
||||
float xstep = (float) (bounds.width - xOffset - xPadding) / (float) buffer_capacity;
|
||||
int legendLength = graphs.size() * 10 + (graphs.size() - 1) * 3;
|
||||
|
||||
float xstep = (float) (bounds.width - xOffset - xPadding) / (float) buffer_capacity;
|
||||
// draw legend
|
||||
int legendXOffset = 0;
|
||||
for(int i = 0; i < graphs.size(); ++i) {
|
||||
graphs.get(i).paint(g, xstep, minY, maxY, rangeY, bounds.height);
|
||||
if(graphs.size() > 1) {
|
||||
g.fillRect(bounds.width - (xOffset + legendLength + 10) + i * 13, 10, 10, 10);
|
||||
//draw legend rectangle
|
||||
g.fillRect(10 + legendXOffset, 10, 10, 10);
|
||||
legendXOffset += 13;
|
||||
//draw label
|
||||
g.setColor(boundsColor);
|
||||
String s = graphs.get(i).label;
|
||||
if(s != null && s.length() > 0) {
|
||||
Rectangle2D fBounds = fm.getStringBounds(s, g);
|
||||
int sWidth = (int)fBounds.getWidth();
|
||||
g.drawString(s, 10 + legendXOffset, 10 + (int)fBounds.getHeight() /2);
|
||||
legendXOffset += sWidth + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -225,6 +247,7 @@ public class SerialPlotter extends AbstractMonitor {
|
||||
String rateString = wholeString.substring(0, wholeString.indexOf(' '));
|
||||
serialRate = Integer.parseInt(rateString);
|
||||
PreferencesData.set("serial.debug_rate", rateString);
|
||||
if (serial != null) {
|
||||
try {
|
||||
close();
|
||||
Thread.sleep(100); // Wait for serial port to properly close
|
||||
@ -232,6 +255,7 @@ public class SerialPlotter extends AbstractMonitor {
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
messageBuffer = new StringBuffer();
|
||||
@ -277,12 +301,114 @@ public class SerialPlotter extends AbstractMonitor {
|
||||
pane.add(graphWidth);
|
||||
|
||||
mainPane.add(pane, BorderLayout.SOUTH);
|
||||
|
||||
textField = new JTextField(40);
|
||||
// textField is selected every time the window is focused
|
||||
addWindowFocusListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent e) {
|
||||
textField.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// Add cut/copy/paste contextual menu to the text input field.
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
Action cut = new DefaultEditorKit.CutAction();
|
||||
cut.putValue(Action.NAME, tr("Cut"));
|
||||
menu.add(cut);
|
||||
|
||||
Action copy = new DefaultEditorKit.CopyAction();
|
||||
copy.putValue(Action.NAME, tr("Copy"));
|
||||
menu.add(copy);
|
||||
|
||||
Action paste = new DefaultEditorKit.PasteAction();
|
||||
paste.putValue(Action.NAME, tr("Paste"));
|
||||
menu.add(paste);
|
||||
|
||||
textField.setComponentPopupMenu(menu);
|
||||
|
||||
sendButton = new JButton(tr("Send"));
|
||||
|
||||
JPanel lowerPane = new JPanel();
|
||||
lowerPane.setLayout(new BoxLayout(lowerPane, BoxLayout.X_AXIS));
|
||||
lowerPane.setBorder(new EmptyBorder(4, 4, 4, 4));
|
||||
|
||||
noLineEndingAlert = new JLabel(I18n.format(tr("You've pressed {0} but nothing was sent. Should you select a line ending?"), tr("Send")));
|
||||
noLineEndingAlert.setToolTipText(noLineEndingAlert.getText());
|
||||
noLineEndingAlert.setForeground(pane.getBackground());
|
||||
Dimension minimumSize = new Dimension(noLineEndingAlert.getMinimumSize());
|
||||
minimumSize.setSize(minimumSize.getWidth() / 3, minimumSize.getHeight());
|
||||
noLineEndingAlert.setMinimumSize(minimumSize);
|
||||
|
||||
|
||||
lineEndings = new JComboBox<String>(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
|
||||
lineEndings.addActionListener((ActionEvent event) -> {
|
||||
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
|
||||
noLineEndingAlert.setForeground(pane.getBackground());
|
||||
});
|
||||
lineEndings.setMaximumSize(lineEndings.getMinimumSize());
|
||||
|
||||
lowerPane.add(textField);
|
||||
lowerPane.add(Box.createRigidArea(new Dimension(4, 0)));
|
||||
lowerPane.add(sendButton);
|
||||
|
||||
pane.add(lowerPane);
|
||||
pane.add(noLineEndingAlert);
|
||||
pane.add(Box.createRigidArea(new Dimension(8, 0)));
|
||||
pane.add(lineEndings);
|
||||
|
||||
applyPreferences();
|
||||
|
||||
onSendCommand((ActionEvent event) -> {
|
||||
send(textField.getText());
|
||||
textField.setText("");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void send(String string) {
|
||||
String s = string;
|
||||
if (serial != null) {
|
||||
switch (lineEndings.getSelectedIndex()) {
|
||||
case 1:
|
||||
s += "\n";
|
||||
break;
|
||||
case 2:
|
||||
s += "\r";
|
||||
break;
|
||||
case 3:
|
||||
s += "\r\n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if ("".equals(s) && lineEndings.getSelectedIndex() == 0 && !PreferencesData.has("runtime.line.ending.alert.notified")) {
|
||||
noLineEndingAlert.setForeground(Color.RED);
|
||||
PreferencesData.set("runtime.line.ending.alert.notified", "true");
|
||||
}
|
||||
serial.write(s);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSendCommand(ActionListener listener) {
|
||||
textField.addActionListener(listener);
|
||||
sendButton.addActionListener(listener);
|
||||
}
|
||||
|
||||
public void appyPreferences() {
|
||||
// Apply line endings.
|
||||
if (PreferencesData.get("serial.line_ending") != null) {
|
||||
lineEndings.setSelectedIndex(PreferencesData.getInteger("serial.line_ending"));
|
||||
}
|
||||
}
|
||||
|
||||
protected void onEnableWindow(boolean enable) {
|
||||
serialRates.setEnabled(enable);
|
||||
graphWidth.setEnabled(enable);
|
||||
clearButton.setEnabled(enable);
|
||||
textField.setEnabled(enable);
|
||||
sendButton.setEnabled(enable);
|
||||
}
|
||||
|
||||
private void onSerialRateChange(ActionListener listener) {
|
||||
@ -315,12 +441,17 @@ public class SerialPlotter extends AbstractMonitor {
|
||||
messageBuffer.delete(0, linebreak + 1);
|
||||
|
||||
line = line.trim();
|
||||
if (line.length() == 0) {
|
||||
// the line only contained trimmable characters
|
||||
continue;
|
||||
}
|
||||
String[] parts = line.split("[, \t]+");
|
||||
if(parts.length == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int validParts = 0;
|
||||
int validLabels = 0;
|
||||
for(int i = 0; i < parts.length; ++i) {
|
||||
// all commands start with #
|
||||
if(parts[i].startsWith("#") && parts[i].length() > 1) {
|
||||
@ -353,7 +484,58 @@ public class SerialPlotter extends AbstractMonitor {
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
Double value = null;
|
||||
String label = null;
|
||||
|
||||
// column formated name value pair
|
||||
if(parts[i].contains(":")) {
|
||||
// get label
|
||||
String[] subString = parts[i].split("[:]+");
|
||||
|
||||
if(subString.length > 0) {
|
||||
int labelLength = subString[0].length();
|
||||
|
||||
if(labelLength > 32) {
|
||||
labelLength = 32;
|
||||
}
|
||||
label = subString[0].substring(0, labelLength);
|
||||
} else {
|
||||
label = "";
|
||||
}
|
||||
|
||||
if(subString.length > 1) {
|
||||
parts[i] = subString[1];
|
||||
} else {
|
||||
parts[i] = "";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
value = Double.valueOf(parts[i]);
|
||||
} catch (NumberFormatException e) {
|
||||
// ignored
|
||||
}
|
||||
//CSV header
|
||||
if(label == null && value == null) {
|
||||
label = parts[i];
|
||||
}
|
||||
|
||||
if(value != null) {
|
||||
if(validParts >= graphs.size()) {
|
||||
graphs.add(new Graph(validParts));
|
||||
}
|
||||
graphs.get(validParts).buffer.add(value);
|
||||
validParts++;
|
||||
}
|
||||
if(label != null) {
|
||||
if(validLabels >= graphs.size()) {
|
||||
graphs.add(new Graph(validLabels));
|
||||
}
|
||||
graphs.get(validLabels).label = label;
|
||||
validLabels++;
|
||||
}
|
||||
if(validParts > validLabels) validLabels = validParts;
|
||||
else if(validLabels > validParts) validParts = validLabels;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ public class SketchController {
|
||||
ensureExistence();
|
||||
|
||||
// if read-only, give an error
|
||||
if (isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath())) {
|
||||
if (isReadOnly()) {
|
||||
// if the files are read-only, need to first do a "save as".
|
||||
Base.showMessage(tr("Sketch is Read-Only"),
|
||||
tr("Some files are marked \"read-only\", so you'll\n" +
|
||||
@ -107,7 +107,7 @@ public class SketchController {
|
||||
}
|
||||
|
||||
// if read-only, give an error
|
||||
if (isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath())) {
|
||||
if (isReadOnly()) {
|
||||
// if the files are read-only, need to first do a "save as".
|
||||
Base.showMessage(tr("Sketch is Read-Only"),
|
||||
tr("Some files are marked \"read-only\", so you'll\n" +
|
||||
@ -225,7 +225,7 @@ public class SketchController {
|
||||
ensureExistence();
|
||||
|
||||
// if read-only, give an error
|
||||
if (isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath())) {
|
||||
if (isReadOnly()) {
|
||||
// if the files are read-only, need to first do a "save as".
|
||||
Base.showMessage(tr("Sketch is Read-Only"),
|
||||
tr("Some files are marked \"read-only\", so you'll\n" +
|
||||
@ -253,13 +253,21 @@ public class SketchController {
|
||||
sketch.delete();
|
||||
editor.base.handleClose(editor);
|
||||
} else {
|
||||
|
||||
boolean neverSavedTab = !current.fileExists();
|
||||
|
||||
// delete the file
|
||||
if (!current.delete(sketch.getBuildPath().toPath())) {
|
||||
if (!current.delete(sketch.getBuildPath().toPath()) && !neverSavedTab) {
|
||||
Base.showMessage(tr("Couldn't do it"),
|
||||
I18n.format(tr("Could not delete \"{0}\"."), current.getFileName()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (neverSavedTab) {
|
||||
// remove the file from the sketch list
|
||||
sketch.removeFile(current);
|
||||
}
|
||||
|
||||
editor.removeTab(current);
|
||||
|
||||
// just set current tab to the main tab
|
||||
@ -295,7 +303,7 @@ public class SketchController {
|
||||
// make sure the user didn't hide the sketch folder
|
||||
ensureExistence();
|
||||
|
||||
if (isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath())) {
|
||||
if (isReadOnly()) {
|
||||
Base.showMessage(tr("Sketch is read-only"),
|
||||
tr("Some files are marked \"read-only\", so you'll\n" +
|
||||
"need to re-save this sketch to another location."));
|
||||
@ -359,7 +367,7 @@ public class SketchController {
|
||||
protected boolean saveAs() throws IOException {
|
||||
// get new name for folder
|
||||
FileDialog fd = new FileDialog(editor, tr("Save sketch folder as..."), FileDialog.SAVE);
|
||||
if (isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath()) || isUntitled()) {
|
||||
if (isReadOnly() || isUntitled()) {
|
||||
// default to the sketchbook folder
|
||||
fd.setDirectory(BaseNoGui.getSketchbookFolder().getAbsolutePath());
|
||||
} else {
|
||||
@ -379,7 +387,14 @@ public class SketchController {
|
||||
if (newName == null) return false;
|
||||
newName = SketchController.checkName(newName);
|
||||
|
||||
File newFolder = new File(newParentDir, newName);
|
||||
File newFolder;
|
||||
// User may want to overwrite a .ino
|
||||
// check if the parent folder name ends with the sketch name
|
||||
if (newName.endsWith(".ino") && newParentDir.endsWith(newName.substring(0, newName.lastIndexOf('.'))+ File.separator)) {
|
||||
newFolder = new File(newParentDir);
|
||||
} else {
|
||||
newFolder = new File(newParentDir, newName);
|
||||
}
|
||||
|
||||
// check if the paths are identical
|
||||
if (newFolder.equals(sketch.getFolder())) {
|
||||
@ -423,7 +438,7 @@ public class SketchController {
|
||||
//editor.sketchbook.rebuildMenusAsync();
|
||||
editor.base.rebuildSketchbookMenus();
|
||||
editor.header.rebuild();
|
||||
|
||||
editor.updateTitle();
|
||||
// Make sure that it's not an untitled sketch
|
||||
setUntitled(false);
|
||||
|
||||
@ -441,7 +456,7 @@ public class SketchController {
|
||||
ensureExistence();
|
||||
|
||||
// if read-only, give an error
|
||||
if (isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath())) {
|
||||
if (isReadOnly()) {
|
||||
// if the files are read-only, need to first do a "save as".
|
||||
Base.showMessage(tr("Sketch is Read-Only"),
|
||||
tr("Some files are marked \"read-only\", so you'll\n" +
|
||||
@ -631,6 +646,8 @@ public class SketchController {
|
||||
progressListener.progress(20);
|
||||
}
|
||||
|
||||
EditorConsole.setCurrentEditorConsole(editor.console);
|
||||
|
||||
ensureExistence();
|
||||
|
||||
|
||||
@ -657,7 +674,7 @@ public class SketchController {
|
||||
FileUtils.copy(sketch.getFolder(), tempFolder);
|
||||
|
||||
for (SketchFile file : Stream.of(sketch.getFiles()).filter(SketchFile::isModified).collect(Collectors.toList())) {
|
||||
Files.write(Paths.get(tempFolder.getAbsolutePath(), file.getFileName()), file.getProgram().getBytes());
|
||||
Files.write(Paths.get(tempFolder.getAbsolutePath(), file.getFileName()), file.getProgram().getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
return Paths.get(tempFolder.getAbsolutePath(), sketch.getPrimaryFile().getFileName()).toFile();
|
||||
@ -693,6 +710,8 @@ public class SketchController {
|
||||
UploaderUtils uploaderInstance = new UploaderUtils();
|
||||
Uploader uploader = uploaderInstance.getUploaderByPreferences(false);
|
||||
|
||||
EditorConsole.setCurrentEditorConsole(editor.console);
|
||||
|
||||
boolean success = false;
|
||||
do {
|
||||
if (uploader.requiresAuthorization() && !PreferencesData.has(uploader.getAuthorizationKey())) {
|
||||
@ -772,7 +791,9 @@ public class SketchController {
|
||||
* examples directory, or when sketches are loaded from read-only
|
||||
* volumes or folders without appropriate permissions.
|
||||
*/
|
||||
public boolean isReadOnly(LibraryList libraries, String examplesPath) {
|
||||
public boolean isReadOnly() {
|
||||
LibraryList libraries = BaseNoGui.librariesIndexer.getInstalledLibraries();
|
||||
String examplesPath = BaseNoGui.getExamplesPath();
|
||||
String apath = sketch.getFolder().getAbsolutePath();
|
||||
|
||||
Optional<UserLibrary> libraryThatIncludesSketch = libraries.stream().filter(lib -> apath.startsWith(lib.getInstalledFolder().getAbsolutePath())).findFirst();
|
||||
@ -825,8 +846,9 @@ public class SketchController {
|
||||
|
||||
if (!newName.equals(origName)) {
|
||||
String msg =
|
||||
tr("The sketch name had to be modified. Sketch names can only consist\n" +
|
||||
"of ASCII characters and numbers and be less than 64 characters long.");
|
||||
tr("The sketch name had to be modified.\n" +
|
||||
"Sketch names must start with a letter or number, followed by letters,\n" +
|
||||
"numbers, dashes, dots and underscores. Maximum length is 63 characters.");
|
||||
System.out.println(msg);
|
||||
}
|
||||
return newName;
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
package processing.app;
|
||||
|
||||
import static processing.app.I18n.format;
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
import java.awt.Color;
|
||||
@ -38,10 +39,23 @@ import java.awt.Toolkit;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.swing.text.StyleContext;
|
||||
|
||||
@ -50,7 +64,8 @@ import org.apache.batik.transcoder.TranscoderException;
|
||||
import org.apache.batik.transcoder.TranscoderInput;
|
||||
import org.apache.batik.transcoder.TranscoderOutput;
|
||||
import org.apache.batik.transcoder.image.PNGTranscoder;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import processing.app.helpers.OSUtils;
|
||||
import processing.app.helpers.PreferencesHelper;
|
||||
import processing.app.helpers.PreferencesMap;
|
||||
@ -62,6 +77,238 @@ import processing.app.helpers.PreferencesMap;
|
||||
*/
|
||||
public class Theme {
|
||||
|
||||
static final String THEME_DIR = "theme/";
|
||||
static final String THEME_FILE_NAME = "theme.txt";
|
||||
|
||||
static final String NAMESPACE_APP = "app:";
|
||||
static final String NAMESPACE_USER = "user:";
|
||||
|
||||
/**
|
||||
* A theme resource, this is returned instead of {@link File} so that we can
|
||||
* support zip-packaged resources as well as files in the file system
|
||||
*/
|
||||
public static class Resource {
|
||||
|
||||
// Priority levels used to determine whether one resource should override
|
||||
// another
|
||||
static public final int PRIORITY_DEFAULT = 0;
|
||||
static public final int PRIORITY_USER_ZIP = 1;
|
||||
static public final int PRIORITY_USER_FILE = 2;
|
||||
|
||||
/**
|
||||
* Priority of this resource.
|
||||
*/
|
||||
private final int priority;
|
||||
|
||||
/**
|
||||
* Resource name (original name of requested resource, relative path only).
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* File if this resource represents a file, can be null.
|
||||
*/
|
||||
private final File file;
|
||||
|
||||
/**
|
||||
* Zip theme if the resource is contained within a zipped theme
|
||||
*/
|
||||
private final ZippedTheme theme;
|
||||
|
||||
/**
|
||||
* Zip entry if this resource represents a zip entry, can be null.
|
||||
*/
|
||||
private final ZipEntry zipEntry;
|
||||
|
||||
/**
|
||||
* URL of this resource regardless of type, theoretically shouldn't ever be
|
||||
* null though it might be if a particular resource path can't be
|
||||
* successfully transformed into a URL (eg. {@link Theme#getUrl} traps a
|
||||
* <tt>MalformedURLException</tt>).
|
||||
*/
|
||||
private final URL url;
|
||||
|
||||
/**
|
||||
* If this resource supercedes a resource with a lower priority, this field
|
||||
* stores a reference to the superceded resource. This allows consumers to
|
||||
* traverse the resource hierarchy if required.
|
||||
*/
|
||||
private Resource parent;
|
||||
|
||||
/**
|
||||
* ctor for file resources
|
||||
*/
|
||||
Resource(int priority, String name, URL url, File file) {
|
||||
this(priority, name, url, file, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* ctor for zip resources
|
||||
*/
|
||||
Resource(int priority, String name, URL url, ZippedTheme theme, ZipEntry entry) {
|
||||
this(priority, name, url, null, theme, entry);
|
||||
}
|
||||
|
||||
private Resource(int priority, String name, URL url, File file, ZippedTheme theme, ZipEntry zipEntry) {
|
||||
this.priority = priority;
|
||||
this.name = name;
|
||||
this.file = file;
|
||||
this.theme = theme;
|
||||
this.zipEntry = zipEntry;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Resource getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public URL getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return this.priority;
|
||||
}
|
||||
|
||||
public boolean isUserDefined() {
|
||||
return this.priority > PRIORITY_DEFAULT;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return this.zipEntry != null || this.file == null || this.file.exists();
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (this.file != null) {
|
||||
return new FileInputStream(this.file);
|
||||
}
|
||||
|
||||
if (this.zipEntry != null) {
|
||||
return this.theme.getZip().getInputStream(this.zipEntry);
|
||||
}
|
||||
|
||||
if (this.url != null) {
|
||||
return this.url.openStream();
|
||||
}
|
||||
|
||||
throw new FileNotFoundException(this.name);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
Resource withParent(Resource parent) {
|
||||
this.parent = parent;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Struct which keeps information about a discovered .zip theme file
|
||||
*/
|
||||
public static class ZippedTheme {
|
||||
|
||||
/**
|
||||
* Configuration key, this key consists of a "namespace" which determines
|
||||
* the root folder the theme was found in without actually storing the path
|
||||
* itself, followed by the file name.
|
||||
*/
|
||||
private final String key;
|
||||
|
||||
/**
|
||||
* File containing the theme
|
||||
*/
|
||||
private final File file;
|
||||
|
||||
/**
|
||||
* Zip file handle for retrieving entries
|
||||
*/
|
||||
private final ZipFile zip;
|
||||
|
||||
/**
|
||||
* Display name, defaulted to filename but can be read from metadata
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Version number, plain text string read from metadata
|
||||
*/
|
||||
private final String version;
|
||||
|
||||
private ZippedTheme(String namespace, File file, ZipFile zip, String name, String version) {
|
||||
this.key = namespace + file.getName();
|
||||
this.file = file;
|
||||
this.zip = zip;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
public ZipFile getZip() {
|
||||
return this.zip;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String description = String.format("%s %s (%s)", this.getName(), this.getVersion(), this.file.getName());
|
||||
return StringUtils.abbreviate(description, 40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse the supplied zip file as a theme file. This is largely
|
||||
* determined by the file being readable and containing a theme.txt entry.
|
||||
* Returns null if the file is unreadable or doesn't contain theme.txt
|
||||
*/
|
||||
static ZippedTheme load(String namespace, File file) {
|
||||
ZipFile zip = null;
|
||||
try {
|
||||
zip = new ZipFile(file);
|
||||
ZipEntry themeTxtEntry = zip.getEntry(THEME_FILE_NAME);
|
||||
if (themeTxtEntry != null) {
|
||||
String name = file.getName().substring(0, file.getName().length() - 4);
|
||||
String version = "";
|
||||
|
||||
ZipEntry themePropsEntry = zip.getEntry("theme.properties");
|
||||
if (themePropsEntry != null) {
|
||||
Properties themeProperties = new Properties();
|
||||
themeProperties.load(zip.getInputStream(themePropsEntry));
|
||||
|
||||
name = themeProperties.getProperty("name", name);
|
||||
version = themeProperties.getProperty("version", version);
|
||||
}
|
||||
|
||||
return new ZippedTheme(namespace, file, zip, name, version);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
System.err.println(format(tr("Error loading theme {0}: {1}"),
|
||||
file.getAbsolutePath(), ex.getMessage()));
|
||||
IOUtils.closeQuietly(zip);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy of the defaults in case the user mangles a preference.
|
||||
*/
|
||||
@ -71,9 +318,21 @@ public class Theme {
|
||||
*/
|
||||
static PreferencesMap table = new PreferencesMap();
|
||||
|
||||
/**
|
||||
* Available zipped themes
|
||||
*/
|
||||
static private final Map<String, ZippedTheme> availableThemes = new TreeMap<>();
|
||||
|
||||
/**
|
||||
* Zip file containing user-defined theme elements
|
||||
*/
|
||||
static private ZippedTheme zipTheme;
|
||||
|
||||
static protected void init() {
|
||||
zipTheme = openZipTheme();
|
||||
|
||||
try {
|
||||
table.load(new File(BaseNoGui.getContentFile("lib"), "theme/theme.txt"));
|
||||
loadFromResource(table, THEME_DIR + THEME_FILE_NAME);
|
||||
} catch (Exception te) {
|
||||
Base.showError(null, tr("Could not read color theme settings.\n"
|
||||
+ "You'll need to reinstall Arduino."),
|
||||
@ -87,6 +346,44 @@ public class Theme {
|
||||
defaults = new PreferencesMap(table);
|
||||
}
|
||||
|
||||
static private ZippedTheme openZipTheme() {
|
||||
refreshAvailableThemes();
|
||||
String selectedTheme = PreferencesData.get("theme.file", "");
|
||||
synchronized(availableThemes) {
|
||||
return availableThemes.get(selectedTheme);
|
||||
}
|
||||
}
|
||||
|
||||
static private void refreshAvailableThemes() {
|
||||
Map<String, ZippedTheme> discoveredThemes = new TreeMap<>();
|
||||
|
||||
refreshAvailableThemes(discoveredThemes, NAMESPACE_APP, new File(BaseNoGui.getContentFile("lib"), THEME_DIR));
|
||||
refreshAvailableThemes(discoveredThemes, NAMESPACE_USER, new File(BaseNoGui.getSketchbookFolder(), THEME_DIR));
|
||||
|
||||
synchronized (availableThemes) {
|
||||
availableThemes.clear();
|
||||
availableThemes.putAll(discoveredThemes);
|
||||
}
|
||||
}
|
||||
|
||||
static private void refreshAvailableThemes(Map<String, ZippedTheme> discoveredThemes, String namespace, File folder) {
|
||||
if (!folder.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (File zipFile : folder.listFiles((dir, name) -> name.endsWith(".zip"))) {
|
||||
ZippedTheme theme = ZippedTheme.load(namespace, zipFile);
|
||||
if (theme != null) {
|
||||
discoveredThemes.put(theme.getKey(), theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Collection<ZippedTheme> getAvailablethemes() {
|
||||
refreshAvailableThemes();
|
||||
return Collections.unmodifiableCollection(availableThemes.values());
|
||||
}
|
||||
|
||||
static public String get(String attribute) {
|
||||
return table.get(attribute);
|
||||
}
|
||||
@ -177,6 +474,9 @@ public class Theme {
|
||||
String value = getDefault(attr);
|
||||
set(attr, value);
|
||||
font = PreferencesHelper.getFont(table, attr);
|
||||
if (font == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return font.deriveFont((float) scale(font.getSize()));
|
||||
}
|
||||
@ -245,33 +545,34 @@ public class Theme {
|
||||
*/
|
||||
static public Image getLibImage(String filename, Component who, int width,
|
||||
int height) {
|
||||
File libFolder = BaseNoGui.getContentFile("lib");
|
||||
Image image = null;
|
||||
|
||||
// Use vector image when available
|
||||
File vectorFile = new File(libFolder, filename + ".svg");
|
||||
Resource vectorFile = getThemeResource(filename + ".svg");
|
||||
if (vectorFile.exists()) {
|
||||
try {
|
||||
image = imageFromSVG(vectorFile.toURI().toURL(), width, height);
|
||||
image = imageFromSVG(vectorFile.getUrl(), width, height);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to load " + vectorFile.getAbsolutePath()
|
||||
+ ": " + e.getMessage());
|
||||
System.err.println("Failed to load " + vectorFile + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise fall-back to PNG bitmaps
|
||||
if (image == null) {
|
||||
File bitmapFile = new File(libFolder, filename + ".png");
|
||||
File bitmap2xFile = new File(libFolder, filename + "@2x.png");
|
||||
Resource bitmapFile = getThemeResource(filename + ".png");
|
||||
|
||||
File imageFile;
|
||||
if ((getScale() > 125 && bitmap2xFile.exists()) || !bitmapFile.exists()) {
|
||||
// Otherwise fall-back to PNG bitmaps, allowing user-defined bitmaps to
|
||||
// override built-in svgs
|
||||
if (image == null || bitmapFile.getPriority() > vectorFile.getPriority()) {
|
||||
Resource bitmap2xFile = getThemeResource(filename + "@2x.png");
|
||||
|
||||
Resource imageFile;
|
||||
if (((getScale() > 125 && bitmap2xFile.exists()) || !bitmapFile.exists())
|
||||
&& (bitmapFile.isUserDefined() && bitmap2xFile.isUserDefined())) {
|
||||
imageFile = bitmap2xFile;
|
||||
} else {
|
||||
imageFile = bitmapFile;
|
||||
}
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
image = tk.getImage(imageFile.getAbsolutePath());
|
||||
image = tk.getImage(imageFile.getUrl());
|
||||
}
|
||||
|
||||
MediaTracker tracker = new MediaTracker(who);
|
||||
@ -298,7 +599,7 @@ public class Theme {
|
||||
*/
|
||||
static public Image getThemeImage(String name, Component who, int width,
|
||||
int height) {
|
||||
return getLibImage("theme/" + name, who, width, height);
|
||||
return getLibImage(THEME_DIR + name, who, width, height);
|
||||
}
|
||||
|
||||
private static Image imageFromSVG(URL url, int width, int height)
|
||||
@ -325,4 +626,102 @@ public class Theme {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the supplied {@link PreferencesMap} from the specified resource,
|
||||
* recursively loading parent resources such that entries are loaded in order
|
||||
* of priority (lowest first).
|
||||
*
|
||||
* @param map preference map to populate
|
||||
* @param name name of resource to load
|
||||
*/
|
||||
static public PreferencesMap loadFromResource(PreferencesMap map, String name) throws IOException {
|
||||
return loadFromResource(map, getThemeResource(name));
|
||||
}
|
||||
|
||||
static private PreferencesMap loadFromResource(PreferencesMap map, Resource resource) throws IOException {
|
||||
if (resource != null) {
|
||||
loadFromResource(map, resource.getParent());
|
||||
map.load(resource.getInputStream());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
static public Resource getThemeResource(String name) {
|
||||
File defaultfile = getDefaultFile(name);
|
||||
Resource resource = new Resource(Resource.PRIORITY_DEFAULT, name, getUrl(defaultfile), defaultfile);
|
||||
|
||||
ZipEntry themeZipEntry = getThemeZipEntry(name);
|
||||
if (themeZipEntry != null) {
|
||||
resource = new Resource(Resource.PRIORITY_USER_ZIP, name, getUrl(themeZipEntry), zipTheme, themeZipEntry).withParent(resource);
|
||||
}
|
||||
|
||||
File themeFile = getThemeFile(name);
|
||||
if (themeFile != null) {
|
||||
resource = new Resource(Resource.PRIORITY_USER_FILE, name, getUrl(themeFile), themeFile).withParent(resource);
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
static private File getThemeFile(String name) {
|
||||
File sketchBookThemeFolder = new File(BaseNoGui.getSketchbookFolder(), THEME_DIR);
|
||||
File themeFile = new File(sketchBookThemeFolder, name);
|
||||
if (themeFile.exists()) {
|
||||
return themeFile;
|
||||
}
|
||||
|
||||
if (name.startsWith(THEME_DIR)) {
|
||||
themeFile = new File(sketchBookThemeFolder, name.substring(THEME_DIR.length()));
|
||||
if (themeFile.exists()) {
|
||||
return themeFile;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static private ZipEntry getThemeZipEntry(String name) {
|
||||
if (zipTheme == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name.startsWith(THEME_DIR)) {
|
||||
name = name.substring(THEME_DIR.length());
|
||||
}
|
||||
|
||||
return zipTheme.getZip().getEntry(name);
|
||||
}
|
||||
|
||||
static private File getDefaultFile(String name) {
|
||||
return new File(BaseNoGui.getContentFile("lib"), name);
|
||||
}
|
||||
|
||||
static URL getUrl(File file) {
|
||||
try {
|
||||
return file.toURI().toURL();
|
||||
} catch (MalformedURLException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static URL getUrl(ZipEntry entry) {
|
||||
try {
|
||||
// Adjust file name for URL format on Windows
|
||||
String zipFile = zipTheme.getZip().getName().replace('\\', '/');
|
||||
if (!zipFile.startsWith("/")) {
|
||||
zipFile = "/" + zipFile;
|
||||
}
|
||||
|
||||
// Construct a URL which points to the internal resource
|
||||
URI uri = new URI("jar", "file:" + zipFile + "!/" + entry.getName(), null);
|
||||
return uri.toURL();
|
||||
|
||||
} catch (MalformedURLException | URISyntaxException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ import static processing.app.I18n.tr;
|
||||
*/
|
||||
public class UpdateCheck implements Runnable {
|
||||
Base base;
|
||||
String downloadURL = tr("http://www.arduino.cc/latest.txt");
|
||||
String downloadURL = tr("https://www.arduino.cc/latest.txt");
|
||||
|
||||
static final long ONE_DAY = 24 * 60 * 60 * 1000;
|
||||
|
||||
@ -66,14 +66,14 @@ public class UpdateCheck implements Runnable {
|
||||
public void run() {
|
||||
//System.out.println("checking for updates...");
|
||||
|
||||
// generate a random id in case none exists yet
|
||||
Random r = new Random();
|
||||
long id = r.nextLong();
|
||||
|
||||
long id;
|
||||
String idString = PreferencesData.get("update.id");
|
||||
if (idString != null) {
|
||||
id = Long.parseLong(idString);
|
||||
} else {
|
||||
// generate a random id in case none exists yet
|
||||
Random r = new Random();
|
||||
id = r.nextLong();
|
||||
PreferencesData.set("update.id", String.valueOf(id));
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ public class UpdateCheck implements Runnable {
|
||||
options,
|
||||
options[0]);
|
||||
if (result == JOptionPane.YES_OPTION) {
|
||||
Base.openURL(tr("http://www.arduino.cc/en/Main/Software"));
|
||||
Base.openURL(tr("https://www.arduino.cc/en/Main/Software"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package processing.app.forms;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.Theme;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.File;
|
||||
|
||||
import static processing.app.I18n.tr;
|
||||
|
||||
@ -34,7 +34,7 @@ public class PasswordAuthorizationDialog extends JDialog {
|
||||
|
||||
typePasswordLabel.setText(dialogText);
|
||||
|
||||
icon.setIcon(new ImageIcon(new File(Base.getContentFile("lib"), "theme/lock.png").getAbsolutePath()));
|
||||
icon.setIcon(new ImageIcon(Theme.getThemeResource("theme/lock.png").getUrl()));
|
||||
|
||||
passwordLabel.setText(tr("Password:"));
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
package processing.app.macosx;
|
||||
|
||||
import com.apple.eawt.*;
|
||||
import com.apple.eawt.AppEvent.AppReOpenedEvent;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.Editor;
|
||||
|
||||
@ -45,6 +47,20 @@ public class ThinkDifferent {
|
||||
|
||||
static public void init() {
|
||||
Application application = Application.getApplication();
|
||||
|
||||
application.addAppEventListener(new AppReOpenedListener() {
|
||||
@Override
|
||||
public void appReOpened(AppReOpenedEvent aroe) {
|
||||
try {
|
||||
if (Base.INSTANCE.getEditors().size() == 0) {
|
||||
Base.INSTANCE.handleNew();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
application.setAboutHandler(new AboutHandler() {
|
||||
@Override
|
||||
public void handleAbout(AppEvent.AboutEvent aboutEvent) {
|
||||
|
@ -24,12 +24,12 @@
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenMap;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenTypes;
|
||||
import processing.app.Base;
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.packages.UserLibrary;
|
||||
import processing.app.debug.TargetPlatform;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@ -89,7 +89,7 @@ public class PdeKeywords {
|
||||
File platformKeywords = new File(tp.getFolder(), "keywords.txt");
|
||||
if (platformKeywords.exists()) parseKeywordsTxt(platformKeywords);
|
||||
}
|
||||
for (ContributedLibrary lib : BaseNoGui.librariesIndexer.getInstalledLibraries()) {
|
||||
for (UserLibrary lib : BaseNoGui.librariesIndexer.getInstalledLibraries()) {
|
||||
File keywords = new File(lib.getInstalledFolder(), "keywords.txt");
|
||||
if (keywords.exists()) {
|
||||
parseKeywordsTxt(keywords);
|
||||
|
@ -30,32 +30,42 @@
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Font;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import javax.swing.KeyStroke;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
import org.fife.ui.rsyntaxtextarea.Token;
|
||||
import org.fife.ui.rtextarea.RTextArea;
|
||||
import org.fife.ui.rtextarea.RTextAreaUI;
|
||||
import processing.app.Base;
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.PreferencesData;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.event.EventListenerList;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Segment;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
|
||||
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.Style;
|
||||
import org.fife.ui.rsyntaxtextarea.Theme;
|
||||
import org.fife.ui.rsyntaxtextarea.Token;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenImpl;
|
||||
import org.fife.ui.rsyntaxtextarea.TokenTypes;
|
||||
import org.fife.ui.rtextarea.RTextArea;
|
||||
import org.fife.ui.rtextarea.RTextAreaUI;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.PreferencesData;
|
||||
import processing.app.helpers.OSUtils;
|
||||
|
||||
/**
|
||||
@ -91,9 +101,9 @@ public class SketchTextArea extends RSyntaxTextArea {
|
||||
}
|
||||
|
||||
private void setTheme(String name) throws IOException {
|
||||
FileInputStream defaultXmlInputStream = null;
|
||||
InputStream defaultXmlInputStream = null;
|
||||
try {
|
||||
defaultXmlInputStream = new FileInputStream(new File(BaseNoGui.getContentFile("lib"), "theme/syntax/" + name + ".xml"));
|
||||
defaultXmlInputStream = processing.app.Theme.getThemeResource("theme/syntax/" + name + ".xml").getInputStream();
|
||||
Theme theme = Theme.load(defaultXmlInputStream);
|
||||
theme.apply(this);
|
||||
} finally {
|
||||
|
@ -3,6 +3,8 @@
|
||||
*/
|
||||
package processing.app.tools;
|
||||
|
||||
import processing.app.PreferencesData;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
@ -567,6 +569,43 @@ public class MenuScroller {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
firstIndex += increment * accelerator;
|
||||
refreshMenu();
|
||||
if (PreferencesData.getBoolean("ide.accessible")) {
|
||||
// If the user has chosen to use accessibility features, it means that they are using a screen reader
|
||||
// to assist them in development of their project. This scroller is very unfriendly toward screen readers
|
||||
// because it does not tell the user that it is scrolling through the board options, and it does not read
|
||||
// the name of the boards as they scroll by. It is possible that the desired board will never become
|
||||
// accessible.
|
||||
// Because this scroller is quite nice for the sighted user, the idea here is to continue to use the
|
||||
// scroller, but to fool it into scrolling one item at a time for accessible features users so that the
|
||||
// screen readers work well, too.
|
||||
// It's not the prettiest of code, but it works.
|
||||
String itemClassName;
|
||||
int keyEvent;
|
||||
|
||||
// The blind user likely used an arrow key to get to the scroller. Determine which arrow key
|
||||
// so we can send an event for the opposite arrow key. This fools the scroller into scrolling
|
||||
// a single item. Get the class name of the new item while we're here
|
||||
if (increment > 0) {
|
||||
itemClassName = menuItems[firstIndex + scrollCount - 1].getClass().getName();
|
||||
keyEvent = KeyEvent.VK_UP;
|
||||
}
|
||||
else {
|
||||
itemClassName = menuItems[firstIndex].getClass().getName();
|
||||
keyEvent = KeyEvent.VK_DOWN;
|
||||
}
|
||||
|
||||
// Use the class name to check if the next item is a separator. If it is, just let it scroll on like
|
||||
// normal, otherwise move the cursor back with the opposite key event to the new item so that item is read
|
||||
// by a screen reader and the user can use their arrow keys to navigate the list one item at a time
|
||||
if (!itemClassName.equals(JSeparator.class.getName()) ) {
|
||||
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Component comp = manager.getFocusOwner();
|
||||
KeyEvent event = new KeyEvent(comp,
|
||||
KeyEvent.KEY_PRESSED, System.currentTimeMillis(), 0,
|
||||
keyEvent, KeyEvent.CHAR_UNDEFINED);
|
||||
comp.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -4,10 +4,11 @@ import cc.arduino.contributions.libraries.LibrariesIndex;
|
||||
import cc.arduino.utils.MultiStepProgress;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.module.mrbean.MrBeanModule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.helpers.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
@ -38,11 +39,13 @@ public class GzippedJsonDownloaderTest {
|
||||
|
||||
@Test
|
||||
public void testJsonDownload() throws Exception {
|
||||
new GZippedJsonDownloader(downloader, new URL("http://downloads.arduino.cc/libraries/library_index.json"), new URL("http://downloads.arduino.cc/libraries/library_index.json.gz")).download(tempFile, new MultiStepProgress(1), "", new NoopProgressListener());
|
||||
BaseNoGui.initPlatform();
|
||||
new GZippedJsonDownloader(downloader, new URL("http://downloads.arduino.cc/libraries/library_index.json"),
|
||||
new URL("http://downloads.arduino.cc/libraries/library_index.json.gz"))
|
||||
.download(tempFile, new MultiStepProgress(1), "", new NoopProgressListener(), true);
|
||||
|
||||
InputStream indexIn = new FileInputStream(tempFile);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(new MrBeanModule());
|
||||
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
||||
mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true);
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
@ -4,10 +4,11 @@ import cc.arduino.contributions.libraries.LibrariesIndex;
|
||||
import cc.arduino.utils.MultiStepProgress;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.module.mrbean.MrBeanModule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.helpers.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
@ -38,11 +39,12 @@ public class JsonDownloaderTest {
|
||||
|
||||
@Test
|
||||
public void testJsonDownload() throws Exception {
|
||||
new JsonDownloader(downloader, new URL("http://downloads.arduino.cc/libraries/library_index.json")).download(tempFile, new MultiStepProgress(1), "", new NoopProgressListener());
|
||||
BaseNoGui.initPlatform();
|
||||
new JsonDownloader(downloader, new URL("http://downloads.arduino.cc/libraries/library_index.json"))
|
||||
.download(tempFile, new MultiStepProgress(1), "", new NoopProgressListener(), true);
|
||||
|
||||
InputStream indexIn = new FileInputStream(tempFile);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(new MrBeanModule());
|
||||
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
||||
mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true);
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
84
app/test/cc/arduino/contributions/UpdatableLibraryTest.java
Normal file
84
app/test/cc/arduino/contributions/UpdatableLibraryTest.java
Normal file
@ -0,0 +1,84 @@
|
||||
package cc.arduino.contributions;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import cc.arduino.contributions.libraries.ContributedLibrary;
|
||||
import cc.arduino.contributions.libraries.LibrariesIndexer;
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.packages.UserLibraryFolder;
|
||||
import processing.app.packages.UserLibraryFolder.Location;
|
||||
|
||||
public class UpdatableLibraryTest {
|
||||
|
||||
File testdata = new File(
|
||||
UpdatableLibraryTest.class.getResource("/").getFile(),
|
||||
"../testdata/libraries");
|
||||
File index_SD_only = new File(testdata, "index_SD_only");
|
||||
File SD111 = new File(testdata, "SD_1.1.1");
|
||||
File SD121 = new File(testdata, "SD_1.2.1");
|
||||
File index_Bridge_only = new File(testdata, "index_Bridge_only");
|
||||
File Bridge163 = new File(testdata, "Bridge_1.6.3");
|
||||
File Bridge170 = new File(testdata, "Bridge_1.7.0");
|
||||
|
||||
@Test
|
||||
public void testUpdatableLibrary() throws Exception {
|
||||
List<UserLibraryFolder> folders = new ArrayList<>();
|
||||
folders.add(new UserLibraryFolder(SD111, Location.IDE_BUILTIN));
|
||||
|
||||
LibrariesIndexer indexer = new LibrariesIndexer(index_SD_only);
|
||||
BaseNoGui.librariesIndexer = indexer;
|
||||
indexer.parseIndex();
|
||||
indexer.setLibrariesFolders(folders);
|
||||
indexer.rescanLibraries();
|
||||
|
||||
ContributedLibrary sdLib = indexer.getIndex().getInstalled("SD").get();
|
||||
assertTrue("SD lib is installed", sdLib.isLibraryInstalled());
|
||||
assertEquals("SD installed version", "1.1.1", sdLib.getParsedVersion());
|
||||
|
||||
assertTrue(ContributionsSelfCheck.checkForUpdatableLibraries());
|
||||
|
||||
folders.add(new UserLibraryFolder(SD121, Location.SKETCHBOOK));
|
||||
indexer.setLibrariesFolders(folders);
|
||||
|
||||
sdLib = indexer.getIndex().getInstalled("SD").get();
|
||||
assertTrue("SD lib is installed", sdLib.isLibraryInstalled());
|
||||
assertEquals("SD installed version", "1.2.1", sdLib.getParsedVersion());
|
||||
|
||||
assertFalse(ContributionsSelfCheck.checkForUpdatableLibraries());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatableLibraryWithBundled() throws Exception {
|
||||
List<UserLibraryFolder> folders = new ArrayList<>();
|
||||
folders.add(new UserLibraryFolder(Bridge163, Location.IDE_BUILTIN));
|
||||
|
||||
LibrariesIndexer indexer = new LibrariesIndexer(index_Bridge_only);
|
||||
BaseNoGui.librariesIndexer = indexer;
|
||||
indexer.parseIndex();
|
||||
indexer.setLibrariesFolders(folders);
|
||||
indexer.rescanLibraries();
|
||||
|
||||
ContributedLibrary l = indexer.getIndex().getInstalled("Bridge").get();
|
||||
assertTrue("Bridge lib is installed", l.isLibraryInstalled());
|
||||
assertEquals("Bridge installed version", "1.6.3", l.getParsedVersion());
|
||||
|
||||
assertTrue(ContributionsSelfCheck.checkForUpdatableLibraries());
|
||||
|
||||
folders.add(new UserLibraryFolder(Bridge170, Location.SKETCHBOOK));
|
||||
indexer.setLibrariesFolders(folders);
|
||||
|
||||
l = indexer.getIndex().getInstalled("Bridge").get();
|
||||
assertTrue("Bridge lib is installed", l.isLibraryInstalled());
|
||||
assertEquals("Bridge installed version", "1.7.0", l.getParsedVersion());
|
||||
|
||||
assertFalse(ContributionsSelfCheck.checkForUpdatableLibraries());
|
||||
}
|
||||
}
|
@ -30,28 +30,38 @@
|
||||
package cc.arduino.contributions;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
public class VersionHelperTest {
|
||||
|
||||
public void assertOptionalEquals(String expected, Optional<Version> value) {
|
||||
assertTrue(value.isPresent());
|
||||
assertEquals(expected, value.get().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersions() throws Exception {
|
||||
assertEquals("1.0.0", VersionHelper.valueOf("1.0.0").toString());
|
||||
assertEquals("1.0.0", VersionHelper.valueOf("1.0").toString());
|
||||
assertEquals("1.0.0", VersionHelper.valueOf("1").toString());
|
||||
assertEquals("1.0.0-abc", VersionHelper.valueOf("1.0.0-abc").toString());
|
||||
assertEquals("1.0.0-abc", VersionHelper.valueOf("1.0-abc").toString());
|
||||
assertEquals("1.0.0-abc", VersionHelper.valueOf("1-abc").toString());
|
||||
assertEquals("1.0.0+abc", VersionHelper.valueOf("1.0.0+abc").toString());
|
||||
assertEquals("1.0.0+abc", VersionHelper.valueOf("1.0+abc").toString());
|
||||
assertEquals("1.0.0+abc", VersionHelper.valueOf("1+abc").toString());
|
||||
assertEquals("1.0.0-def+abc", VersionHelper.valueOf("1.0.0-def+abc").toString());
|
||||
assertEquals("1.0.0-def+abc", VersionHelper.valueOf("1.0-def+abc").toString());
|
||||
assertEquals("1.0.0-def+abc", VersionHelper.valueOf("1-def+abc").toString());
|
||||
assertEquals("1.0.0+def-abc", VersionHelper.valueOf("1.0.0+def-abc").toString());
|
||||
assertEquals("1.0.0+def-abc", VersionHelper.valueOf("1.0+def-abc").toString());
|
||||
assertEquals("1.0.0+def-abc", VersionHelper.valueOf("1+def-abc").toString());
|
||||
assertOptionalEquals("1.0.0", VersionHelper.valueOf("1.0.0"));
|
||||
assertOptionalEquals("1.0.0", VersionHelper.valueOf("1.0"));
|
||||
assertOptionalEquals("1.0.0", VersionHelper.valueOf("1"));
|
||||
assertOptionalEquals("1.0.0-abc", VersionHelper.valueOf("1.0.0-abc"));
|
||||
assertOptionalEquals("1.0.0-abc", VersionHelper.valueOf("1.0-abc"));
|
||||
assertOptionalEquals("1.0.0-abc", VersionHelper.valueOf("1-abc"));
|
||||
assertOptionalEquals("1.0.0+abc", VersionHelper.valueOf("1.0.0+abc"));
|
||||
assertOptionalEquals("1.0.0+abc", VersionHelper.valueOf("1.0+abc"));
|
||||
assertOptionalEquals("1.0.0+abc", VersionHelper.valueOf("1+abc"));
|
||||
assertOptionalEquals("1.0.0-def+abc", VersionHelper.valueOf("1.0.0-def+abc"));
|
||||
assertOptionalEquals("1.0.0-def+abc", VersionHelper.valueOf("1.0-def+abc"));
|
||||
assertOptionalEquals("1.0.0-def+abc", VersionHelper.valueOf("1-def+abc"));
|
||||
assertOptionalEquals("1.0.0+def-abc", VersionHelper.valueOf("1.0.0+def-abc"));
|
||||
assertOptionalEquals("1.0.0+def-abc", VersionHelper.valueOf("1.0+def-abc"));
|
||||
assertOptionalEquals("1.0.0+def-abc", VersionHelper.valueOf("1+def-abc"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -83,8 +83,8 @@ public class CustomProxySelectorTest {
|
||||
public void testProxyPACHTTPWithLogin() throws Exception {
|
||||
preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO);
|
||||
preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_http.pac").toExternalForm());
|
||||
preferences.put(Constants.PREF_PROXY_AUTO_USERNAME, "auto");
|
||||
preferences.put(Constants.PREF_PROXY_AUTO_PASSWORD, "autopassword");
|
||||
preferences.put(Constants.PREF_PROXY_USERNAME, "auto");
|
||||
preferences.put(Constants.PREF_PROXY_PASSWORD, "autopassword");
|
||||
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
|
||||
Proxy proxy = proxySelector.getProxyFor(uri);
|
||||
|
||||
@ -154,8 +154,8 @@ public class CustomProxySelectorTest {
|
||||
preferences.put(Constants.PREF_PROXY_MANUAL_TYPE, Constants.PROXY_MANUAL_TYPE_HTTP);
|
||||
preferences.put(Constants.PREF_PROXY_MANUAL_HOSTNAME, "localhost");
|
||||
preferences.put(Constants.PREF_PROXY_MANUAL_PORT, "8080");
|
||||
preferences.put(Constants.PREF_PROXY_MANUAL_USERNAME, "username");
|
||||
preferences.put(Constants.PREF_PROXY_MANUAL_PASSWORD, "pwd");
|
||||
preferences.put(Constants.PREF_PROXY_USERNAME, "username");
|
||||
preferences.put(Constants.PREF_PROXY_PASSWORD, "pwd");
|
||||
|
||||
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
|
||||
Proxy proxy = proxySelector.getProxyFor(uri);
|
||||
|
@ -35,18 +35,29 @@ import org.junit.Test;
|
||||
import processing.app.helpers.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class MergeSketchWithUploaderTest {
|
||||
|
||||
private File sketch;
|
||||
private File bootloader;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
File originalSketch = new File(MergeSketchWithUploaderTest.class.getResource("/sketch.hex").getFile());
|
||||
File originalSketch = getResourceFile("/sketch.hex");
|
||||
sketch = new File(System.getProperty("java.io.tmpdir"), "sketch.hex");
|
||||
FileUtils.copyFile(originalSketch, sketch);
|
||||
removeCariageReturns(sketch);
|
||||
|
||||
File originalBootloader = getResourceFile("/optiboot_atmega328.hex");
|
||||
bootloader = new File(System.getProperty("java.io.tmpdir"), "optiboot_atmega328.hex");
|
||||
FileUtils.copyFile(originalBootloader, bootloader);
|
||||
removeCariageReturns(bootloader);
|
||||
}
|
||||
|
||||
@After
|
||||
@ -57,11 +68,24 @@ public class MergeSketchWithUploaderTest {
|
||||
@Test
|
||||
public void shouldMergeWithOptiboot() throws Exception {
|
||||
assertEquals(11720, sketch.length());
|
||||
assertEquals(1432, bootloader.length());
|
||||
|
||||
File bootloader = new File(MergeSketchWithUploaderTest.class.getResource("/optiboot_atmega328.hex").getFile());
|
||||
File bootloader = getResourceFile("/optiboot_atmega328.hex");
|
||||
new MergeSketchWithBooloader().merge(sketch, bootloader);
|
||||
assertEquals(13140, sketch.length());
|
||||
}
|
||||
|
||||
|
||||
private static File getResourceFile(String resourcePath) throws Exception {
|
||||
return new File(URLDecoder.decode(
|
||||
MergeSketchWithUploaderTest.class.getResource(resourcePath).getFile(), "UTF-8"));
|
||||
}
|
||||
|
||||
private static void removeCariageReturns(File file) throws Exception {
|
||||
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
for(String line : lines) {
|
||||
contentBuilder.append(line).append('\n');
|
||||
}
|
||||
Files.write(file.toPath(), contentBuilder.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import static org.junit.Assert.*;
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.fest.assertions.Assertions;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -127,4 +128,35 @@ public class CommandLineTest {
|
||||
prefs = new PreferencesMap(prefFile);
|
||||
assertEquals("preference should be saved", "xxx", prefs.get("test_pref"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommandLineVersion() throws Exception {
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Process pr = rt.exec(new String[]{
|
||||
arduinoPath.getAbsolutePath(),
|
||||
"--version",
|
||||
});
|
||||
pr.waitFor();
|
||||
|
||||
Assertions.assertThat(pr.exitValue())
|
||||
.as("Process will finish with exit code 0 in --version")
|
||||
.isEqualTo(0);
|
||||
Assertions.assertThat(new String(IOUtils.toByteArray(pr.getInputStream())))
|
||||
.matches("Arduino: \\d+\\.\\d+\\.\\d+.*\r?\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommandLineMultipleAction() throws Exception {
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Process pr = rt.exec(new String[]{
|
||||
arduinoPath.getAbsolutePath(),
|
||||
"--version",
|
||||
"--verify",
|
||||
});
|
||||
pr.waitFor();
|
||||
|
||||
Assertions.assertThat(pr.exitValue())
|
||||
.as("Multiple Action will be rejected")
|
||||
.isEqualTo(3);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2020 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
@ -23,20 +25,34 @@
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
|
||||
*/
|
||||
|
||||
package processing.app.helpers;
|
||||
|
||||
import org.junit.Test;
|
||||
package processing.app;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class StringUtilsTest {
|
||||
import org.junit.Test;
|
||||
|
||||
public class SerialTest {
|
||||
class NullSerial extends Serial {
|
||||
public NullSerial() throws SerialException {
|
||||
super("none", 0, 'n', 0, 0, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void message(char[] chars, int length) {
|
||||
output += new String(chars, 0, length);
|
||||
}
|
||||
|
||||
String output = "";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldJoinAnArray() {
|
||||
assertEquals("1 - 2 - 3", StringUtils.join(new String[]{"1", "2", "3"}, " - "));
|
||||
public void testSerialUTF8Decoder() throws Exception {
|
||||
NullSerial s = new NullSerial();
|
||||
// https://github.com/arduino/Arduino/issues/9808
|
||||
String testdata = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789°0123456789";
|
||||
s.processSerialEvent(testdata.getBytes());
|
||||
assertEquals(s.output, testdata);
|
||||
}
|
||||
}
|
91
app/test/processing/app/UpdateTextAreaActionTest.java
Normal file
91
app/test/processing/app/UpdateTextAreaActionTest.java
Normal file
@ -0,0 +1,91 @@
|
||||
package processing.app;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import cc.arduino.packages.BoardPort;
|
||||
import processing.app.helpers.PreferencesMap;
|
||||
|
||||
public class UpdateTextAreaActionTest {
|
||||
|
||||
private static final String TIMESTAMP_REGEX = "\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d";
|
||||
|
||||
class DummyTextMonitor extends AbstractTextMonitor {
|
||||
public DummyTextMonitor(BoardPort boardPort) {
|
||||
super(boardPort);
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
PreferencesData.defaults = new PreferencesMap();
|
||||
PreferencesData.set("editor.font", "Monospaced,plain,12");
|
||||
PreferencesData.set("gui.scale", "100");
|
||||
Theme.defaults = new PreferencesMap();
|
||||
Theme.table.put("console.font", "Monospaced,plain,12");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noTimestampAdded() {
|
||||
DummyTextMonitor textMon = new DummyTextMonitor(new BoardPort());
|
||||
textMon.addTimeStampBox.setSelected(false);
|
||||
|
||||
textMon.updateTextArea("line1\nline2\r\nline3");
|
||||
assertThat(textMon.textArea.getText()).matches("line1\nline2\r\nline3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void all3LinesHaveTimestampAdded() {
|
||||
DummyTextMonitor textMon = new DummyTextMonitor(new BoardPort());
|
||||
textMon.addTimeStampBox.setSelected(true);
|
||||
|
||||
textMon.updateTextArea("line1\nline2\r\nline3");
|
||||
assertThat(textMon.textArea.getText())
|
||||
.matches(TIMESTAMP_REGEX + " -> line1\\n" + //
|
||||
TIMESTAMP_REGEX + " -> line2\\r\\n" + //
|
||||
TIMESTAMP_REGEX + " -> line3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyLinesHaveTimestampToo() {
|
||||
DummyTextMonitor textMon = new DummyTextMonitor(new BoardPort());
|
||||
textMon.addTimeStampBox.setSelected(true);
|
||||
|
||||
textMon.updateTextArea("line_1\n\nline_2");
|
||||
assertThat(textMon.textArea.getText())
|
||||
.matches(TIMESTAMP_REGEX + " -> line_1\\n" + //
|
||||
TIMESTAMP_REGEX + " -> \\n" + //
|
||||
TIMESTAMP_REGEX + " -> line_2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newLinesAreRememberedWhenNewBufferIsUsed() {
|
||||
DummyTextMonitor textMon = new DummyTextMonitor(new BoardPort());
|
||||
textMon.addTimeStampBox.setSelected(true);
|
||||
|
||||
textMon.updateTextArea("no newline");
|
||||
assertThat(textMon.textArea.getText())
|
||||
.matches(TIMESTAMP_REGEX + " -> no newline");
|
||||
|
||||
textMon.updateTextArea(" more text");
|
||||
assertThat(textMon.textArea.getText())
|
||||
.matches(TIMESTAMP_REGEX + " -> no newline more text");
|
||||
|
||||
textMon.updateTextArea("\n");
|
||||
assertThat(textMon.textArea.getText())
|
||||
.matches(TIMESTAMP_REGEX + " -> no newline more text\n");
|
||||
|
||||
textMon.updateTextArea("\n");
|
||||
assertThat(textMon.textArea.getText())
|
||||
.matches(TIMESTAMP_REGEX + " -> no newline more text\n" + //
|
||||
TIMESTAMP_REGEX + " -> \n");
|
||||
|
||||
textMon.updateTextArea("third line");
|
||||
assertThat(textMon.textArea.getText())
|
||||
.matches(TIMESTAMP_REGEX + " -> no newline more text\n" + //
|
||||
TIMESTAMP_REGEX + " -> \n" + //
|
||||
TIMESTAMP_REGEX + " -> third line");
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
= Ethernet Library for Arduino =
|
||||
= Bridge Library for Arduino =
|
||||
|
||||
With the Arduino Ethernet Shield, this library allows an Arduino board to connect to the internet.
|
||||
The Bridge library simplifies communication between the ATmega32U4 and the AR9331.
|
||||
|
||||
For more information about this library please visit us at
|
||||
http://www.arduino.cc/en/Reference/Ethernet
|
||||
http://www.arduino.cc/en/Reference/YunBridgeLibrary
|
||||
|
||||
== License ==
|
||||
|
||||
Copyright (c) 2010 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2014 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
183
app/testdata/libraries/Bridge_1.6.3/Bridge/examples/Bridge/Bridge.ino
vendored
Normal file
183
app/testdata/libraries/Bridge_1.6.3/Bridge/examples/Bridge/Bridge.ino
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
Arduino Yún Bridge example
|
||||
|
||||
This example for the YunShield/Yún shows how
|
||||
to use the Bridge library to access the digital and
|
||||
analog pins on the board through REST calls.
|
||||
It demonstrates how you can create your own API when
|
||||
using REST style calls through the browser.
|
||||
|
||||
Possible commands created in this shetch:
|
||||
|
||||
"/arduino/digital/13" -> digitalRead(13)
|
||||
"/arduino/digital/13/1" -> digitalWrite(13, HIGH)
|
||||
"/arduino/analog/2/123" -> analogWrite(2, 123)
|
||||
"/arduino/analog/2" -> analogRead(2)
|
||||
"/arduino/mode/13/input" -> pinMode(13, INPUT)
|
||||
"/arduino/mode/13/output" -> pinMode(13, OUTPUT)
|
||||
|
||||
This example code is part of the public domain
|
||||
|
||||
http://www.arduino.cc/en/Tutorial/Bridge
|
||||
|
||||
*/
|
||||
|
||||
#include <Bridge.h>
|
||||
#include <BridgeServer.h>
|
||||
#include <BridgeClient.h>
|
||||
|
||||
// Listen to the default port 5555, the Yún webserver
|
||||
// will forward there all the HTTP requests you send
|
||||
BridgeServer server;
|
||||
|
||||
void setup() {
|
||||
// Bridge startup
|
||||
pinMode(13, OUTPUT);
|
||||
digitalWrite(13, LOW);
|
||||
Bridge.begin();
|
||||
digitalWrite(13, HIGH);
|
||||
|
||||
// Listen for incoming connection only from localhost
|
||||
// (no one from the external network could connect)
|
||||
server.listenOnLocalhost();
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Get clients coming from server
|
||||
BridgeClient client = server.accept();
|
||||
|
||||
// There is a new client?
|
||||
if (client) {
|
||||
// Process request
|
||||
process(client);
|
||||
|
||||
// Close connection and free resources.
|
||||
client.stop();
|
||||
}
|
||||
|
||||
delay(50); // Poll every 50ms
|
||||
}
|
||||
|
||||
void process(BridgeClient client) {
|
||||
// read the command
|
||||
String command = client.readStringUntil('/');
|
||||
|
||||
// is "digital" command?
|
||||
if (command == "digital") {
|
||||
digitalCommand(client);
|
||||
}
|
||||
|
||||
// is "analog" command?
|
||||
if (command == "analog") {
|
||||
analogCommand(client);
|
||||
}
|
||||
|
||||
// is "mode" command?
|
||||
if (command == "mode") {
|
||||
modeCommand(client);
|
||||
}
|
||||
}
|
||||
|
||||
void digitalCommand(BridgeClient client) {
|
||||
int pin, value;
|
||||
|
||||
// Read pin number
|
||||
pin = client.parseInt();
|
||||
|
||||
// If the next character is a '/' it means we have an URL
|
||||
// with a value like: "/digital/13/1"
|
||||
if (client.read() == '/') {
|
||||
value = client.parseInt();
|
||||
digitalWrite(pin, value);
|
||||
} else {
|
||||
value = digitalRead(pin);
|
||||
}
|
||||
|
||||
// Send feedback to client
|
||||
client.print(F("Pin D"));
|
||||
client.print(pin);
|
||||
client.print(F(" set to "));
|
||||
client.println(value);
|
||||
|
||||
// Update datastore key with the current pin value
|
||||
String key = "D";
|
||||
key += pin;
|
||||
Bridge.put(key, String(value));
|
||||
}
|
||||
|
||||
void analogCommand(BridgeClient client) {
|
||||
int pin, value;
|
||||
|
||||
// Read pin number
|
||||
pin = client.parseInt();
|
||||
|
||||
// If the next character is a '/' it means we have an URL
|
||||
// with a value like: "/analog/5/120"
|
||||
if (client.read() == '/') {
|
||||
// Read value and execute command
|
||||
value = client.parseInt();
|
||||
analogWrite(pin, value);
|
||||
|
||||
// Send feedback to client
|
||||
client.print(F("Pin D"));
|
||||
client.print(pin);
|
||||
client.print(F(" set to analog "));
|
||||
client.println(value);
|
||||
|
||||
// Update datastore key with the current pin value
|
||||
String key = "D";
|
||||
key += pin;
|
||||
Bridge.put(key, String(value));
|
||||
} else {
|
||||
// Read analog pin
|
||||
value = analogRead(pin);
|
||||
|
||||
// Send feedback to client
|
||||
client.print(F("Pin A"));
|
||||
client.print(pin);
|
||||
client.print(F(" reads analog "));
|
||||
client.println(value);
|
||||
|
||||
// Update datastore key with the current pin value
|
||||
String key = "A";
|
||||
key += pin;
|
||||
Bridge.put(key, String(value));
|
||||
}
|
||||
}
|
||||
|
||||
void modeCommand(BridgeClient client) {
|
||||
int pin;
|
||||
|
||||
// Read pin number
|
||||
pin = client.parseInt();
|
||||
|
||||
// If the next character is not a '/' we have a malformed URL
|
||||
if (client.read() != '/') {
|
||||
client.println(F("error"));
|
||||
return;
|
||||
}
|
||||
|
||||
String mode = client.readStringUntil('\r');
|
||||
|
||||
if (mode == "input") {
|
||||
pinMode(pin, INPUT);
|
||||
// Send feedback to client
|
||||
client.print(F("Pin D"));
|
||||
client.print(pin);
|
||||
client.print(F(" configured as INPUT!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == "output") {
|
||||
pinMode(pin, OUTPUT);
|
||||
// Send feedback to client
|
||||
client.print(F("Pin D"));
|
||||
client.print(pin);
|
||||
client.print(F(" configured as OUTPUT!"));
|
||||
return;
|
||||
}
|
||||
|
||||
client.print(F("error: invalid mode "));
|
||||
client.print(mode);
|
||||
}
|
95
app/testdata/libraries/Bridge_1.6.3/Bridge/examples/ConsoleAsciiTable/ConsoleAsciiTable.ino
vendored
Normal file
95
app/testdata/libraries/Bridge_1.6.3/Bridge/examples/ConsoleAsciiTable/ConsoleAsciiTable.ino
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
Console ASCII table for YunShield/Yún
|
||||
Prints out byte values in all possible formats:
|
||||
* as raw binary values
|
||||
* as ASCII-encoded decimal, hex, octal, and binary values
|
||||
|
||||
For more on ASCII, see http://www.asciitable.com and http://en.wikipedia.org/wiki/ASCII
|
||||
|
||||
The circuit:
|
||||
- YunShield/Yún
|
||||
|
||||
created 2006
|
||||
by Nicholas Zambetti
|
||||
http://www.zambetti.com
|
||||
modified 9 Apr 2012
|
||||
by Tom Igoe
|
||||
modified 22 May 2013
|
||||
by Cristian Maglie
|
||||
|
||||
This example code is in the public domain.
|
||||
|
||||
http://www.arduino.cc/en/Tutorial/ConsoleAsciiTable
|
||||
|
||||
*/
|
||||
|
||||
#include <Console.h>
|
||||
|
||||
void setup() {
|
||||
//Initialize Console and wait for port to open:
|
||||
Bridge.begin();
|
||||
Console.begin();
|
||||
|
||||
// Uncomment the following line to enable buffering:
|
||||
// - better transmission speed and efficiency
|
||||
// - needs to call Console.flush() to ensure that all
|
||||
// transmitted data is sent
|
||||
|
||||
//Console.buffer(64);
|
||||
|
||||
while (!Console) {
|
||||
; // wait for Console port to connect.
|
||||
}
|
||||
|
||||
// prints title with ending line break
|
||||
Console.println("ASCII Table ~ Character Map");
|
||||
}
|
||||
|
||||
// first visible ASCIIcharacter '!' is number 33:
|
||||
int thisByte = 33;
|
||||
// you can also write ASCII characters in single quotes.
|
||||
// for example. '!' is the same as 33, so you could also use this:
|
||||
//int thisByte = '!';
|
||||
|
||||
void loop() {
|
||||
// prints value unaltered, i.e. the raw binary version of the
|
||||
// byte. The Console monitor interprets all bytes as
|
||||
// ASCII, so 33, the first number, will show up as '!'
|
||||
Console.write(thisByte);
|
||||
|
||||
Console.print(", dec: ");
|
||||
// prints value as string as an ASCII-encoded decimal (base 10).
|
||||
// Decimal is the default format for Console.print() and Console.println(),
|
||||
// so no modifier is needed:
|
||||
Console.print(thisByte);
|
||||
// But you can declare the modifier for decimal if you want to.
|
||||
//this also works if you uncomment it:
|
||||
|
||||
// Console.print(thisByte, DEC);
|
||||
|
||||
Console.print(", hex: ");
|
||||
// prints value as string in hexadecimal (base 16):
|
||||
Console.print(thisByte, HEX);
|
||||
|
||||
Console.print(", oct: ");
|
||||
// prints value as string in octal (base 8);
|
||||
Console.print(thisByte, OCT);
|
||||
|
||||
Console.print(", bin: ");
|
||||
// prints value as string in binary (base 2)
|
||||
// also prints ending line break:
|
||||
Console.println(thisByte, BIN);
|
||||
|
||||
// if printed last visible character '~' or 126, stop:
|
||||
if (thisByte == 126) { // you could also use if (thisByte == '~') {
|
||||
// ensure the latest bit of data is sent
|
||||
Console.flush();
|
||||
|
||||
// This loop loops forever and does nothing
|
||||
while (true) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// go on to the next character
|
||||
thisByte++;
|
||||
}
|
63
app/testdata/libraries/Bridge_1.6.3/Bridge/examples/ConsolePixel/ConsolePixel.ino
vendored
Normal file
63
app/testdata/libraries/Bridge_1.6.3/Bridge/examples/ConsolePixel/ConsolePixel.ino
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Console Pixel
|
||||
|
||||
An example of using YunShield/Yún board to receive data from the
|
||||
Console on the Yún. In this case, the board turns on an LED when
|
||||
it receives the character 'H', and turns off the LED when it
|
||||
receives the character 'L'.
|
||||
|
||||
To see the Console, pick your Yún's name and IP address in the Port menu
|
||||
then open the Port Monitor. You can also see it by opening a terminal window
|
||||
and typing
|
||||
ssh root@ yourYunsName.local 'telnet localhost 6571'
|
||||
then pressing enter. When prompted for the password, enter it.
|
||||
|
||||
|
||||
The circuit:
|
||||
* LED connected from digital pin 13 to ground
|
||||
|
||||
created 2006
|
||||
by David A. Mellis
|
||||
modified 25 Jun 2013
|
||||
by Tom Igoe
|
||||
|
||||
This example code is in the public domain.
|
||||
|
||||
http://www.arduino.cc/en/Tutorial/ConsolePixel
|
||||
|
||||
*/
|
||||
|
||||
#include <Console.h>
|
||||
|
||||
const int ledPin = 13; // the pin that the LED is attached to
|
||||
char incomingByte; // a variable to read incoming Console data into
|
||||
|
||||
void setup() {
|
||||
Bridge.begin(); // Initialize Bridge
|
||||
Console.begin(); // Initialize Console
|
||||
|
||||
// Wait for the Console port to connect
|
||||
while (!Console);
|
||||
|
||||
Console.println("type H or L to turn pin 13 on or off");
|
||||
|
||||
// initialize the LED pin as an output:
|
||||
pinMode(ledPin, OUTPUT);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// see if there's incoming Console data:
|
||||
if (Console.available() > 0) {
|
||||
// read the oldest byte in the Console buffer:
|
||||
incomingByte = Console.read();
|
||||
Console.println(incomingByte);
|
||||
// if it's a capital H (ASCII 72), turn on the LED:
|
||||
if (incomingByte == 'H') {
|
||||
digitalWrite(ledPin, HIGH);
|
||||
}
|
||||
// if it's an L (ASCII 76) turn off the LED:
|
||||
if (incomingByte == 'L') {
|
||||
digitalWrite(ledPin, LOW);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user