├── .classpath
├── .gitignore
├── .project
├── LICENSE
├── META-INF
└── MANIFEST.MF
├── OSGI-INF
└── l10n
│ ├── bundle.properties
│ └── bundle_fr.properties
├── README.md
├── build.properties
├── contexts.xml
├── icons
├── bullets.png
├── change.gif
├── clear.png
├── clear_co.png
├── deadlock_view.png
├── file-new-16x16.png
├── note.png
├── save_edit.png
├── settings_obj.png
├── style_bold.gif
├── style_italic.gif
├── style_strikeout.png
├── style_underline.gif
└── web.png
├── plugin.xml
├── screenshot.png
└── src
└── io
└── github
└── pyvesb
└── notepad4e
├── Notepad4e.java
├── preferences
├── PreferenceInitializer.java
├── PreferencePage.java
└── Preferences.java
├── strings
├── LocalStrings.java
├── localstrings.properties
└── localstrings_fr.properties
├── utils
├── AbstractSelectedNoteAction.java
├── NotepadAction.java
├── ShortcutHandler.java
└── UndoRedoManager.java
└── views
├── Note.java
└── NotepadView.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Mobile Tools for Java (J2ME)
4 | .mtj.tmp/
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12 | hs_err_pid*
13 |
14 | # Eclipse files
15 | .settings
16 | /bin/
17 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Notepad4e
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.pde.ManifestBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.pde.SchemaBuilder
20 |
21 |
22 |
23 |
24 |
25 | org.eclipse.pde.PluginNature
26 | org.eclipse.jdt.core.javanature
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Eclipse Public License - v 2.0
2 |
3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
6 |
7 | 1. DEFINITIONS
8 |
9 | "Contribution" means:
10 |
11 | a) in the case of the initial Contributor, the initial content
12 | Distributed under this Agreement, and
13 |
14 | b) in the case of each subsequent Contributor:
15 | i) changes to the Program, and
16 | ii) additions to the Program;
17 | where such changes and/or additions to the Program originate from
18 | and are Distributed by that particular Contributor. A Contribution
19 | "originates" from a Contributor if it was added to the Program by
20 | such Contributor itself or anyone acting on such Contributor's behalf.
21 | Contributions do not include changes or additions to the Program that
22 | are not Modified Works.
23 |
24 | "Contributor" means any person or entity that Distributes the Program.
25 |
26 | "Licensed Patents" mean patent claims licensable by a Contributor which
27 | are necessarily infringed by the use or sale of its Contribution alone
28 | or when combined with the Program.
29 |
30 | "Program" means the Contributions Distributed in accordance with this
31 | Agreement.
32 |
33 | "Recipient" means anyone who receives the Program under this Agreement
34 | or any Secondary License (as applicable), including Contributors.
35 |
36 | "Derivative Works" shall mean any work, whether in Source Code or other
37 | form, that is based on (or derived from) the Program and for which the
38 | editorial revisions, annotations, elaborations, or other modifications
39 | represent, as a whole, an original work of authorship.
40 |
41 | "Modified Works" shall mean any work in Source Code or other form that
42 | results from an addition to, deletion from, or modification of the
43 | contents of the Program, including, for purposes of clarity any new file
44 | in Source Code form that contains any contents of the Program. Modified
45 | Works shall not include works that contain only declarations,
46 | interfaces, types, classes, structures, or files of the Program solely
47 | in each case in order to link to, bind by name, or subclass the Program
48 | or Modified Works thereof.
49 |
50 | "Distribute" means the acts of a) distributing or b) making available
51 | in any manner that enables the transfer of a copy.
52 |
53 | "Source Code" means the form of a Program preferred for making
54 | modifications, including but not limited to software source code,
55 | documentation source, and configuration files.
56 |
57 | "Secondary License" means either the GNU General Public License,
58 | Version 2.0, or any later versions of that license, including any
59 | exceptions or additional permissions as identified by the initial
60 | Contributor.
61 |
62 | 2. GRANT OF RIGHTS
63 |
64 | a) Subject to the terms of this Agreement, each Contributor hereby
65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright
66 | license to reproduce, prepare Derivative Works of, publicly display,
67 | publicly perform, Distribute and sublicense the Contribution of such
68 | Contributor, if any, and such Derivative Works.
69 |
70 | b) Subject to the terms of this Agreement, each Contributor hereby
71 | grants Recipient a non-exclusive, worldwide, royalty-free patent
72 | license under Licensed Patents to make, use, sell, offer to sell,
73 | import and otherwise transfer the Contribution of such Contributor,
74 | if any, in Source Code or other form. This patent license shall
75 | apply to the combination of the Contribution and the Program if, at
76 | the time the Contribution is added by the Contributor, such addition
77 | of the Contribution causes such combination to be covered by the
78 | Licensed Patents. The patent license shall not apply to any other
79 | combinations which include the Contribution. No hardware per se is
80 | licensed hereunder.
81 |
82 | c) Recipient understands that although each Contributor grants the
83 | licenses to its Contributions set forth herein, no assurances are
84 | provided by any Contributor that the Program does not infringe the
85 | patent or other intellectual property rights of any other entity.
86 | Each Contributor disclaims any liability to Recipient for claims
87 | brought by any other entity based on infringement of intellectual
88 | property rights or otherwise. As a condition to exercising the
89 | rights and licenses granted hereunder, each Recipient hereby
90 | assumes sole responsibility to secure any other intellectual
91 | property rights needed, if any. For example, if a third party
92 | patent license is required to allow Recipient to Distribute the
93 | Program, it is Recipient's responsibility to acquire that license
94 | before distributing the Program.
95 |
96 | d) Each Contributor represents that to its knowledge it has
97 | sufficient copyright rights in its Contribution, if any, to grant
98 | the copyright license set forth in this Agreement.
99 |
100 | e) Notwithstanding the terms of any Secondary License, no
101 | Contributor makes additional grants to any Recipient (other than
102 | those set forth in this Agreement) as a result of such Recipient's
103 | receipt of the Program under the terms of a Secondary License
104 | (if permitted under the terms of Section 3).
105 |
106 | 3. REQUIREMENTS
107 |
108 | 3.1 If a Contributor Distributes the Program in any form, then:
109 |
110 | a) the Program must also be made available as Source Code, in
111 | accordance with section 3.2, and the Contributor must accompany
112 | the Program with a statement that the Source Code for the Program
113 | is available under this Agreement, and informs Recipients how to
114 | obtain it in a reasonable manner on or through a medium customarily
115 | used for software exchange; and
116 |
117 | b) the Contributor may Distribute the Program under a license
118 | different than this Agreement, provided that such license:
119 | i) effectively disclaims on behalf of all other Contributors all
120 | warranties and conditions, express and implied, including
121 | warranties or conditions of title and non-infringement, and
122 | implied warranties or conditions of merchantability and fitness
123 | for a particular purpose;
124 |
125 | ii) effectively excludes on behalf of all other Contributors all
126 | liability for damages, including direct, indirect, special,
127 | incidental and consequential damages, such as lost profits;
128 |
129 | iii) does not attempt to limit or alter the recipients' rights
130 | in the Source Code under section 3.2; and
131 |
132 | iv) requires any subsequent distribution of the Program by any
133 | party to be under a license that satisfies the requirements
134 | of this section 3.
135 |
136 | 3.2 When the Program is Distributed as Source Code:
137 |
138 | a) it must be made available under this Agreement, or if the
139 | Program (i) is combined with other material in a separate file or
140 | files made available under a Secondary License, and (ii) the initial
141 | Contributor attached to the Source Code the notice described in
142 | Exhibit A of this Agreement, then the Program may be made available
143 | under the terms of such Secondary Licenses, and
144 |
145 | b) a copy of this Agreement must be included with each copy of
146 | the Program.
147 |
148 | 3.3 Contributors may not remove or alter any copyright, patent,
149 | trademark, attribution notices, disclaimers of warranty, or limitations
150 | of liability ("notices") contained within the Program from any copy of
151 | the Program which they Distribute, provided that Contributors may add
152 | their own appropriate notices.
153 |
154 | 4. COMMERCIAL DISTRIBUTION
155 |
156 | Commercial distributors of software may accept certain responsibilities
157 | with respect to end users, business partners and the like. While this
158 | license is intended to facilitate the commercial use of the Program,
159 | the Contributor who includes the Program in a commercial product
160 | offering should do so in a manner which does not create potential
161 | liability for other Contributors. Therefore, if a Contributor includes
162 | the Program in a commercial product offering, such Contributor
163 | ("Commercial Contributor") hereby agrees to defend and indemnify every
164 | other Contributor ("Indemnified Contributor") against any losses,
165 | damages and costs (collectively "Losses") arising from claims, lawsuits
166 | and other legal actions brought by a third party against the Indemnified
167 | Contributor to the extent caused by the acts or omissions of such
168 | Commercial Contributor in connection with its distribution of the Program
169 | in a commercial product offering. The obligations in this section do not
170 | apply to any claims or Losses relating to any actual or alleged
171 | intellectual property infringement. In order to qualify, an Indemnified
172 | Contributor must: a) promptly notify the Commercial Contributor in
173 | writing of such claim, and b) allow the Commercial Contributor to control,
174 | and cooperate with the Commercial Contributor in, the defense and any
175 | related settlement negotiations. The Indemnified Contributor may
176 | participate in any such claim at its own expense.
177 |
178 | For example, a Contributor might include the Program in a commercial
179 | product offering, Product X. That Contributor is then a Commercial
180 | Contributor. If that Commercial Contributor then makes performance
181 | claims, or offers warranties related to Product X, those performance
182 | claims and warranties are such Commercial Contributor's responsibility
183 | alone. Under this section, the Commercial Contributor would have to
184 | defend claims against the other Contributors related to those performance
185 | claims and warranties, and if a court requires any other Contributor to
186 | pay any damages as a result, the Commercial Contributor must pay
187 | those damages.
188 |
189 | 5. NO WARRANTY
190 |
191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
196 | PURPOSE. Each Recipient is solely responsible for determining the
197 | appropriateness of using and distributing the Program and assumes all
198 | risks associated with its exercise of rights under this Agreement,
199 | including but not limited to the risks and costs of program errors,
200 | compliance with applicable laws, damage to or loss of data, programs
201 | or equipment, and unavailability or interruption of operations.
202 |
203 | 6. DISCLAIMER OF LIABILITY
204 |
205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
213 | POSSIBILITY OF SUCH DAMAGES.
214 |
215 | 7. GENERAL
216 |
217 | If any provision of this Agreement is invalid or unenforceable under
218 | applicable law, it shall not affect the validity or enforceability of
219 | the remainder of the terms of this Agreement, and without further
220 | action by the parties hereto, such provision shall be reformed to the
221 | minimum extent necessary to make such provision valid and enforceable.
222 |
223 | If Recipient institutes patent litigation against any entity
224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the
225 | Program itself (excluding combinations of the Program with other software
226 | or hardware) infringes such Recipient's patent(s), then such Recipient's
227 | rights granted under Section 2(b) shall terminate as of the date such
228 | litigation is filed.
229 |
230 | All Recipient's rights under this Agreement shall terminate if it
231 | fails to comply with any of the material terms or conditions of this
232 | Agreement and does not cure such failure in a reasonable period of
233 | time after becoming aware of such noncompliance. If all Recipient's
234 | rights under this Agreement terminate, Recipient agrees to cease use
235 | and distribution of the Program as soon as reasonably practicable.
236 | However, Recipient's obligations under this Agreement and any licenses
237 | granted by Recipient relating to the Program shall continue and survive.
238 |
239 | Everyone is permitted to copy and distribute copies of this Agreement,
240 | but in order to avoid inconsistency the Agreement is copyrighted and
241 | may only be modified in the following manner. The Agreement Steward
242 | reserves the right to publish new versions (including revisions) of
243 | this Agreement from time to time. No one other than the Agreement
244 | Steward has the right to modify this Agreement. The Eclipse Foundation
245 | is the initial Agreement Steward. The Eclipse Foundation may assign the
246 | responsibility to serve as the Agreement Steward to a suitable separate
247 | entity. Each new version of the Agreement will be given a distinguishing
248 | version number. The Program (including Contributions) may always be
249 | Distributed subject to the version of the Agreement under which it was
250 | received. In addition, after a new version of the Agreement is published,
251 | Contributor may elect to Distribute the Program (including its
252 | Contributions) under the new version.
253 |
254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
255 | receives no rights or licenses to the intellectual property of any
256 | Contributor under this Agreement, whether expressly, by implication,
257 | estoppel or otherwise. All rights in the Program not expressly granted
258 | under this Agreement are reserved. Nothing in this Agreement is intended
259 | to be enforceable by any entity that is not a Contributor or Recipient.
260 | No third-party beneficiary rights are created under this Agreement.
261 |
262 | Exhibit A - Form of Secondary Licenses Notice
263 |
264 | "This Source Code may also be made available under the following
265 | Secondary Licenses when the conditions for such availability set forth
266 | in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
267 | version(s), and exceptions or additional permissions here}."
268 |
269 | Simply including a copy of this Agreement, including this Exhibit A
270 | is not sufficient to license the Source Code under Secondary Licenses.
271 |
272 | If it is not possible or desirable to put the notice in a particular
273 | file, then You may include the notice in a location (such as a LICENSE
274 | file in a relevant directory) where a recipient would be likely to
275 | look for such a notice.
276 |
277 | You may add additional accurate notices of copyright ownership.
278 |
--------------------------------------------------------------------------------
/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Bundle-ManifestVersion: 2
3 | Bundle-Name: Notepad4e
4 | Bundle-SymbolicName: Notepad4e;singleton:=true
5 | Bundle-Version: 1.5.3
6 | Bundle-Activator: io.github.pyvesb.notepad4e.Notepad4e
7 | Require-Bundle: org.eclipse.ui,
8 | org.eclipse.core.runtime
9 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8
10 | Bundle-ActivationPolicy: lazy
11 | Bundle-Localization: OSGI-INF/l10n/bundle
12 | Export-Package: io.github.pyvesb.notepad4e,
13 | io.github.pyvesb.notepad4e.preferences,
14 | io.github.pyvesb.notepad4e.views,
15 | io.github.pyvesb.notepad4e.utils,
16 | io.github.pyvesb.notepad4e.strings
17 |
--------------------------------------------------------------------------------
/OSGI-INF/l10n/bundle.properties:
--------------------------------------------------------------------------------
1 | #Properties file for Notepad4e
2 | category.name = General
3 | category.description = Category for Notepad4e commands
4 | command.description.new_note = Creates a new note
5 | command.name.new_note = New Note
6 | command.description.bold = Sets the style of the currently selected text to bold
7 | command.name.bold = Bold
8 | command.description.italic = Sets the style of the currently selected text to italic
9 | command.name.italic = Italic
10 | command.description.underline = Underlines the currently selected text
11 | command.name.underline = Underline
12 | command.description.strikeout = Strikes out the currently selected text
13 | command.name.strikeout = Strikethrough
14 | command.description.list = Turns the currently selected lines into a list
15 | command.name.list = Bullet List
16 | command.description.clear_style = Clears the style of the currently selected text
17 | command.name.clear_style = Clear Style
18 | command.description.clear_note = Clears the current note
19 | command.name.clear_note = Clear Note
20 | command.description.undo = Performs an undo action on the current note
21 | command.name.undo = Undo
22 | command.description.redo = Performs a redo action on the current note
23 | command.name.redo = Redo
24 | command.description.close = Closes the currently selected note
25 | command.name.close = Close Note
26 | context.name = In Notepad4e
--------------------------------------------------------------------------------
/OSGI-INF/l10n/bundle_fr.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/OSGI-INF/l10n/bundle_fr.properties
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Notepad4e
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | **Create highly customisable notes in Eclipse, and never lose track of your thoughts, ideas or code snippets!**
22 |
23 |
24 |
25 |
26 | A simple example of how the plugin can be used and customised. You have countless possibilities!
27 |
28 |
29 | # Features at a glance
30 |
31 | * Create as many notes as you like in a dedicated Eclipse view.
32 | * Modify the text style (font, color, bold, italic, underlined, strikeout, etc.).
33 | * Customise the appearance and functionality of the plugin via its preference page.
34 | * Create lists of text elements.
35 | * Automatically save the contents of your notes to a default or custom location.
36 | * Use editable shortcuts to boost your productivity.
37 | * Export notes as text files.
38 | * Lightweight and user-friendly interface.
39 | * Source code fully available.
40 | * More to discover and to come in future updates!
41 |
42 | Check out what's new in the [latest releases](https://github.com/PyvesB/Notepad4e/releases)!
43 |
44 | # Getting started
45 |
46 | #### :cd: Plugin installation
47 |
48 | You can download and install the plugin via the [Eclipse Marketplace](https://marketplace.eclipse.org/content/notepad4e), or simply drag the below button to your running Eclipse workspace:
49 |
50 |
51 |
52 |
53 |
54 | Alternatively, head to the [update site](https://pyvesb.github.io/Notepad4e/) and follow the instructions listed there.
55 |
56 | #### :wrench: Setting your own working copy of the project
57 |
58 | * Ensure you have a working version of the [JDK](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) (Java Development Kit).
59 | * Download the [RCP and RAP Developers](https://eclipse.org/downloads/eclipse-packages/) version of Eclipse.
60 | * Make a copy of the repository on your computer. You can also create your own fork by clicking on the *Fork* icon on the top right of this page.
61 | * In Eclipse, go to `File` -> `Import...` -> `Existing Projects into Workspace`.
62 | * In the `Select root directory` field, indicate the location where you downloaded the Notepad4e repository.
63 | * Tick the project that appears in the `Projects` field and click `Finish`.
64 | * You're ready to go! You can now either launch an instance of Eclipse running the plugin by right-clicking on the project and selecting `Run As` -> `Eclipse Application`, or you can export a plugin archive file by selecting `Export` -> `Deployable plug-ins and fragments`.
65 |
66 | # Contributing
67 |
68 | #### `$ code`
69 |
70 | Want to make Notepad4e better, faster, stronger? Contributions are more than welcome, open a **pull request** and share your code! Simply **fork** the repository by clicking on the icon on the top right of this page and you're ready to go!
71 |
72 | #### :speech_balloon: Support
73 |
74 | Thought of a cool idea? Found a problem or need some help? Simply open an [**issue**](https://github.com/PyvesB/Notepad4e/issues)!
75 |
76 | #### :star: Thanks
77 |
78 | Find the project useful, fun or interesting? **Star** the repository by clicking on the icon on the top right of this page!
79 |
80 | # License
81 |
82 | GNU General Public License v3.0
83 |
--------------------------------------------------------------------------------
/build.properties:
--------------------------------------------------------------------------------
1 | source.. = src/
2 | output.. = bin/
3 | bin.includes = plugin.xml,\
4 | META-INF/,\
5 | .,\
6 | icons/,\
7 | contexts.xml,\
8 | OSGI-INF/
9 |
--------------------------------------------------------------------------------
/contexts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Create highly customisable notes in Eclipse, and never lose track of your thoughts, ideas or code snippets!
5 |
6 |
7 |
--------------------------------------------------------------------------------
/icons/bullets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/bullets.png
--------------------------------------------------------------------------------
/icons/change.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/change.gif
--------------------------------------------------------------------------------
/icons/clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/clear.png
--------------------------------------------------------------------------------
/icons/clear_co.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/clear_co.png
--------------------------------------------------------------------------------
/icons/deadlock_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/deadlock_view.png
--------------------------------------------------------------------------------
/icons/file-new-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/file-new-16x16.png
--------------------------------------------------------------------------------
/icons/note.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/note.png
--------------------------------------------------------------------------------
/icons/save_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/save_edit.png
--------------------------------------------------------------------------------
/icons/settings_obj.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/settings_obj.png
--------------------------------------------------------------------------------
/icons/style_bold.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/style_bold.gif
--------------------------------------------------------------------------------
/icons/style_italic.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/style_italic.gif
--------------------------------------------------------------------------------
/icons/style_strikeout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/style_strikeout.png
--------------------------------------------------------------------------------
/icons/style_underline.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/style_underline.gif
--------------------------------------------------------------------------------
/icons/web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/icons/web.png
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
11 |
12 |
13 |
15 |
17 |
18 |
19 |
21 |
24 |
25 |
31 |
32 |
33 |
35 |
37 |
42 |
43 |
44 |
45 |
47 |
49 |
50 |
51 |
53 |
57 |
58 |
63 |
64 |
69 |
70 |
75 |
76 |
81 |
82 |
87 |
88 |
93 |
94 |
99 |
100 |
105 |
106 |
111 |
112 |
117 |
118 |
123 |
124 |
125 |
127 |
132 |
133 |
138 |
139 |
144 |
145 |
150 |
151 |
156 |
157 |
162 |
163 |
168 |
169 |
174 |
175 |
180 |
181 |
186 |
187 |
192 |
193 |
194 |
196 |
200 |
201 |
202 |
203 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/screenshot.png
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/Notepad4e.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 |
6 | import org.eclipse.core.runtime.IStatus;
7 | import org.eclipse.core.runtime.Status;
8 | import org.eclipse.core.runtime.preferences.InstanceScope;
9 | import org.eclipse.jface.dialogs.DialogSettings;
10 | import org.eclipse.jface.dialogs.IDialogSettings;
11 | import org.eclipse.ui.plugin.AbstractUIPlugin;
12 | import org.osgi.framework.BundleContext;
13 |
14 | import io.github.pyvesb.notepad4e.preferences.Preferences;
15 | import io.github.pyvesb.notepad4e.strings.LocalStrings;
16 |
17 | /**
18 | * Class used to control the plugin's life cycle.
19 | *
20 | * @author Pyves
21 | *
22 | */
23 | public class Notepad4e extends AbstractUIPlugin {
24 |
25 | // Plugin's ID.
26 | public static final String PLUGIN_ID = "Notepad4e";
27 |
28 | private static final String FN_DIALOG_SETTINGS_CUSTOM = "notepad4e.xml";
29 | private static final String FN_DIALOG_SETTINGS = "dialog_settings.xml";
30 |
31 | // Shared instance. Not ideal to use a static field here, but common practice to do this with AbstractUIPlugin.
32 | private static Notepad4e plugin;
33 |
34 | private IDialogSettings dialogSettings;
35 |
36 | @Override
37 | public void start(BundleContext context) throws Exception {
38 | super.start(context);
39 | plugin = this;
40 | }
41 |
42 | @Override
43 | public void stop(BundleContext context) throws Exception {
44 | plugin = null;
45 | super.stop(context);
46 | }
47 |
48 | @Override
49 | public IDialogSettings getDialogSettings() {
50 | if (dialogSettings == null) {
51 | restoreDialogSettings();
52 | }
53 | return dialogSettings;
54 | }
55 |
56 | @Override
57 | public void saveDialogSettings() {
58 | String directory = getDialogSettingsDirectory();
59 | saveDialogSettings(directory);
60 | }
61 |
62 | public void saveDialogSettings(String directory) {
63 | String settingsPath;
64 | if (directory == null || directory.isEmpty()) {
65 | settingsPath = getStateLocation().append(FN_DIALOG_SETTINGS).toOSString();
66 | } else {
67 | settingsPath = directory + File.separator + FN_DIALOG_SETTINGS_CUSTOM;
68 | }
69 | try {
70 | dialogSettings.save(settingsPath);
71 | } catch (IOException | IllegalStateException e) {
72 | // Ignore problems as in super.saveDialogSettings().
73 | }
74 | }
75 |
76 | public void restoreDialogSettings() {
77 | String directory = getDialogSettingsDirectory();
78 | if (directory == null || directory.isEmpty()) {
79 | dialogSettings = super.getDialogSettings();
80 | } else {
81 | dialogSettings = new DialogSettings("Workbench");
82 | String settingsPath = directory + File.separator + FN_DIALOG_SETTINGS_CUSTOM;
83 | File settingsFile = new File(settingsPath);
84 | if (settingsFile.exists()) {
85 | try {
86 | dialogSettings.load(settingsPath);
87 | } catch (IOException e) {
88 | getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, LocalStrings.getDialogSettingsErrorMsg, e));
89 | dialogSettings = super.getDialogSettings();
90 | }
91 | }
92 | }
93 | }
94 |
95 | private String getDialogSettingsDirectory() {
96 | return InstanceScope.INSTANCE.getNode(PLUGIN_ID).get(Preferences.SAVE_LOCATION, Preferences.SAVE_LOCATION_DEFAULT);
97 | }
98 |
99 | /**
100 | * Returns the shared instance.
101 | *
102 | * @return the shared instance
103 | */
104 | public static Notepad4e getDefault() {
105 | return plugin;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/preferences/PreferenceInitializer.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.preferences;
2 |
3 | import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
4 | import org.eclipse.jface.preference.IPreferenceStore;
5 |
6 | import io.github.pyvesb.notepad4e.Notepad4e;
7 |
8 | /**
9 | * Class used to initialise the default preference values.
10 | *
11 | * @author Pyves
12 | *
13 | */
14 | public class PreferenceInitializer extends AbstractPreferenceInitializer {
15 |
16 | /**
17 | * Initialises the default values of the parameters in the plugin's preference page.
18 | */
19 | @Override
20 | public void initializeDefaultPreferences() {
21 | IPreferenceStore store = Notepad4e.getDefault().getPreferenceStore();
22 | store.setDefault(Preferences.WRAP, Preferences.WRAP_DEFAULT);
23 | store.setDefault(Preferences.JUSTIFY, Preferences.JUSTIFY_DEFAULT);
24 | store.setDefault(Preferences.ALIGNMENT, Preferences.ALIGNMENT_DEFAULT);
25 | store.setDefault(Preferences.FONT, Preferences.FONT_DEFAULT);
26 | store.setDefault(Preferences.FONT_COLOR, Preferences.FONT_COLOR_DEFAULT);
27 | store.setDefault(Preferences.BACKGROUND_COLOR, Preferences.BACKGROUND_COLOR_DEFAULT);
28 | store.setDefault(Preferences.LINE_SPACING, Preferences.LINE_SPACING_DEFAULT);
29 | store.setDefault(Preferences.NAME_PREFIX, Preferences.NAME_PREFIX_DEFAULT);
30 | store.setDefault(Preferences.CLOSE_CONFIRMATION, Preferences.CLOSE_CONFIRMATION_DEFAULT);
31 | store.setDefault(Preferences.PASTE_CLIPBOARD_IN_NEW_NOTES, Preferences.PASTE_CLIPBOARD_IN_NEW_NOTES_DEFAULT);
32 | store.setDefault(Preferences.BULLET_SPACING, Preferences.BULLET_SPACING_DEFAULT);
33 | store.setDefault(Preferences.SAVE_INTERVAL, Preferences.SAVE_INTERVAL_DEFAULT);
34 | store.setDefault(Preferences.SAVE_LOCATION, Preferences.SAVE_LOCATION_DEFAULT);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/preferences/PreferencePage.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.preferences;
2 |
3 | import org.eclipse.jface.preference.*;
4 | import org.eclipse.ui.IWorkbenchPreferencePage;
5 |
6 | import io.github.pyvesb.notepad4e.Notepad4e;
7 | import io.github.pyvesb.notepad4e.strings.LocalStrings;
8 |
9 | import org.eclipse.ui.IWorkbench;
10 |
11 | /**
12 | * Class representing the preference page of the plugin.
13 | *
14 | * @author Pyves
15 | *
16 | */
17 | public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
18 |
19 | /**
20 | * Creates the field editors corresponding to the different preferences.
21 | */
22 | @Override
23 | public void createFieldEditors() {
24 | addField(new RadioGroupFieldEditor(Preferences.ALIGNMENT, LocalStrings.prefAlignment, 2,
25 | new String[][] { { LocalStrings.prefLeft, "left" }, { LocalStrings.prefRight, "right" } },
26 | getFieldEditorParent(), true));
27 | addField(new BooleanFieldEditor(Preferences.CLOSE_CONFIRMATION, LocalStrings.prefCloseConfirmation,
28 | getFieldEditorParent()));
29 | addField(new BooleanFieldEditor(Preferences.PASTE_CLIPBOARD_IN_NEW_NOTES, LocalStrings.prefPasteClipboardInNewNotes,
30 | getFieldEditorParent()));
31 | addField(new BooleanFieldEditor(Preferences.WRAP, LocalStrings.prefWrap, getFieldEditorParent()));
32 | addField(new BooleanFieldEditor(Preferences.JUSTIFY, LocalStrings.prefJustify, getFieldEditorParent()));
33 | addField(new IntegerFieldEditor(Preferences.LINE_SPACING, LocalStrings.prefLineSpacing, getFieldEditorParent()));
34 | addField(new IntegerFieldEditor(Preferences.BULLET_SPACING, LocalStrings.prefBulletSpacing, getFieldEditorParent()));
35 | addField(new StringFieldEditor(Preferences.NAME_PREFIX, LocalStrings.prefNamePrefix, getFieldEditorParent()));
36 | addField(new ColorFieldEditor(Preferences.FONT_COLOR, LocalStrings.prefFontColor, getFieldEditorParent()));
37 | addField(new ColorFieldEditor(Preferences.BACKGROUND_COLOR, LocalStrings.prefBackgroundColor,
38 | getFieldEditorParent()));
39 | addField(new FontFieldEditor(Preferences.FONT, LocalStrings.prefFont, getFieldEditorParent()));
40 | addField(new IntegerFieldEditor(Preferences.SAVE_INTERVAL, LocalStrings.prefSaveInterval, getFieldEditorParent()));
41 | addField(new DirectoryFieldEditor(Preferences.SAVE_LOCATION, LocalStrings.prefSaveLocation, getFieldEditorParent()));
42 | }
43 |
44 | /**
45 | * Initialises the preference page.
46 | */
47 | @Override
48 | public void init(IWorkbench workbench) {
49 | setPreferenceStore(Notepad4e.getDefault().getPreferenceStore());
50 | setDescription(LocalStrings.prefDesc);
51 | }
52 | }
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/preferences/Preferences.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.preferences;
2 |
3 | /**
4 | * Class used to list the names of the preferences used by the plugin as well as their default values.
5 | *
6 | * @author Pyves
7 | *
8 | */
9 | public class Preferences {
10 |
11 | // Names of preferences.
12 | public static final String WRAP = "Wrap";
13 | public static final String JUSTIFY = "Justify";
14 | public static final String ALIGNMENT = "Alignment";
15 | public static final String FONT = "Font";
16 | public static final String FONT_COLOR = "FontColor";
17 | public static final String BACKGROUND_COLOR = "BackgroundColor";
18 | public static final String LINE_SPACING = "LineSpacing";
19 | public static final String NAME_PREFIX = "NamePrefix";
20 | public static final String CLOSE_CONFIRMATION = "CloseConfirmation";
21 | public static final String PASTE_CLIPBOARD_IN_NEW_NOTES = "PasteClipboardInNewNotes";
22 | public static final String BULLET_SPACING = "BulletSpacing";
23 | public static final String SAVE_INTERVAL = "SaveInterval";
24 | public static final String SAVE_LOCATION = "SaveLocation";
25 |
26 | // Default values of preferences.
27 | public static final boolean WRAP_DEFAULT = true;
28 | public static final boolean JUSTIFY_DEFAULT = false;
29 | public static final String ALIGNMENT_DEFAULT = "left";
30 | public static final String FONT_DEFAULT = "";
31 | public static final String FONT_COLOR_DEFAULT = "0,0,0";
32 | public static final String BACKGROUND_COLOR_DEFAULT = "255,255,255";
33 | public static final int LINE_SPACING_DEFAULT = 0;
34 | public static final String NAME_PREFIX_DEFAULT = "Note";
35 | public static final boolean CLOSE_CONFIRMATION_DEFAULT = true;
36 | public static final boolean PASTE_CLIPBOARD_IN_NEW_NOTES_DEFAULT = false;
37 | public static final int BULLET_SPACING_DEFAULT = 15;
38 | public static final int SAVE_INTERVAL_DEFAULT = 120;
39 | public static final String SAVE_LOCATION_DEFAULT = "";
40 |
41 | private Preferences() {
42 | // Not called.
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/strings/LocalStrings.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.strings;
2 |
3 | import org.eclipse.osgi.util.NLS;
4 |
5 | /**
6 | * Class used to translate the plugin's messages into different languages. Each translation must be contained in a
7 | * localstring_XX.properties file, where XX represents the locale.
8 | *
9 | * @author Pyves
10 | *
11 | */
12 | public class LocalStrings extends NLS {
13 |
14 | private static final String BUNDLE_NAME = "io.github.pyvesb.notepad4e.strings.localstrings";
15 |
16 | public static String dialogCloseLockedMsg;
17 | public static String dialogCloseLockedTitle;
18 | public static String dialogCloseMsg;
19 | public static String dialogCloseTitle;
20 | public static String dialogErrorMsg;
21 | public static String dialogErrorTitle;
22 | public static String dialogExportTitle;
23 | public static String dialogExportedMsg;
24 | public static String dialogExportedTitle;
25 | public static String dialogOverwriteMsg;
26 | public static String dialogOverwriteTitle;
27 | public static String dialogRenameMsg;
28 | public static String dialogRenameTitle;
29 | public static String iconBold;
30 | public static String iconBulletList;
31 | public static String iconChangelog;
32 | public static String iconClearNote;
33 | public static String iconClearStyle;
34 | public static String iconExport;
35 | public static String iconItalic;
36 | public static String iconLock;
37 | public static String iconNewNote;
38 | public static String iconPreferences;
39 | public static String iconStrikeout;
40 | public static String iconUnderline;
41 | public static String iconWebpage;
42 | public static String menuCopy;
43 | public static String menuCut;
44 | public static String menuPaste;
45 | public static String menuRedo;
46 | public static String menuSelectAll;
47 | public static String menuUndo;
48 | public static String prefAlignment;
49 | public static String prefBackgroundColor;
50 | public static String prefBulletSpacing;
51 | public static String prefCloseConfirmation;
52 | public static String prefDesc;
53 | public static String prefFont;
54 | public static String prefFontColor;
55 | public static String prefJustify;
56 | public static String prefLeft;
57 | public static String prefLineSpacing;
58 | public static String prefNamePrefix;
59 | public static String prefPasteClipboardInNewNotes;
60 | public static String prefRight;
61 | public static String prefWrap;
62 | public static String prefSaveInterval;
63 | public static String prefSaveLocation;
64 | public static String getDialogSettingsErrorMsg;
65 |
66 | static {
67 | // Initialise resource bundle.
68 | NLS.initializeMessages(BUNDLE_NAME, LocalStrings.class);
69 | }
70 |
71 | private LocalStrings() {
72 | // Not called.
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/strings/localstrings.properties:
--------------------------------------------------------------------------------
1 | dialogCloseLockedMsg=This note is locked. Are you really sure you want to close it?
2 | dialogCloseLockedTitle=Close Locked Note
3 | dialogCloseMsg=Are you sure you want to close this note?
4 | dialogCloseTitle=Close Note
5 | dialogErrorMsg=Error while attempting to save the file.
6 | dialogErrorTitle=Error
7 | dialogExportTitle=Export to File
8 | dialogExportedMsg=The note has been succesfully exported.
9 | dialogExportedTitle=Note Exported
10 | dialogOverwriteMsg=Do you want to overwrite?
11 | dialogOverwriteTitle=File Already Exists
12 | dialogRenameMsg=Please select the new name of the note:
13 | dialogRenameTitle=Rename Note
14 | iconBold=Bold
15 | iconBulletList=Bullet List
16 | iconChangelog=Changelog
17 | iconClearNote=Clear Note
18 | iconClearStyle=Clear Style
19 | iconExport=Export Note
20 | iconItalic=Italic
21 | iconLock=Lock/Unlock Note
22 | iconNewNote=New Note
23 | iconPreferences=Preferences
24 | iconStrikeout=Strikeout
25 | iconUnderline=Underline
26 | iconWebpage=Project Webpage
27 | menuCopy=Copy
28 | menuCut=Cut
29 | menuPaste=Paste
30 | menuRedo=Redo
31 | menuSelectAll=Select All
32 | menuUndo=Undo
33 | prefAlignment=Text alignment:
34 | prefBackgroundColor=Background color:
35 | prefBulletSpacing=Bullet list indentation:
36 | prefCloseConfirmation=Confirmation when closing a note (overriden if note locked)
37 | prefDesc=Modify the appearance and several editor properties of Notepad4e.\nEdit shortcuts in the General -> Keys section of Eclipse preferences.
38 | prefFont=Text font:
39 | prefFontColor=Font color:
40 | prefJustify=Justify lines
41 | prefLeft=Left
42 | prefLineSpacing=Line spacing:
43 | prefNamePrefix=Name prefix of new notes:
44 | prefPasteClipboardInNewNotes=Paste current contents of clipboard when creating a new note
45 | prefRight=Right
46 | prefWrap=Wrap lines in notes (automatically enforced if right alignment)
47 | prefSaveInterval=Autosave interval (seconds):
48 | prefSaveLocation=Override state save location:
49 | getDialogSettingsErrorMsg=Error whilst loading DialogSettings. Unable to restore the plugin's state.
50 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/strings/localstrings_fr.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyvesB/Notepad4e/0b19e1207091c7ecf15c7de516d1d3388a2252a5/src/io/github/pyvesb/notepad4e/strings/localstrings_fr.properties
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/utils/AbstractSelectedNoteAction.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.utils;
2 |
3 | import org.eclipse.jface.action.Action;
4 |
5 | import io.github.pyvesb.notepad4e.views.Note;
6 | import io.github.pyvesb.notepad4e.views.NotepadView;
7 |
8 | /**
9 | * Class used to run an action on the currently selected note.
10 | *
11 | * @author Pyves
12 | *
13 | */
14 | public abstract class AbstractSelectedNoteAction extends Action {
15 |
16 | private final NotepadView notepadView;
17 |
18 | /**
19 | * Constructor. Sets a reference to the main plugin's view.
20 | *
21 | * @param notepadView
22 | */
23 | public AbstractSelectedNoteAction(NotepadView notepadView) {
24 | this.notepadView = notepadView;
25 | }
26 |
27 | @Override
28 | public void run() {
29 | Note selectedNote = notepadView.getSelectedNote();
30 | if (selectedNote != null) {
31 | runSelectedNoteAction(selectedNote);
32 | }
33 | }
34 |
35 | /**
36 | * Runs an action on the currently selected note.
37 | *
38 | * @param selectedNote
39 | */
40 | protected abstract void runSelectedNoteAction(Note selectedNote);
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/utils/NotepadAction.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.utils;
2 |
3 | import io.github.pyvesb.notepad4e.strings.LocalStrings;
4 |
5 | /**
6 | * Enum used to represent all the possible user actions in the plugin. These are either handled by shortcuts or by
7 | * NotepadView buttons or by both.
8 | *
9 | * @author Pyves
10 | *
11 | */
12 | public enum NotepadAction {
13 |
14 | NEW_NOTE(LocalStrings.iconNewNote, "notepad4e.command.note.new", "/icons/file-new-16x16.png"),
15 | BOLD_TEXT(LocalStrings.iconBold, "notepad4e.command.text.bold", "/icons/style_bold.gif"),
16 | ITALIC_TEXT(LocalStrings.iconItalic, "notepad4e.command.text.italic", "/icons/style_italic.gif"),
17 | UNDERLINE_TEXT(LocalStrings.iconUnderline, "notepad4e.command.text.underline", "/icons/style_underline.gif"),
18 | STRIKEOUT_TEXT(LocalStrings.iconStrikeout, "notepad4e.command.text.strikeout", "/icons/style_strikeout.png"),
19 | CLEAR_STYLE_TEXT(LocalStrings.iconClearStyle, "notepad4e.command.text.clear", "/icons/clear_co.png"),
20 | BULLET_LIST(LocalStrings.iconBulletList, "notepad4e.command.bullet.list", "/icons/bullets.png"),
21 | CLEAR_NOTE(LocalStrings.iconClearNote, "notepad4e.command.note.clear", "/icons/clear.png"),
22 | UNDO_TEXT(null, "notepad4e.command.text.undo", null),
23 | REDO_TEXT(null, "notepad4e.command.text.redo", null),
24 | CLOSE_NOTE(null, "notepad4e.command.note.close", null),
25 | TOGGLE_EDITABLE_NOTE(LocalStrings.iconLock, null, "/icons/deadlock_view.png"),
26 | EXPORT_NOTE(LocalStrings.iconExport, null, "/icons/save_edit.png"),
27 | PREFERENCES(LocalStrings.iconPreferences, null, "/icons/settings_obj.png"),
28 | WEBSITE(LocalStrings.iconWebpage, null, "/icons/web.png"),
29 | CHANGELOG(LocalStrings.iconChangelog, null, "/icons/change.gif");
30 |
31 | private final String text;
32 | private final String commandID;
33 | private final String imagePath;
34 |
35 | private NotepadAction(String text, String commandID, String image) {
36 | this.text = text;
37 | this.commandID = commandID;
38 | this.imagePath = image;
39 | }
40 |
41 | public static NotepadAction of(String commandID) {
42 | for (NotepadAction notepadAction : values()) {
43 | if (commandID.equals(notepadAction.commandID)) {
44 | return notepadAction;
45 | }
46 | }
47 | return null;
48 | }
49 |
50 | public final String getText() {
51 | return text;
52 | }
53 |
54 | public final String getCommandID() {
55 | return commandID;
56 | }
57 |
58 | public final String getImagePath() {
59 | return imagePath;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/utils/ShortcutHandler.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.utils;
2 |
3 | import org.eclipse.core.commands.AbstractHandler;
4 | import org.eclipse.core.commands.ExecutionEvent;
5 | import org.eclipse.core.commands.ExecutionException;
6 |
7 | import io.github.pyvesb.notepad4e.views.Note;
8 | import io.github.pyvesb.notepad4e.views.NotepadView;
9 |
10 | /**
11 | * Class used to listen to keyboard events and launch actions accordingly.
12 | *
13 | * @author Pyves
14 | *
15 | */
16 | public class ShortcutHandler extends AbstractHandler {
17 |
18 | private final NotepadView notepadView;
19 |
20 | /**
21 | * Constructor. Sets a reference to the main plugin's view.
22 | *
23 | * @param notepadView
24 | */
25 | public ShortcutHandler(NotepadView notepadView) {
26 | this.notepadView = notepadView;
27 | }
28 |
29 | /**
30 | * Deals with keyboard events, in other words Notepad4e shortcuts. This handler listens to all events, filtering
31 | * must therefore be done accordingly.
32 | *
33 | * @param event
34 | */
35 | @Override
36 | public Object execute(ExecutionEvent event) throws ExecutionException {
37 | if (event.getCommand() == null) {
38 | return null;
39 | }
40 |
41 | NotepadAction action = NotepadAction.of(event.getCommand().getId());
42 | if (action == NotepadAction.NEW_NOTE) {
43 | notepadView.addNewNote();
44 | } else if (action == NotepadAction.CLOSE_NOTE) {
45 | notepadView.closeCurrentSelection();
46 | } else {
47 | Note selectedNote = notepadView.getSelectedNote();
48 | if (selectedNote != null) {
49 | executeNoteAction(action, selectedNote);
50 | }
51 | }
52 | return null;
53 | }
54 |
55 | /**
56 | * Executes an action on the selected note.
57 | *
58 | * @param action
59 | * @param selectedNote
60 | */
61 | private void executeNoteAction(NotepadAction action, Note selectedNote) {
62 | switch (action) {
63 | case BOLD_TEXT:
64 | selectedNote.boldSelection();
65 | break;
66 | case ITALIC_TEXT:
67 | selectedNote.italicSelection();
68 | break;
69 | case UNDERLINE_TEXT:
70 | selectedNote.underlineSelection();
71 | break;
72 | case STRIKEOUT_TEXT:
73 | selectedNote.strikeoutSelection();
74 | break;
75 | case BULLET_LIST:
76 | selectedNote.bulletListSelection();
77 | break;
78 | case CLEAR_STYLE_TEXT:
79 | selectedNote.clearSelectionStyles();
80 | break;
81 | case CLEAR_NOTE:
82 | selectedNote.clearText();
83 | break;
84 | case UNDO_TEXT:
85 | selectedNote.undo();
86 | break;
87 | case REDO_TEXT:
88 | selectedNote.redo();
89 | break;
90 | default:
91 | break;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/utils/UndoRedoManager.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.utils;
2 |
3 | import java.util.ArrayDeque;
4 | import java.util.Deque;
5 |
6 | import org.eclipse.swt.custom.StyleRange;
7 |
8 | import io.github.pyvesb.notepad4e.views.Note;
9 |
10 | /**
11 | * Class in charge of handling the undo and redo actions of a note.
12 | *
13 | * @author Pyves
14 | *
15 | */
16 | public class UndoRedoManager {
17 |
18 | // Used to prevent the size of undo and deques queues from growing indefinitely.
19 | private static final int MAX_DEQUE_SIZES = 200;
20 |
21 | // Reference to the note this manager is handling.
22 | private final Note note;
23 | // Deques used to store note states.
24 | private final Deque undoDeque = new ArrayDeque<>(MAX_DEQUE_SIZES);
25 | private final Deque redoDeque = new ArrayDeque<>(MAX_DEQUE_SIZES);
26 |
27 | /**
28 | * Constructor, connects the note and the new manager instance.
29 | *
30 | * @param note
31 | */
32 | public UndoRedoManager(Note note) {
33 | this.note = note;
34 | }
35 |
36 | /**
37 | * Saves a note's state to allow undo and redo operations to be performed.
38 | */
39 | public void saveNoteState() {
40 | // Empty redo deque.
41 | redoDeque.clear();
42 |
43 | undoDeque.push(new NoteState(note.getText(), note.getCaretOffset(), note.getStyleRanges(), getBulletLineMapping()));
44 |
45 | // Limit maximum size of deque by clearing oldest states.
46 | if (undoDeque.size() > MAX_DEQUE_SIZES) {
47 | undoDeque.pollLast();
48 | }
49 | }
50 |
51 | /**
52 | * Indicates whether the manager has no currently saved state.
53 | *
54 | * @return true if undoDeque is empty, false otherwise
55 | */
56 | public boolean isNoteStateEmpty() {
57 | return undoDeque.isEmpty();
58 | }
59 |
60 | /**
61 | * Performs an undo action.
62 | */
63 | public void undo() {
64 | NoteState noteState = undoDeque.pollFirst();
65 | if (noteState != null) { // Something to undo.
66 | if (redoDeque.isEmpty()) {
67 | // Pushes the current state of the note before a sequence of undo operations is performed.
68 | redoDeque.push(new NoteState(note.getText(), note.getCaretOffset(), note.getStyleRanges(),
69 | getBulletLineMapping()));
70 | }
71 | restoreState(noteState);
72 | redoDeque.push(noteState);
73 | }
74 | }
75 |
76 | /**
77 | * Performs a redo action.
78 | */
79 | public void redo() {
80 | if (!redoDeque.isEmpty()) { // Something to redo.
81 | undoDeque.push(redoDeque.pollFirst());
82 | if (redoDeque.size() == 1) {
83 | // Last possible redo operation, clear deque.
84 | restoreState(redoDeque.pollFirst());
85 | } else {
86 | restoreState(redoDeque.peekFirst());
87 | }
88 | }
89 | }
90 |
91 | /**
92 | * Restores the note to a previous state.
93 | *
94 | * @param noteState
95 | */
96 | private void restoreState(NoteState noteState) {
97 | // Set the text via the content to avoid firing events which would be picked up by the manager whilst
98 | // performing undo redo operations.
99 | note.getContent().setText(noteState.getText());
100 |
101 | note.setCaretOffset(noteState.getCaretOffset());
102 |
103 | note.setStyleRanges(noteState.getStyles());
104 |
105 | setBulletLineMapping(noteState.getBulletLineMapping());
106 | }
107 |
108 | /**
109 | * Constructs an array containing the bullets for the note.
110 | *
111 | * @return array of bullets indexed by line number; null value if no bullet on the line
112 | */
113 | private boolean[] getBulletLineMapping() {
114 | boolean[] bullets = new boolean[note.getLineCount()];
115 | for (int line = 0; line < bullets.length; ++line) {
116 | bullets[line] = (note.getLineBullet(line) != null);
117 | }
118 | return bullets;
119 | }
120 |
121 | /**
122 | * Sets bullets in the note given a bullet line mapping array.
123 | *
124 | * @param bulletLineMapping
125 | */
126 | private void setBulletLineMapping(boolean[] bulletLineMapping) {
127 | if (bulletLineMapping.length > 0) {
128 | // It's more efficient to set several bullets at the same time, we therefore look for the longest sequence
129 | // where all the bullets have the same state (i.e. they exist or don't).
130 | boolean currentSequenceState = bulletLineMapping[0];
131 | int sequenceLineStart = 0;
132 | for (int line = 1; line < bulletLineMapping.length; ++line) {
133 | if (currentSequenceState != bulletLineMapping[line]) {
134 | note.setLineBullet(sequenceLineStart, line - sequenceLineStart, currentSequenceState);
135 | sequenceLineStart = line;
136 | currentSequenceState = !currentSequenceState;
137 | }
138 | }
139 | note.setLineBullet(sequenceLineStart, bulletLineMapping.length - sequenceLineStart, currentSequenceState);
140 | }
141 | }
142 |
143 | /**
144 | * Class used to keep track of the state of the note in order to perform undo and redo actions.
145 | *
146 | * @author Pyves
147 | *
148 | */
149 | private static final class NoteState {
150 |
151 | // Text content of the note.
152 | final String text;
153 | // Offset of the caret relative to the start of the text.
154 | final int caretOffset;
155 | // Styles of the text, e.g. bold, italic, etc.
156 | final StyleRange[] styles;
157 | // Indicates whether a bullet is present at the beginning of each text line.
158 | final boolean[] bulletLineMapping;
159 |
160 | NoteState(String text, int caretOffset, StyleRange[] styles, boolean[] bulletLineMapping) {
161 | this.text = text;
162 | this.caretOffset = caretOffset;
163 | this.styles = styles;
164 | this.bulletLineMapping = bulletLineMapping;
165 | }
166 |
167 | String getText() {
168 | return text;
169 | }
170 |
171 | int getCaretOffset() {
172 | return caretOffset;
173 | }
174 |
175 | StyleRange[] getStyles() {
176 | return styles;
177 | }
178 |
179 | boolean[] getBulletLineMapping() {
180 | return bulletLineMapping;
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/views/Note.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.views;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.PrintWriter;
7 |
8 | import org.eclipse.core.runtime.IStatus;
9 | import org.eclipse.core.runtime.Status;
10 | import org.eclipse.core.runtime.preferences.IEclipsePreferences;
11 | import org.eclipse.core.runtime.preferences.InstanceScope;
12 | import org.eclipse.jface.dialogs.MessageDialog;
13 | import org.eclipse.swt.SWT;
14 | import org.eclipse.swt.custom.Bullet;
15 | import org.eclipse.swt.custom.ST;
16 | import org.eclipse.swt.custom.StyleRange;
17 | import org.eclipse.swt.custom.StyledText;
18 | import org.eclipse.swt.events.SelectionAdapter;
19 | import org.eclipse.swt.events.SelectionEvent;
20 | import org.eclipse.swt.graphics.Color;
21 | import org.eclipse.swt.graphics.Font;
22 | import org.eclipse.swt.graphics.FontData;
23 | import org.eclipse.swt.graphics.GlyphMetrics;
24 | import org.eclipse.swt.graphics.Point;
25 | import org.eclipse.swt.widgets.Composite;
26 | import org.eclipse.swt.widgets.Display;
27 | import org.eclipse.swt.widgets.FileDialog;
28 | import org.eclipse.swt.widgets.Menu;
29 | import org.eclipse.swt.widgets.MenuItem;
30 | import org.eclipse.ui.IWorkbenchPartSite;
31 |
32 | import io.github.pyvesb.notepad4e.Notepad4e;
33 | import io.github.pyvesb.notepad4e.preferences.Preferences;
34 | import io.github.pyvesb.notepad4e.strings.LocalStrings;
35 | import io.github.pyvesb.notepad4e.utils.UndoRedoManager;
36 |
37 | /**
38 | * Class representing an individual note in the plugin's view.
39 | *
40 | * @author Pyves
41 | *
42 | */
43 | public class Note extends StyledText {
44 |
45 | // Used to parse strings.
46 | private static final String SERIALISATION_DELIMITER = ",";
47 |
48 | // Used to enable undo and redo actions.
49 | private final UndoRedoManager undoRedoManager;
50 | // User defined preferences.
51 | private final IEclipsePreferences preferences;
52 | // Used at the beginning of each line in lists.
53 | private final Bullet bullet;
54 |
55 | // Appearance parameters of the note.
56 | private Color fontColor;
57 | private Color backgroundColor;
58 | private Font font;
59 | // Menu items (mouse right-click).
60 | private MenuItem menuItemUndo;
61 | private MenuItem menuItemRedo;
62 | private MenuItem menuItemCut;
63 | private MenuItem menuItemCopy;
64 | private MenuItem menuItemPaste;
65 | private MenuItem menuItemSelectAll;
66 | private MenuItem menuItemSeparator1;
67 | private MenuItem menuItemSeparator2;
68 |
69 | private enum TextStyle {
70 | BOLD, ITALIC, UNDERLINE, STRIKEOUT
71 | }
72 |
73 | /**
74 | * Constructor. Sets properties of the editor window.
75 | *
76 | * @param parent
77 | * @param text
78 | * @param bullets
79 | * @param style
80 | * @param editable
81 | */
82 | public Note(Composite parent, String text, String style, String bullets, boolean editable) {
83 | // Enable multiple lines and scroll bars.
84 | super(parent, SWT.V_SCROLL | SWT.H_SCROLL);
85 |
86 | preferences = InstanceScope.INSTANCE.getNode(Notepad4e.PLUGIN_ID);
87 |
88 | StyleRange bulletStyle = new StyleRange();
89 | bulletStyle.metrics = new GlyphMetrics(0, 0, 0);
90 | bullet = new Bullet(ST.BULLET_DOT, bulletStyle);
91 |
92 | // Scroll bars only appear when the text extends beyond the note window.
93 | setAlwaysShowScrollBars(false);
94 | setParametersFromPreferences();
95 | setText(text);
96 | deserialiseStyle(style);
97 | deserialiseBullets(bullets);
98 | initialiseMenu();
99 |
100 | undoRedoManager = new UndoRedoManager(this);
101 | // Listen to text modifications.
102 | addVerifyListener(event -> {
103 | // Save state if new word OR no previously state OR overwriting existing text OR pasting several chars.
104 | if (" ".equals(event.text) || undoRedoManager.isNoteStateEmpty() || event.end - event.start > 0
105 | || event.text.length() > 1) {
106 | undoRedoManager.saveNoteState();
107 | }
108 | });
109 |
110 | if (!editable) {
111 | toggleEditable();
112 | }
113 | }
114 |
115 | /**
116 | * Disposes the resources owned by the note.
117 | */
118 | @Override
119 | public void dispose() {
120 | fontColor.dispose();
121 | backgroundColor.dispose();
122 | if (font != null) {
123 | font.dispose();
124 | }
125 | menuItemUndo.dispose();
126 | menuItemRedo.dispose();
127 | menuItemCut.dispose();
128 | menuItemCopy.dispose();
129 | menuItemPaste.dispose();
130 | menuItemSelectAll.dispose();
131 | menuItemSeparator1.dispose();
132 | menuItemSeparator2.dispose();
133 | super.dispose();
134 | }
135 |
136 | /**
137 | * Sets properties that can be defined by the user in the plugin's preferences page.
138 | */
139 | public void setParametersFromPreferences() {
140 | // Line spacing parameter.
141 | setLineSpacing(preferences.getInt(Preferences.LINE_SPACING, Preferences.LINE_SPACING_DEFAULT));
142 |
143 | // Set bullet indentation spacing (width of GlyphMetrics) parameter.
144 | bullet.style.metrics.width = preferences.getInt(Preferences.BULLET_SPACING, Preferences.BULLET_SPACING_DEFAULT);
145 |
146 | // Line wrap parameter.
147 | setWordWrap(preferences.getBoolean(Preferences.WRAP, Preferences.WRAP_DEFAULT));
148 |
149 | // Text justify parameter.
150 | setJustify(preferences.getBoolean(Preferences.JUSTIFY, Preferences.JUSTIFY_DEFAULT));
151 |
152 | // Alignment parameter (left or right).
153 | if ("right".equals(preferences.get(Preferences.ALIGNMENT, Preferences.ALIGNMENT_DEFAULT))) {
154 | setAlignment(SWT.RIGHT);
155 | // Word wrapping must be enabled for right alignment to be effective.
156 | setWordWrap(true);
157 | } else {
158 | setAlignment(SWT.LEFT);
159 | }
160 |
161 | // Font color parameter.
162 | String[] fontColorRGBStrings = preferences
163 | .get(Preferences.FONT_COLOR, Preferences.FONT_COLOR_DEFAULT)
164 | .split(SERIALISATION_DELIMITER);
165 | // The strings in the above array correspond to the red, green and blue colors.
166 | fontColor = new Color(Display.getCurrent(), Integer.parseInt(fontColorRGBStrings[0]),
167 | Integer.parseInt(fontColorRGBStrings[1]), Integer.parseInt(fontColorRGBStrings[2]));
168 | setForeground(fontColor);
169 |
170 | // Background color parameter.
171 | String[] backgroundColorRGBStrings = preferences
172 | .get(Preferences.BACKGROUND_COLOR, Preferences.BACKGROUND_COLOR_DEFAULT)
173 | .split(SERIALISATION_DELIMITER);
174 | // The strings in the above array correspond to the red, green and blue colors.
175 | backgroundColor = new Color(Display.getCurrent(), Integer.parseInt(backgroundColorRGBStrings[0]),
176 | Integer.parseInt(backgroundColorRGBStrings[1]), Integer.parseInt(backgroundColorRGBStrings[2]));
177 | setBackground(backgroundColor);
178 |
179 | // Font parameter; a semicolon is appended by the Eclipse API when retrieving it from the plugin's preference
180 | // page, it must be deleted.
181 | String fontString = preferences.get(Preferences.FONT, Preferences.FONT_DEFAULT).replace(";", "");
182 | // An empty string is returned when the user has not set the font in the preferences; do not set the font so the
183 | // plugin will display the default font of the StyledText component instead.
184 | if (!fontString.isEmpty()) {
185 | font = new Font(Display.getCurrent(), new FontData(fontString));
186 | setFont(font);
187 | }
188 | }
189 |
190 | /**
191 | * Undos latest Note modification.
192 | */
193 | public void undo() {
194 | if (getEditable()) {
195 | undoRedoManager.undo();
196 | }
197 | }
198 |
199 | /**
200 | * Redos latest Note modification.
201 | */
202 | public void redo() {
203 | if (getEditable()) {
204 | undoRedoManager.redo();
205 | }
206 | }
207 |
208 | /**
209 | * Removes all the text from the note.
210 | */
211 | public void clearText() {
212 | if (getEditable()) {
213 | setText("");
214 | }
215 | }
216 |
217 | /**
218 | * Applies a bold style to the currently selected text.
219 | */
220 | public void boldSelection() {
221 | addStyleToSelection(TextStyle.BOLD);
222 | }
223 |
224 | /**
225 | * Applies an italic style to the currently selected text.
226 | */
227 | public void italicSelection() {
228 | addStyleToSelection(TextStyle.ITALIC);
229 | }
230 |
231 | /**
232 | * Applies an underlined style to the currently selected text.
233 | */
234 | public void underlineSelection() {
235 | addStyleToSelection(TextStyle.UNDERLINE);
236 | }
237 |
238 | /**
239 | * Applies a strikeout style to the currently selected text.
240 | */
241 | public void strikeoutSelection() {
242 | addStyleToSelection(TextStyle.STRIKEOUT);
243 | }
244 |
245 | /**
246 | * Applies a bullet list style to the currently selected lines.
247 | */
248 | public void bulletListSelection() {
249 | if (getEditable()) {
250 | // Save bullet state prior to modification for undo actions.
251 | undoRedoManager.saveNoteState();
252 |
253 | Point selection = getSelectionRange();
254 | int selectionStartLine = getLineAtOffset(selection.x);
255 | int selectionEndLine = getLineAtOffset(selection.x + selection.y);
256 | int bulletsInSelection = 0;
257 | // Count number of lines that currently have a bullet.
258 | for (int line = selectionStartLine; line <= selectionEndLine; ++line) {
259 | if (getLineBullet(line) != null) {
260 | ++bulletsInSelection;
261 | }
262 | }
263 |
264 | int selectedLines = selectionEndLine - selectionStartLine + 1;
265 | // If all lines already have bullets, remove them all, otherwise add them.
266 | setLineBullet(selectionStartLine, selectedLines, bulletsInSelection != selectedLines);
267 | }
268 | }
269 |
270 | /**
271 | * Removes all styles from the current selection.
272 | */
273 | public void clearSelectionStyles() {
274 | Point selectionRange = getSelectionRange();
275 | if (getEditable() && selectionRange.y != 0) {
276 | // Save style state prior to modification for undo actions.
277 | undoRedoManager.saveNoteState();
278 |
279 | // No colors are specified as they are defined by the plugin's preferences.
280 | StyleRange styleRange = new StyleRange(selectionRange.x, selectionRange.y, null, null, SWT.NORMAL);
281 | setStyleRange(styleRange);
282 | }
283 | }
284 |
285 | /**
286 | * Makes the note read-only or editable again.
287 | */
288 | public void toggleEditable() {
289 | boolean newState = !getEditable();
290 | setEditable(newState);
291 | menuItemUndo.setEnabled(newState);
292 | menuItemRedo.setEnabled(newState);
293 | menuItemCut.setEnabled(newState);
294 | menuItemPaste.setEnabled(newState);
295 | }
296 |
297 | /**
298 | * Creates a string giving a description of the styles in the current note.
299 | *
300 | * @return CSV string containing a serialised representation of the styles
301 | */
302 | public String serialiseStyle() {
303 | StringBuilder styleSerialisation = new StringBuilder();
304 | StyleRange[] currentStyles = getStyleRanges();
305 | // Append integers corresponding to various information of each style range object, separated by
306 | // STRING_SEPARATOR.
307 | for (StyleRange currentStyle : currentStyles) {
308 | styleSerialisation.append(currentStyle.start).append(SERIALISATION_DELIMITER);
309 | styleSerialisation.append(currentStyle.length).append(SERIALISATION_DELIMITER);
310 | styleSerialisation.append(currentStyle.fontStyle).append(SERIALISATION_DELIMITER);
311 | // If underlined, 1, else 0.
312 | styleSerialisation.append(currentStyle.underline ? 1 : 0).append(SERIALISATION_DELIMITER);
313 | // If strikeout, 1, else 0.
314 | styleSerialisation.append(currentStyle.strikeout ? 1 : 0).append(SERIALISATION_DELIMITER);
315 | }
316 | return styleSerialisation.toString();
317 | }
318 |
319 | /**
320 | * Creates a string giving a description of the bullets in the current note.
321 | *
322 | * @return CSV string containing a serialised representation of the bullets
323 | */
324 | public String serialiseBullets() {
325 | StringBuilder bulletLines = new StringBuilder();
326 | for (int line = 0; line < getLineCount(); ++line) {
327 | if (getLineBullet(line) != null) {
328 | // Bullet found: add line number with separator.
329 | bulletLines.append(line).append(SERIALISATION_DELIMITER);
330 | }
331 | }
332 | // Remove trailing separator.
333 | return bulletLines.length() > 1 ? bulletLines.substring(0, bulletLines.length() - 1) : "";
334 | }
335 |
336 | /**
337 | * Exports the brute text in the current note as a text file.
338 | *
339 | * @param iWorkbenchPartSite
340 | */
341 | public void exportToFile(IWorkbenchPartSite iWorkbenchPartSite) {
342 | // Retrieve the file to save to with an explorer window.
343 | FileDialog fileDialog = new FileDialog(iWorkbenchPartSite.getShell(), SWT.SAVE);
344 | fileDialog.setText(LocalStrings.dialogExportTitle);
345 | String fileName = fileDialog.open();
346 | // Invalid name specified.
347 | if (fileName == null || fileName.isEmpty()) {
348 | return;
349 | }
350 |
351 | File file = new File(fileName);
352 | if (!file.exists() || MessageDialog.openQuestion(iWorkbenchPartSite.getShell(),
353 | LocalStrings.dialogOverwriteTitle, LocalStrings.dialogOverwriteMsg)) {
354 | // Write the current note's text to the file, with handling of IO exceptions.
355 | try (FileOutputStream outStream = new FileOutputStream(file);
356 | PrintWriter printStream = new PrintWriter(outStream)) {
357 | printStream.print(getText());
358 | printStream.flush();
359 | MessageDialog.openInformation(iWorkbenchPartSite.getShell(), LocalStrings.dialogExportedTitle,
360 | LocalStrings.dialogExportedMsg);
361 | } catch (IOException e) {
362 | MessageDialog.openInformation(iWorkbenchPartSite.getShell(), LocalStrings.dialogErrorTitle,
363 | LocalStrings.dialogErrorMsg);
364 | Notepad4e.getDefault().getLog()
365 | .log(new Status(IStatus.ERROR, Notepad4e.PLUGIN_ID, LocalStrings.dialogErrorMsg, e));
366 | }
367 | }
368 | }
369 |
370 | /**
371 | * Adds or removes a bullet at the start of the specified line.
372 | *
373 | * @param line
374 | * @param count
375 | * @param isPresent
376 | */
377 | public void setLineBullet(int line, int count, boolean isPresent) {
378 | setLineBullet(line, count, isPresent ? bullet : null);
379 | }
380 |
381 | /**
382 | * Applies styles to the current note based on a styles' serialisation string.
383 | *
384 | * @param serialisation
385 | */
386 | private void deserialiseStyle(String serialisation) {
387 | // Style can be null if new note.
388 | if (serialisation != null && !serialisation.isEmpty()) {
389 | String[] integers = serialisation.split(SERIALISATION_DELIMITER);
390 | StyleRange[] styles = new StyleRange[integers.length / 5];
391 | // Do the parsing.
392 | for (int styleIndex = 0; styleIndex < styles.length; ++styleIndex) {
393 | // Each StyleRange object has 5 corresponding integers in the CSV string.
394 | int integerIndex = 5 * styleIndex;
395 | styles[styleIndex] = new StyleRange();
396 | styles[styleIndex].start = Integer.parseInt(integers[integerIndex]);
397 | styles[styleIndex].length = Integer.parseInt(integers[integerIndex + 1]);
398 | styles[styleIndex].fontStyle = Integer.parseInt(integers[integerIndex + 2]);
399 | styles[styleIndex].underline = (Integer.parseInt(integers[integerIndex + 3]) == 1) ? true : false;
400 | styles[styleIndex].strikeout = (Integer.parseInt(integers[integerIndex + 4]) == 1) ? true : false;
401 | }
402 | // Apply the parsed styles.
403 | setStyleRanges(styles);
404 | }
405 | }
406 |
407 | /**
408 | * Adds bullets to the current note based on a bullets' serialisation string (for instance "0,1,4").
409 | *
410 | * @param serialisation
411 | */
412 | private void deserialiseBullets(String serialisation) {
413 | // Bullets can be null if new note or upgrading from old plugin version.
414 | if (serialisation != null && !serialisation.isEmpty()) {
415 | for (String lineNumber : serialisation.split(SERIALISATION_DELIMITER)) {
416 | setLineBullet(Integer.parseInt(lineNumber), 1, bullet);
417 | }
418 | }
419 | }
420 |
421 | /**
422 | * Initialises the menu triggered by a right-click inside the note.
423 | */
424 | private void initialiseMenu() {
425 | Menu menu = new Menu(getShell(), SWT.POP_UP);
426 | menuItemUndo = new MenuItem(menu, SWT.NONE);
427 | menuItemUndo.setText(LocalStrings.menuUndo);
428 | menuItemUndo.addSelectionListener(new SelectionAdapter() {
429 | @Override
430 | public void widgetSelected(SelectionEvent event) {
431 | undo();
432 | }
433 | });
434 | menuItemRedo = new MenuItem(menu, SWT.NONE);
435 | menuItemRedo.setText(LocalStrings.menuRedo);
436 | menuItemRedo.addSelectionListener(new SelectionAdapter() {
437 | @Override
438 | public void widgetSelected(SelectionEvent event) {
439 | redo();
440 | }
441 | });
442 | menuItemSeparator1 = new MenuItem(menu, SWT.SEPARATOR);
443 | menuItemCut = new MenuItem(menu, SWT.NONE);
444 | menuItemCut.setText(LocalStrings.menuCut);
445 | menuItemCut.addSelectionListener(new SelectionAdapter() {
446 | @Override
447 | public void widgetSelected(SelectionEvent event) {
448 | cut();
449 | }
450 | });
451 | menuItemCopy = new MenuItem(menu, SWT.NONE);
452 | menuItemCopy.setText(LocalStrings.menuCopy);
453 | menuItemCopy.addSelectionListener(new SelectionAdapter() {
454 | @Override
455 | public void widgetSelected(SelectionEvent event) {
456 | copy();
457 | }
458 | });
459 | menuItemPaste = new MenuItem(menu, SWT.NONE);
460 | menuItemPaste.setText(LocalStrings.menuPaste);
461 | menuItemPaste.addSelectionListener(new SelectionAdapter() {
462 | @Override
463 | public void widgetSelected(SelectionEvent event) {
464 | paste();
465 | }
466 | });
467 | menuItemSeparator2 = new MenuItem(menu, SWT.SEPARATOR);
468 | menuItemSelectAll = new MenuItem(menu, SWT.NONE);
469 | menuItemSelectAll.setText(LocalStrings.menuSelectAll);
470 | menuItemSelectAll.addSelectionListener(new SelectionAdapter() {
471 | @Override
472 | public void widgetSelected(SelectionEvent event) {
473 | selectAll();
474 | }
475 | });
476 | setMenu(menu);
477 | }
478 |
479 | /**
480 | * Applies a new style to the currently selected text.
481 | *
482 | * @param newStyle
483 | */
484 | private void addStyleToSelection(TextStyle newStyle) {
485 | Point selectionRange = getSelectionRange();
486 | // Only attempt to apply styles if text is selected and note editable.
487 | if (getEditable() && selectionRange.y != 0) {
488 | // Save style state prior to modification for undo actions.
489 | undoRedoManager.saveNoteState();
490 |
491 | // Retrieve the current styles in the selection. If the selection (or parts of it) does not have any style,
492 | // there are no corresponding entries in the following array.
493 | StyleRange[] currentStyles = getStyleRanges(selectionRange.x, selectionRange.y);
494 |
495 | StyleRange selectionStyleRange = new StyleRange(selectionRange.x, selectionRange.y, null, null);
496 | addStyleToStyleRange(newStyle, selectionStyleRange);
497 | // Apply the style to the whole selection range; ranges that previously had no style and that are are not
498 | // accounted for in currentStyles now have the wanted style.
499 | setStyleRange(selectionStyleRange);
500 |
501 | // The above call overwrote the previous styles; the previous styles are re-applied with the additional
502 | // new one.
503 | for (StyleRange currentStyle : currentStyles) {
504 | addStyleToStyleRange(newStyle, currentStyle);
505 | setStyleRange(currentStyle);
506 | }
507 | }
508 | }
509 |
510 | /**
511 | * Adds a new style to the StyleRange object.
512 | *
513 | * @param newStyle
514 | * @param styleRange
515 | */
516 | private void addStyleToStyleRange(TextStyle newStyle, StyleRange styleRange) {
517 | switch (newStyle) {
518 | case BOLD:
519 | styleRange.fontStyle |= SWT.BOLD;
520 | break;
521 | case ITALIC:
522 | styleRange.fontStyle |= SWT.ITALIC;
523 | break;
524 | case UNDERLINE:
525 | styleRange.underline = true;
526 | break;
527 | case STRIKEOUT:
528 | styleRange.strikeout = true;
529 | break;
530 | default:
531 | break;
532 | }
533 | }
534 | }
535 |
--------------------------------------------------------------------------------
/src/io/github/pyvesb/notepad4e/views/NotepadView.java:
--------------------------------------------------------------------------------
1 | package io.github.pyvesb.notepad4e.views;
2 |
3 | import java.net.URL;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | import org.eclipse.core.runtime.FileLocator;
7 | import org.eclipse.core.runtime.IProgressMonitor;
8 | import org.eclipse.core.runtime.IStatus;
9 | import org.eclipse.core.runtime.Path;
10 | import org.eclipse.core.runtime.Platform;
11 | import org.eclipse.core.runtime.Status;
12 | import org.eclipse.core.runtime.jobs.Job;
13 | import org.eclipse.core.runtime.preferences.IEclipsePreferences;
14 | import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
15 | import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
16 | import org.eclipse.core.runtime.preferences.InstanceScope;
17 | import org.eclipse.jface.action.Action;
18 | import org.eclipse.jface.action.IMenuManager;
19 | import org.eclipse.jface.action.IToolBarManager;
20 | import org.eclipse.jface.action.Separator;
21 | import org.eclipse.jface.bindings.Binding;
22 | import org.eclipse.jface.dialogs.IDialogSettings;
23 | import org.eclipse.jface.dialogs.InputDialog;
24 | import org.eclipse.jface.dialogs.MessageDialog;
25 | import org.eclipse.jface.preference.PreferenceDialog;
26 | import org.eclipse.jface.resource.ImageDescriptor;
27 | import org.eclipse.jface.util.Geometry;
28 | import org.eclipse.swt.SWT;
29 | import org.eclipse.swt.custom.CTabFolder;
30 | import org.eclipse.swt.custom.CTabFolder2Adapter;
31 | import org.eclipse.swt.custom.CTabFolderEvent;
32 | import org.eclipse.swt.custom.CTabItem;
33 | import org.eclipse.swt.dnd.Clipboard;
34 | import org.eclipse.swt.dnd.DND;
35 | import org.eclipse.swt.dnd.TextTransfer;
36 | import org.eclipse.swt.events.MouseAdapter;
37 | import org.eclipse.swt.events.MouseEvent;
38 | import org.eclipse.swt.events.SelectionAdapter;
39 | import org.eclipse.swt.events.SelectionEvent;
40 | import org.eclipse.swt.graphics.Point;
41 | import org.eclipse.swt.graphics.Rectangle;
42 | import org.eclipse.swt.program.Program;
43 | import org.eclipse.swt.widgets.Composite;
44 | import org.eclipse.swt.widgets.Display;
45 | import org.eclipse.swt.widgets.Tracker;
46 | import org.eclipse.ui.IActionBars;
47 | import org.eclipse.ui.PlatformUI;
48 | import org.eclipse.ui.contexts.IContextService;
49 | import org.eclipse.ui.dialogs.PreferencesUtil;
50 | import org.eclipse.ui.handlers.IHandlerService;
51 | import org.eclipse.ui.keys.IBindingService;
52 | import org.eclipse.ui.part.ViewPart;
53 |
54 | import io.github.pyvesb.notepad4e.Notepad4e;
55 | import io.github.pyvesb.notepad4e.preferences.Preferences;
56 | import io.github.pyvesb.notepad4e.strings.LocalStrings;
57 | import io.github.pyvesb.notepad4e.utils.AbstractSelectedNoteAction;
58 | import io.github.pyvesb.notepad4e.utils.NotepadAction;
59 | import io.github.pyvesb.notepad4e.utils.ShortcutHandler;
60 |
61 | /**
62 | * Class handling the plugin's view with the different note tabs.
63 | *
64 | * @author Pyves
65 | *
66 | */
67 | public class NotepadView extends ViewPart implements IPreferenceChangeListener {
68 |
69 | private static final String LOCK_PREFIX = "\uD83D\uDD12 ";
70 | // The ID of the view as specified by the extension.
71 | public static final String ID = "notepad4e.views.NotepadView";
72 | // Keys used to store and retrieve the plugin's view between Eclipse sessions.
73 | private static final String STORE_COUNT_KEY = "NumOfTabs";
74 | private static final String STORE_TEXT_PREFIX_KEY = "TabText";
75 | private static final String STORE_STYLE_PREFIX_KEY = "TabStyle";
76 | private static final String STORE_TITLE_PREFIX_KEY = "TabTitle";
77 | private static final String STORE_EDITABLE_PREFIX_KEY = "TabEditable";
78 | private static final String STORE_BULLETS_PREFIX_KEY = "TabBullets";
79 |
80 | // Keyboard events listener.
81 | private final ShortcutHandler shortcutHandler = new ShortcutHandler(this);
82 |
83 | // Actions corresponding to the different buttons in the view.
84 | private Action addNewNoteAction;
85 | private Action clearNoteAction;
86 | private Action boldTextAction;
87 | private Action italicTextAction;
88 | private Action underlineTextAction;
89 | private Action strikeoutTextAction;
90 | private Action bulletListAction;
91 | private Action clearTextStyleAction;
92 | private Action toggleEditableAction;
93 | private Action exportNoteAction;
94 | private Action preferencesAction;
95 | private Action websiteAction;
96 | private Action changelogAction;
97 | // User defined preferences.
98 | private IEclipsePreferences preferences;
99 | // Object handling the different tabs.
100 | private CTabFolder tabFolder;
101 | // Current clipboard, used for the paste contents of clipboard in new notes feature.
102 | private Clipboard clipboard;
103 | // Note autosave interval.
104 | private long saveIntervalMillis;
105 |
106 | /**
107 | * Allows to create the viewer and initialise it.
108 | */
109 | @Override
110 | public void createPartControl(Composite parent) {
111 | preferences = InstanceScope.INSTANCE.getNode(Notepad4e.PLUGIN_ID);
112 | // Listen to any change to the preferences of the plugin.
113 | preferences.addPreferenceChangeListener(this);
114 |
115 | clipboard = new Clipboard(Display.getCurrent());
116 |
117 | tabFolder = new CTabFolder(parent, SWT.MULTI | SWT.WRAP);
118 |
119 | addPluginDisposeListener();
120 | addCloseTabListener();
121 | addSwapTabListener();
122 | addRenameTabListener();
123 | addTabSelectionListener();
124 |
125 | restoreViewFromPreviousSession();
126 |
127 | saveIntervalMillis = TimeUnit.SECONDS
128 | .toMillis(preferences.getInt(Preferences.SAVE_INTERVAL, Preferences.SAVE_INTERVAL_DEFAULT));
129 | if (saveIntervalMillis >= 0) {
130 | new Job("Notepad4e scheduled autosave") {
131 | @Override
132 | protected IStatus run(IProgressMonitor monitor) {
133 | Display.getDefault().asyncExec(() -> savePluginState(preferences.get(Preferences.SAVE_LOCATION,
134 | Preferences.SAVE_LOCATION_DEFAULT)));
135 | schedule(saveIntervalMillis);
136 | return Status.OK_STATUS;
137 | }
138 | }.schedule(saveIntervalMillis);
139 | }
140 |
141 | PlatformUI.getWorkbench().getHelpSystem().setHelp(tabFolder, "Notepad4e.viewer");
142 |
143 | IContextService contextService = getSite().getService(IContextService.class);
144 | contextService.activateContext("notepad4e.context");
145 |
146 | IHandlerService handlerService = getSite().getService(IHandlerService.class);
147 | // Associate each shortcut command with the shortcut handler.
148 | for (NotepadAction notepadAction : NotepadAction.values()) {
149 | if (notepadAction.getCommandID() != null) {
150 | handlerService.activateHandler(notepadAction.getCommandID(), shortcutHandler);
151 | }
152 | }
153 |
154 | makeActions();
155 | contributeToActionBars();
156 | }
157 |
158 | /**
159 | * Unregisters listeners and cleans up.
160 | */
161 | @Override
162 | public void dispose() {
163 | shortcutHandler.dispose();
164 | tabFolder.dispose();
165 | clipboard.dispose();
166 | preferences.removePreferenceChangeListener(this);
167 | super.dispose();
168 | }
169 |
170 | /**
171 | * Refreshes all notes when a change in the plugin's preferences is detected.
172 | *
173 | * @param event
174 | */
175 | @Override
176 | public void preferenceChange(PreferenceChangeEvent event) {
177 | if (Preferences.SAVE_LOCATION.equals(event.getKey())) {
178 | savePluginState((String) event.getOldValue());
179 | // Load dialog settings using new location.
180 | Notepad4e.getDefault().restoreDialogSettings();
181 | // This will merge newly restored dialog settings with current state of notes.
182 | restoreViewFromPreviousSession();
183 | }
184 | for (int tabIndex = 0; tabIndex < tabFolder.getItemCount(); ++tabIndex) {
185 | getNote(tabIndex).setParametersFromPreferences();
186 | }
187 | saveIntervalMillis = TimeUnit.SECONDS
188 | .toMillis(preferences.getInt(Preferences.SAVE_INTERVAL, Preferences.SAVE_INTERVAL_DEFAULT));
189 | }
190 |
191 | /**
192 | * Passes the focus request to the viewer's control.
193 | */
194 | @Override
195 | public void setFocus() {
196 | if (tabFolder.getItemCount() == 0) {
197 | // Give focus to the plugin; hack-ish trick to "steal" focus from other elements in some scenarios (example:
198 | // no tabs and try to open view again via quick access).
199 | tabFolder.getAccessible().getControl().setFocus();
200 | } else {
201 | // Set focus on the last item in the tabs folder component.
202 | tabFolder.getItem(tabFolder.getItemCount() - 1).getControl().setFocus();
203 | }
204 | }
205 |
206 | /**
207 | * Adds a new note to the notepad.
208 | */
209 | public void addNewNote() {
210 | String noteTitle = getNewNoteTitle();
211 | String noteText = "";
212 | if (preferences.getBoolean(Preferences.PASTE_CLIPBOARD_IN_NEW_NOTES,
213 | Preferences.PASTE_CLIPBOARD_IN_NEW_NOTES_DEFAULT)) {
214 | noteText = (String) clipboard.getContents(TextTransfer.getInstance(), DND.CLIPBOARD);
215 | }
216 | // Add a new note tab with a number appended to its name (Note 1, Note 2, Note 3, etc.).
217 | addNewNoteTab(noteTitle, noteText, null, true, null);
218 | CTabItem previousSelectedTab = tabFolder.getSelection();
219 | // Remove lock for currently selected tab.
220 | if (previousSelectedTab != null && previousSelectedTab.getText().startsWith(LOCK_PREFIX)) {
221 | previousSelectedTab.setText(previousSelectedTab.getText().substring(LOCK_PREFIX.length()));
222 | }
223 | tabFolder.setSelection(tabFolder.getItemCount() - 1);
224 | }
225 |
226 | /**
227 | * Closes the currently selected tab in the view and disposes resources appropriately.
228 | */
229 | public void closeCurrentSelection() {
230 | Note selectedNote = getSelectedNote();
231 | if (selectedNote != null) {
232 | if (!selectedNote.getEditable()) {
233 | if (MessageDialog.openQuestion(getSite().getShell(), LocalStrings.dialogCloseLockedTitle,
234 | LocalStrings.dialogCloseLockedMsg)) {
235 | tabFolder.getSelection().dispose();
236 | }
237 | } else if (!preferences.getBoolean(Preferences.CLOSE_CONFIRMATION, Preferences.CLOSE_CONFIRMATION_DEFAULT)
238 | || MessageDialog.openQuestion(getSite().getShell(), LocalStrings.dialogCloseTitle,
239 | LocalStrings.dialogCloseMsg)) {
240 | tabFolder.getSelection().dispose();
241 | }
242 | }
243 | }
244 |
245 | /**
246 | * Returns the currently selected Note or null.
247 | *
248 | * @return selected Note
249 | */
250 | public Note getSelectedNote() {
251 | return tabFolder.getSelectionIndex() >= 0 ? getNote(tabFolder.getSelectionIndex()) : null;
252 | }
253 |
254 | /**
255 | * Constructs the title of a new note. The title does not match the ones of extisting notes.
256 | *
257 | * @return the note title, for instance "Note 2"
258 | */
259 | private String getNewNoteTitle() {
260 | int noteNumber = tabFolder.getItemCount() + 1;
261 | String title = preferences.get(Preferences.NAME_PREFIX, Preferences.NAME_PREFIX_DEFAULT) + " " + noteNumber;
262 | if (tabFolder.getItemCount() == 0) {
263 | return title;
264 | }
265 | while (true) {
266 | for (int tabIndex = 0; tabIndex < tabFolder.getItemCount(); ++tabIndex) {
267 | if (tabFolder.getItem(tabIndex).getText().contains(title)) {
268 | break;
269 | } else if (tabIndex == tabFolder.getItemCount() - 1) {
270 | return title;
271 | }
272 | }
273 | ++noteNumber;
274 | title = preferences.get(Preferences.NAME_PREFIX, Preferences.NAME_PREFIX_DEFAULT) + " " + noteNumber;
275 | }
276 | }
277 |
278 | /**
279 | * Returns a Note object given an index in the tab folder.
280 | *
281 | * @param index
282 | * @return Note at the given index
283 | */
284 | private Note getNote(int index) {
285 | return (Note) (tabFolder.getItem(index).getControl());
286 | }
287 |
288 | /**
289 | * Listens to disposal of the tab folder and saves state for next Eclipse session or when reopening the view.
290 | */
291 | private void addPluginDisposeListener() {
292 | tabFolder.addDisposeListener(event -> savePluginState(preferences.get(Preferences.SAVE_LOCATION,
293 | Preferences.SAVE_LOCATION_DEFAULT)));
294 | }
295 |
296 | /**
297 | * Saves plugin state for next Eclipse session or when reopening the view.
298 | *
299 | * @param directory
300 | */
301 | private void savePluginState(String directory) {
302 | if (!tabFolder.isDisposed()) {
303 | IDialogSettings section = Notepad4e.getDefault().getDialogSettings().getSection(ID);
304 | section.put(STORE_COUNT_KEY, tabFolder.getItemCount());
305 | for (int tabIndex = 0; tabIndex < tabFolder.getItemCount(); ++tabIndex) {
306 | CTabItem tab = tabFolder.getItem(tabIndex);
307 | if (!tab.isDisposed()) {
308 | Note note = getNote(tabIndex);
309 | section.put(STORE_TEXT_PREFIX_KEY + tabIndex, note.getText());
310 | section.put(STORE_STYLE_PREFIX_KEY + tabIndex, note.serialiseStyle());
311 | if (tab.getText().startsWith(LOCK_PREFIX)) {
312 | // Do not save lock symbol.
313 | section.put(STORE_TITLE_PREFIX_KEY + tabIndex, tab.getText().substring(LOCK_PREFIX.length()));
314 | } else {
315 | section.put(STORE_TITLE_PREFIX_KEY + tabIndex, tab.getText());
316 | }
317 | section.put(STORE_EDITABLE_PREFIX_KEY + tabIndex, note.getEditable());
318 | section.put(STORE_BULLETS_PREFIX_KEY + tabIndex, note.serialiseBullets());
319 | }
320 | }
321 | Notepad4e.getDefault().saveDialogSettings(directory);
322 | }
323 | }
324 |
325 | /**
326 | * Displays a confirmation dialog when closing a note tab, if enabled in preferences.
327 | */
328 | private void addCloseTabListener() {
329 | tabFolder.addCTabFolder2Listener(new CTabFolder2Adapter() {
330 | @Override
331 | public void close(CTabFolderEvent event) {
332 | // Selected tab may not be the one being closed, the one provided by the event must be used.
333 | if (!getNote(tabFolder.indexOf((CTabItem) event.item)).getEditable()) {
334 | event.doit = MessageDialog.openQuestion(getSite().getShell(), LocalStrings.dialogCloseLockedTitle,
335 | LocalStrings.dialogCloseLockedMsg);
336 | } else if (preferences.getBoolean(Preferences.CLOSE_CONFIRMATION, Preferences.CLOSE_CONFIRMATION_DEFAULT)) {
337 | event.doit = MessageDialog.openQuestion(getSite().getShell(), LocalStrings.dialogCloseTitle,
338 | LocalStrings.dialogCloseMsg);
339 | }
340 | }
341 | });
342 | }
343 |
344 | /**
345 | * Allows to rename a tab when user double clicks on its title.
346 | */
347 | private void addRenameTabListener() {
348 | tabFolder.addMouseListener(new MouseAdapter() {
349 | @Override
350 | public void mouseDoubleClick(MouseEvent event) {
351 | CTabItem clickedTab = tabFolder.getItem(new Point(event.x, event.y));
352 | if (clickedTab == null) {
353 | return;
354 | }
355 | boolean isLocked = false;
356 | String dialogText = clickedTab.getText();
357 | if (dialogText.startsWith(LOCK_PREFIX)) {
358 | isLocked = true;
359 | dialogText = dialogText.substring(LOCK_PREFIX.length());
360 | }
361 | // Open a dialog window so user can enter the new name of his note.
362 | InputDialog inputDialog = new InputDialog(null, LocalStrings.dialogRenameTitle,
363 | LocalStrings.dialogRenameMsg, dialogText, null);
364 | inputDialog.open();
365 | // If user selected Cancel, text will be null.
366 | if (inputDialog.getValue() != null && !inputDialog.getValue().isEmpty()) {
367 | if (isLocked) {
368 | clickedTab.setText(LOCK_PREFIX + inputDialog.getValue());
369 | } else {
370 | clickedTab.setText(inputDialog.getValue());
371 | }
372 | }
373 | }
374 | });
375 | }
376 |
377 | /**
378 | * Swaps two tabs and corresponding notes when a user drags one to another.
379 | */
380 | private void addSwapTabListener() {
381 | tabFolder.addDragDetectListener(dragDetectedEvent -> {
382 | Rectangle viewRectangle = Geometry.toDisplay(tabFolder.getParent(), tabFolder.getBounds());
383 | Tracker tracker = new Tracker(tabFolder, SWT.NONE);
384 | tracker.setStippled(true);
385 | tracker.addListener(SWT.Move, event -> {
386 | Point location = new Point(event.x - viewRectangle.x, event.y - viewRectangle.y);
387 | CTabItem tabAtLocation = tabFolder.getItem(location);
388 | if (tabAtLocation != null) {
389 | // Move tracker to follow mouse cursor.
390 | tracker.setRectangles(new Rectangle[] { tabAtLocation.getBounds() });
391 | } else {
392 | // Mouse cursor no longer above any tab in the action bar, hide tacker.
393 | tracker.setRectangles(new Rectangle[0]);
394 | }
395 | });
396 | if (tracker.open()) {
397 | Rectangle[] rectangles = tracker.getRectangles();
398 | if (rectangles.length > 0) {
399 | CTabItem tabToSwap = tabFolder.getItem(new Point(rectangles[0].x, rectangles[0].y));
400 | // Swap selected tab with the one situated at the mouse cursor's position.
401 | if (tabToSwap != null) {
402 | swapNoteTabs(tabFolder.indexOf(tabToSwap));
403 | }
404 | }
405 | }
406 | tracker.close();
407 | tracker.dispose();
408 | });
409 | }
410 |
411 | /**
412 | * Listens for tab selections and displays or removes lock symbol when a locked tab is selected.
413 | */
414 | private void addTabSelectionListener() {
415 | tabFolder.addSelectionListener(new SelectionAdapter() {
416 | @Override
417 | public void widgetSelected(SelectionEvent event) {
418 | // Remove lock symbols from all tabs.
419 | for (int tabIndex = 0; tabIndex < tabFolder.getItemCount(); ++tabIndex) {
420 | CTabItem tab = tabFolder.getItem(tabIndex);
421 | if (tab.getText().startsWith(LOCK_PREFIX)) {
422 | tab.setText(tab.getText().substring(LOCK_PREFIX.length()));
423 | }
424 | }
425 | // Put lock symbol on selected tab, if non editable.
426 | if (!getSelectedNote().getEditable()) {
427 | CTabItem selectedTab = (CTabItem) event.item;
428 | selectedTab.setText(LOCK_PREFIX + selectedTab.getText());
429 | }
430 | }
431 | });
432 | }
433 |
434 | /**
435 | * Allows to restore the plugin's view as it was in a previous session of Eclipse.
436 | */
437 | private void restoreViewFromPreviousSession() {
438 | IDialogSettings settings = Notepad4e.getDefault().getDialogSettings();
439 | IDialogSettings section = settings.getSection(ID);
440 | if (section == null) {
441 | section = settings.addNewSection(ID);
442 | }
443 |
444 | int numOfTabs = 0;
445 | String numOfTabsString = section.get(STORE_COUNT_KEY);
446 | // numOfTabsString can be null if plugin was not previously launched in this working environment.
447 | if (numOfTabsString != null) {
448 | numOfTabs = Integer.parseInt(numOfTabsString);
449 | }
450 |
451 | if (numOfTabs == 0 && tabFolder.getItemCount() == 0) {
452 | // No notes were previously opened: create new one.
453 | String prefixName = preferences.get(Preferences.NAME_PREFIX, Preferences.NAME_PREFIX_DEFAULT);
454 | addNewNoteTab(prefixName + " 1", "", null, true, null);
455 | // Set selection on this tab.
456 | tabFolder.setSelection(0);
457 | } else {
458 | // Populate with tabs opened in previous session.
459 | for (int tabIndex = 0; tabIndex < numOfTabs; ++tabIndex) {
460 | String tabTitle = section.get(STORE_TITLE_PREFIX_KEY + tabIndex);
461 | boolean editable = section.get(STORE_EDITABLE_PREFIX_KEY + tabIndex) == null ? true
462 | : section.getBoolean(STORE_EDITABLE_PREFIX_KEY + tabIndex);
463 | String noteText = section.get(STORE_TEXT_PREFIX_KEY + tabIndex);
464 | String noteStyle = section.get(STORE_STYLE_PREFIX_KEY + tabIndex);
465 | String noteBullets = section.get(STORE_BULLETS_PREFIX_KEY + tabIndex);
466 | if (tabTitle != null && noteText != null) {
467 | addNewNoteTab(tabTitle, noteText, noteStyle, editable, noteBullets);
468 | }
469 | }
470 | // Set selection on the last tab.
471 | tabFolder.setSelection(tabFolder.getItemCount() - 1);
472 | if (!getSelectedNote().getEditable()) {
473 | tabFolder.getSelection().setText(LOCK_PREFIX + tabFolder.getSelection().getText());
474 | }
475 | }
476 | }
477 |
478 | /**
479 | * Adds a new note to the view.
480 | *
481 | * @param title
482 | * @param text
483 | * @param style
484 | * @param editable
485 | * @param bullets
486 | */
487 | private void addNewNoteTab(String title, String text, String style, boolean editable, String bullets) {
488 | CTabItem tab = new CTabItem(tabFolder, SWT.NONE);
489 | tab.setText(title);
490 | // Add listener to clean up corresponding note when disposing the tab.
491 | tab.addDisposeListener(event -> {
492 | CTabItem itemToDispose = (CTabItem) event.getSource();
493 | ((Note) itemToDispose.getControl()).dispose();
494 | });
495 | Note note = new Note(tabFolder, text, style, bullets, editable);
496 | tab.setControl(note);
497 | }
498 |
499 | /**
500 | * Populates the different action bars of the view.
501 | */
502 | private void contributeToActionBars() {
503 | IActionBars bars = getViewSite().getActionBars();
504 | fillLocalPullDown(bars.getMenuManager());
505 | fillLocalToolBar(bars.getToolBarManager());
506 | }
507 |
508 | /**
509 | * Populates the drop down menu action bar.
510 | *
511 | * @param manager
512 | */
513 | private void fillLocalPullDown(IMenuManager manager) {
514 | manager.add(toggleEditableAction);
515 | manager.add(exportNoteAction);
516 | manager.add(new Separator());
517 | manager.add(preferencesAction);
518 | manager.add(websiteAction);
519 | manager.add(changelogAction);
520 | }
521 |
522 | /**
523 | * Populates the tool bar.
524 | *
525 | * @param manager
526 | */
527 | private void fillLocalToolBar(IToolBarManager manager) {
528 | manager.add(boldTextAction);
529 | manager.add(italicTextAction);
530 | manager.add(underlineTextAction);
531 | manager.add(strikeoutTextAction);
532 | manager.add(bulletListAction);
533 | manager.add(clearTextStyleAction);
534 | manager.add(new Separator());
535 | manager.add(addNewNoteAction);
536 | manager.add(clearNoteAction);
537 | }
538 |
539 | /**
540 | * Defines all the plugin's actions corresponding to the different buttons in the view.
541 | */
542 | private void makeActions() {
543 | addNewNoteAction = new Action() {
544 | @Override
545 | public void run() {
546 | addNewNote();
547 | }
548 | };
549 | setTextAndImageToAction(addNewNoteAction, NotepadAction.NEW_NOTE);
550 |
551 | clearNoteAction = new AbstractSelectedNoteAction(this) {
552 | @Override
553 | protected void runSelectedNoteAction(Note selectedNote) {
554 | selectedNote.clearText();
555 | }
556 | };
557 | setTextAndImageToAction(clearNoteAction, NotepadAction.CLEAR_NOTE);
558 |
559 | boldTextAction = new AbstractSelectedNoteAction(this) {
560 | @Override
561 | protected void runSelectedNoteAction(Note selectedNote) {
562 | selectedNote.boldSelection();
563 | }
564 | };
565 | setTextAndImageToAction(boldTextAction, NotepadAction.BOLD_TEXT);
566 |
567 | italicTextAction = new AbstractSelectedNoteAction(this) {
568 | @Override
569 | protected void runSelectedNoteAction(Note selectedNote) {
570 | selectedNote.italicSelection();
571 | }
572 | };
573 | setTextAndImageToAction(italicTextAction, NotepadAction.ITALIC_TEXT);
574 |
575 | underlineTextAction = new AbstractSelectedNoteAction(this) {
576 | @Override
577 | protected void runSelectedNoteAction(Note selectedNote) {
578 | selectedNote.underlineSelection();
579 | }
580 | };
581 | setTextAndImageToAction(underlineTextAction, NotepadAction.UNDERLINE_TEXT);
582 |
583 | strikeoutTextAction = new AbstractSelectedNoteAction(this) {
584 | @Override
585 | protected void runSelectedNoteAction(Note selectedNote) {
586 | selectedNote.strikeoutSelection();
587 | }
588 | };
589 | setTextAndImageToAction(strikeoutTextAction, NotepadAction.STRIKEOUT_TEXT);
590 |
591 | bulletListAction = new AbstractSelectedNoteAction(this) {
592 | @Override
593 | protected void runSelectedNoteAction(Note selectedNote) {
594 | selectedNote.bulletListSelection();
595 | }
596 | };
597 | setTextAndImageToAction(bulletListAction, NotepadAction.BULLET_LIST);
598 |
599 | clearTextStyleAction = new AbstractSelectedNoteAction(this) {
600 | @Override
601 | protected void runSelectedNoteAction(Note selectedNote) {
602 | selectedNote.clearSelectionStyles();
603 | }
604 | };
605 | setTextAndImageToAction(clearTextStyleAction, NotepadAction.CLEAR_STYLE_TEXT);
606 |
607 | toggleEditableAction = new AbstractSelectedNoteAction(this) {
608 | @Override
609 | protected void runSelectedNoteAction(Note selectedNote) {
610 | CTabItem tab = tabFolder.getSelection();
611 | if (!selectedNote.getEditable()) {
612 | tab.setText(tab.getText().substring(LOCK_PREFIX.length()));
613 | } else {
614 | tab.setText(LOCK_PREFIX + tab.getText());
615 | }
616 | selectedNote.toggleEditable();
617 | }
618 | };
619 | setTextAndImageToAction(toggleEditableAction, NotepadAction.TOGGLE_EDITABLE_NOTE);
620 |
621 | exportNoteAction = new AbstractSelectedNoteAction(this) {
622 | @Override
623 | protected void runSelectedNoteAction(Note selectedNote) {
624 | selectedNote.exportToFile(getSite());
625 | }
626 | };
627 | setTextAndImageToAction(exportNoteAction, NotepadAction.EXPORT_NOTE);
628 |
629 | preferencesAction = new Action() {
630 | @Override
631 | public void run() {
632 | // Create preference dialog page that will appear in current workbench window.
633 | PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null,
634 | "notepad4e.preferences.PreferencePage", new String[] { "notepad4e.preferences.PreferencePage" },
635 | null);
636 | dialog.open();
637 | }
638 | };
639 | setTextAndImageToAction(preferencesAction, NotepadAction.PREFERENCES);
640 |
641 | websiteAction = new Action() {
642 | @Override
643 | public void run() {
644 | // Open website in the user's external browser.
645 | Program.launch("https://github.com/PyvesB/Notepad4e");
646 | }
647 | };
648 | setTextAndImageToAction(websiteAction, NotepadAction.WEBSITE);
649 |
650 | changelogAction = new Action() {
651 | @Override
652 | public void run() {
653 | // Open changelog page in the user's external browser.
654 | Program.launch("https://github.com/PyvesB/Notepad4e/releases");
655 | }
656 | };
657 | setTextAndImageToAction(changelogAction, NotepadAction.CHANGELOG);
658 | }
659 |
660 | /**
661 | * Sets the image and the tool tip to an action button.
662 | *
663 | * @param action
664 | * @param notepadAction
665 | */
666 | private void setTextAndImageToAction(Action action, NotepadAction notepadAction) {
667 | if (notepadAction.getCommandID() != null) {
668 | // Action appears in action bar with an associated shortcut.
669 | action.setToolTipText(notepadAction.getText() + getKeyBindingDescription(notepadAction.getCommandID()));
670 | } else {
671 | // Action appears in drop down menu.
672 | action.setText(notepadAction.getText());
673 | }
674 |
675 | // The URL matches an image in the plugin's icons folder.
676 | URL url = FileLocator.find(Platform.getBundle(Notepad4e.PLUGIN_ID), new Path(notepadAction.getImagePath()),
677 | null);
678 | action.setImageDescriptor(ImageDescriptor.createFromURL(url));
679 | }
680 |
681 | /**
682 | * Returns key binding as a String for a given command ID.
683 | *
684 | * @param commandID
685 | * @return the key binding, for instance Ctrl + B
686 | */
687 | private String getKeyBindingDescription(String commandID) {
688 | Binding bestBinding = null;
689 | for (Binding binding : getViewSite().getService(IBindingService.class).getBindings()) {
690 | if (binding.getParameterizedCommand() != null
691 | && commandID.equals(binding.getParameterizedCommand().getId())) {
692 | if (bestBinding == null) {
693 | bestBinding = binding;
694 | } else if (binding.getType() == Binding.USER) {
695 | // Give higher priority to a user type binding (user has overriden default).
696 | bestBinding = binding;
697 | break;
698 | }
699 | }
700 | }
701 | return bestBinding == null ? "" : " " + bestBinding.getTriggerSequence().format();
702 | }
703 |
704 | /**
705 | * Swaps two tabs and corresponding notes in the view.
706 | *
707 | * @param swappedIndex
708 | */
709 | private void swapNoteTabs(int swappedIndex) {
710 | Note selectedNote = getSelectedNote();
711 | Note swappedNote = getNote(swappedIndex);
712 | tabFolder.getItem(swappedIndex).setControl(selectedNote);
713 | tabFolder.getSelection().setControl(swappedNote);
714 |
715 | String selectedTitle = tabFolder.getSelection().getText();
716 | String swappedTitle = tabFolder.getItem(swappedIndex).getText();
717 | tabFolder.getItem(swappedIndex).setText(selectedTitle);
718 | tabFolder.getSelection().setText(swappedTitle);
719 |
720 | tabFolder.setSelection(swappedIndex);
721 | }
722 | }
723 |
--------------------------------------------------------------------------------