|
50 | 50 | import java.util.Collection;
|
51 | 51 | import java.util.List;
|
52 | 52 |
|
| 53 | +import javax.swing.Action; |
53 | 54 | import javax.swing.ImageIcon;
|
| 55 | +import javax.swing.JMenu; |
| 56 | +import javax.swing.JMenuItem; |
54 | 57 | import javax.swing.JOptionPane;
|
| 58 | +import javax.swing.JPopupMenu; |
55 | 59 | import javax.swing.JScrollPane;
|
56 | 60 | import javax.swing.JViewport;
|
| 61 | +import javax.swing.KeyStroke; |
57 | 62 | import javax.swing.ToolTipManager;
|
| 63 | +import javax.swing.UIManager; |
58 | 64 | import javax.swing.event.DocumentEvent;
|
59 | 65 | import javax.swing.event.DocumentListener;
|
60 | 66 | import javax.swing.event.HyperlinkEvent;
|
61 | 67 | import javax.swing.event.HyperlinkListener;
|
62 | 68 | import javax.swing.text.BadLocationException;
|
| 69 | +import javax.swing.text.Caret; |
63 | 70 | import javax.swing.text.DefaultEditorKit;
|
| 71 | +import javax.swing.text.Document; |
| 72 | +import javax.swing.text.Element; |
| 73 | +import javax.swing.text.Segment; |
64 | 74 |
|
65 | 75 | import org.fife.rsta.ac.LanguageSupport;
|
66 | 76 | import org.fife.rsta.ac.LanguageSupportFactory;
|
67 | 77 | import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
68 | 78 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
| 79 | +import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit; |
| 80 | +import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit.CopyAsStyledTextAction; |
| 81 | +import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit.ToggleCommentAction; |
| 82 | +import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit.DecreaseIndentAction; |
| 83 | +import org.fife.ui.rtextarea.RTextAreaEditorKit.*; |
69 | 84 | import org.fife.ui.rsyntaxtextarea.Style;
|
70 | 85 | import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
|
71 | 86 | import org.fife.ui.rsyntaxtextarea.Theme;
|
72 | 87 | import org.fife.ui.rtextarea.Gutter;
|
73 | 88 | import org.fife.ui.rtextarea.GutterIconInfo;
|
74 | 89 | import org.fife.ui.rtextarea.RTextArea;
|
| 90 | +import org.fife.ui.rtextarea.RTextAreaEditorKit; |
75 | 91 | import org.fife.ui.rtextarea.RTextScrollPane;
|
76 | 92 | import org.fife.ui.rtextarea.RecordableTextAction;
|
77 | 93 | import org.fife.ui.rtextarea.SearchContext;
|
@@ -155,21 +171,28 @@ public void hyperlinkUpdate(final HyperlinkEvent hle) {
|
155 | 171 | }
|
156 | 172 | }
|
157 | 173 | });
|
158 |
| - |
| 174 | + |
159 | 175 | // load preferences
|
160 | 176 | loadPreferences();
|
161 | 177 |
|
162 |
| - getActionMap() |
163 |
| - .put(DefaultEditorKit.nextWordAction, wordMovement(+1, false)); |
164 |
| - getActionMap().put(DefaultEditorKit.selectionNextWordAction, |
165 |
| - wordMovement(+1, true)); |
166 |
| - getActionMap().put(DefaultEditorKit.previousWordAction, |
167 |
| - wordMovement(-1, false)); |
| 178 | + // Register recordable actions |
| 179 | + getActionMap().put(DefaultEditorKit.nextWordAction, wordMovement("Next-Word-Action", +1, false)); |
| 180 | + getActionMap().put(DefaultEditorKit.selectionNextWordAction, wordMovement("Next-Word-Select-Action", +1, true)); |
| 181 | + getActionMap().put(DefaultEditorKit.previousWordAction, wordMovement("Prev-Word-Action", -1, false)); |
168 | 182 | getActionMap().put(DefaultEditorKit.selectionPreviousWordAction,
|
169 |
| - wordMovement(-1, true)); |
| 183 | + wordMovement("Prev-Word-Select-Action", -1, true)); |
| 184 | + getActionMap().put(RTextAreaEditorKit.rtaTimeDateAction, new TimeDateAction()); |
| 185 | + if (getActionMap().get(RTextAreaEditorKit.clipboardHistoryAction) != null) |
| 186 | + getActionMap().put(RTextAreaEditorKit.clipboardHistoryAction, new ClipboardHistoryAction()); |
| 187 | + if (getActionMap().get(RSyntaxTextAreaEditorKit.rstaToggleCommentAction) != null) |
| 188 | + getActionMap().put(RSyntaxTextAreaEditorKit.rstaToggleCommentAction, new ToggleCommentAction()); |
| 189 | + if (getActionMap().get(RSyntaxTextAreaEditorKit.rstaCopyAsStyledTextAction) != null) |
| 190 | + getActionMap().put(RSyntaxTextAreaEditorKit.rstaCopyAsStyledTextAction, new CopyAsStyledTextAction()); |
| 191 | + |
| 192 | + adjustPopupMenu(); |
| 193 | + |
170 | 194 | ToolTipManager.sharedInstance().registerComponent(this);
|
171 | 195 | getDocument().addDocumentListener(this);
|
172 |
| - |
173 | 196 | addMouseListener(new MouseAdapter() {
|
174 | 197 |
|
175 | 198 | SearchContext context;
|
@@ -207,6 +230,32 @@ public void mousePressed(final MouseEvent me) {
|
207 | 230 | });
|
208 | 231 | }
|
209 | 232 |
|
| 233 | + private void adjustPopupMenu() { |
| 234 | + final JPopupMenu popup = super.getPopupMenu(); |
| 235 | + JMenu menu = new JMenu("Move"); |
| 236 | + popup.add(menu); |
| 237 | + menu.add(getMenuItem("Decrease Indent", new DecreaseIndentAction())); |
| 238 | + menu.add(getMenuItem("Increase Indent", new IncreaseIndentAction())); |
| 239 | + menu.addSeparator(); |
| 240 | + menu.add(getMenuItem("Move Up", new LineMoveAction(RTextAreaEditorKit.rtaLineUpAction, -1))); |
| 241 | + menu.add(getMenuItem("Move Down", new LineMoveAction(RTextAreaEditorKit.rtaLineDownAction, 1))); |
| 242 | + menu = new JMenu("Transform"); |
| 243 | + popup.add(menu); |
| 244 | + menu.add(getMenuItem("Camel Case", new CamelCaseAction())); |
| 245 | + menu.add(getMenuItem("Invert Case", new InvertSelectionCaseAction())); |
| 246 | + menu.add(getMenuItem("Lower Case", new LowerSelectionCaseAction())); |
| 247 | + menu.add(getMenuItem("Upper Case", new UpperSelectionCaseAction())); |
| 248 | + } |
| 249 | + |
| 250 | + private JMenuItem getMenuItem(final String label, final RecordableTextAction a) { |
| 251 | + JMenuItem item = new JMenuItem(a); |
| 252 | + item.setAccelerator((KeyStroke) a.getValue(Action.ACCELERATOR_KEY)); |
| 253 | + if (getActionMap().get(a.getName()) == null) |
| 254 | + getActionMap().put(a.getName(), a); // make it recordable |
| 255 | + item.setText(label); |
| 256 | + return item; |
| 257 | + } |
| 258 | + |
210 | 259 | @Override
|
211 | 260 | public void setTabSize(final int width) {
|
212 | 261 | if (getTabSize() != width) super.setTabSize(width);
|
@@ -261,16 +310,21 @@ private ImageIcon createBookmarkIcon() {
|
261 | 310 | return new ImageIcon(image);
|
262 | 311 | }
|
263 | 312 |
|
264 |
| - /** |
265 |
| - * TODO |
266 |
| - * |
267 |
| - * @param direction |
268 |
| - * @param select |
269 |
| - * @return |
270 |
| - */ |
271 |
| - RecordableTextAction wordMovement(final int direction, final boolean select) { |
272 |
| - final String id = "WORD_MOVEMENT_" + select + direction; |
| 313 | + RecordableTextAction wordMovement(final String id, final int direction, final boolean select) { |
273 | 314 | return new RecordableTextAction(id) {
|
| 315 | + private static final long serialVersionUID = 1L; |
| 316 | + |
| 317 | + @Override |
| 318 | + public String getDescription() { |
| 319 | + final StringBuilder sb = new StringBuilder(); |
| 320 | + if (direction > 0) |
| 321 | + sb.append("Next"); |
| 322 | + else |
| 323 | + sb.append("Previous"); |
| 324 | + sb.append("Word"); |
| 325 | + if (select) sb.append("Select"); |
| 326 | + return sb.toString(); |
| 327 | + } |
274 | 328 |
|
275 | 329 | @Override
|
276 | 330 | public void actionPerformedImpl(final ActionEvent e,
|
@@ -1007,4 +1061,139 @@ String getSupportStatus() {
|
1007 | 1061 | return supportStatus;
|
1008 | 1062 | }
|
1009 | 1063 |
|
| 1064 | + static class CamelCaseAction extends RecordableTextAction { |
| 1065 | + private static final long serialVersionUID = 1L; |
| 1066 | + |
| 1067 | + CamelCaseAction() { |
| 1068 | + super("RTA.CamelCaseAction"); |
| 1069 | + } |
| 1070 | + |
| 1071 | + @Override |
| 1072 | + public void actionPerformedImpl(final ActionEvent e, final RTextArea textArea) { |
| 1073 | + if (!textArea.isEditable() || !textArea.isEnabled()) { |
| 1074 | + UIManager.getLookAndFeel().provideErrorFeedback(textArea); |
| 1075 | + return; |
| 1076 | + } |
| 1077 | + final String selection = textArea.getSelectedText(); |
| 1078 | + if (selection != null) { |
| 1079 | + final String[] words = selection.split("[\\W_]+"); |
| 1080 | + final StringBuilder buffer = new StringBuilder(); |
| 1081 | + for (int i = 0; i < words.length; i++) { |
| 1082 | + String word = words[i]; |
| 1083 | + if (i == 0) { |
| 1084 | + word = word.isEmpty() ? word : word.toLowerCase(); |
| 1085 | + } else { |
| 1086 | + word = word.isEmpty() ? word |
| 1087 | + : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase(); |
| 1088 | + } |
| 1089 | + buffer.append(word); |
| 1090 | + } |
| 1091 | + textArea.replaceSelection(buffer.toString()); |
| 1092 | + } |
| 1093 | + textArea.requestFocusInWindow(); |
| 1094 | + } |
| 1095 | + |
| 1096 | + @Override |
| 1097 | + public String getMacroID() { |
| 1098 | + return getName(); |
| 1099 | + } |
| 1100 | + |
| 1101 | + } |
| 1102 | + |
| 1103 | + /** Modified from DecreaseIndentAction */ |
| 1104 | + static class IncreaseIndentAction extends RecordableTextAction { |
| 1105 | + |
| 1106 | + private static final long serialVersionUID = 1L; |
| 1107 | + |
| 1108 | + private final Segment s; |
| 1109 | + |
| 1110 | + public IncreaseIndentAction() { |
| 1111 | + super("RSTA.IncreaseIndentAction"); |
| 1112 | + s = new Segment(); |
| 1113 | + } |
| 1114 | + |
| 1115 | + @Override |
| 1116 | + public void actionPerformedImpl(final ActionEvent e, final RTextArea textArea) { |
| 1117 | + |
| 1118 | + if (!textArea.isEditable() || !textArea.isEnabled()) { |
| 1119 | + UIManager.getLookAndFeel().provideErrorFeedback(textArea); |
| 1120 | + return; |
| 1121 | + } |
| 1122 | + |
| 1123 | + final Document document = textArea.getDocument(); |
| 1124 | + final Element map = document.getDefaultRootElement(); |
| 1125 | + final Caret c = textArea.getCaret(); |
| 1126 | + int dot = c.getDot(); |
| 1127 | + int mark = c.getMark(); |
| 1128 | + int line1 = map.getElementIndex(dot); |
| 1129 | + final int tabSize = textArea.getTabSize(); |
| 1130 | + final StringBuilder sb = new StringBuilder(); |
| 1131 | + if (textArea.getTabsEmulated()) { |
| 1132 | + while (sb.length() < tabSize) { |
| 1133 | + sb.append(' '); |
| 1134 | + } |
| 1135 | + } else { |
| 1136 | + sb.append('\t'); |
| 1137 | + } |
| 1138 | + final String paddingString = sb.toString(); |
| 1139 | + |
| 1140 | + // If there is a selection, indent all lines in the selection. |
| 1141 | + // Otherwise, indent the line the caret is on. |
| 1142 | + if (dot != mark) { |
| 1143 | + final int line2 = map.getElementIndex(mark); |
| 1144 | + dot = Math.min(line1, line2); |
| 1145 | + mark = Math.max(line1, line2); |
| 1146 | + Element elem; |
| 1147 | + textArea.beginAtomicEdit(); |
| 1148 | + try { |
| 1149 | + for (line1 = dot; line1 < mark; line1++) { |
| 1150 | + elem = map.getElement(line1); |
| 1151 | + handleIncreaseIndent(elem, document, paddingString); |
| 1152 | + } |
| 1153 | + // Don't do the last line if the caret is at its |
| 1154 | + // beginning. We must call getDot() again and not just |
| 1155 | + // use 'dot' as the caret's position may have changed |
| 1156 | + // due to the insertion of the tabs above. |
| 1157 | + elem = map.getElement(mark); |
| 1158 | + final int start = elem.getStartOffset(); |
| 1159 | + if (Math.max(c.getDot(), c.getMark()) != start) { |
| 1160 | + handleIncreaseIndent(elem, document, paddingString); |
| 1161 | + } |
| 1162 | + } catch (final BadLocationException ble) { |
10000
| 1163 | + ble.printStackTrace(); |
| 1164 | + UIManager.getLookAndFeel().provideErrorFeedback(textArea); |
| 1165 | + } finally { |
| 1166 | + textArea.endAtomicEdit(); |
| 1167 | + } |
| 1168 | + } else { |
| 1169 | + final Element elem = map.getElement(line1); |
| 1170 | + try { |
| 1171 | + handleIncreaseIndent(elem, document, paddingString); |
| 1172 | + } catch (final BadLocationException ble) { |
| 1173 | + ble.printStackTrace(); |
| 1174 | + UIManager.getLookAndFeel().provideErrorFeedback(textArea); |
| 1175 | + } |
| 1176 | + } |
| 1177 | + |
| 1178 | + } |
| 1179 | + |
| 1180 | + @Override |
| 1181 | + public final String getMacroID() { |
| 1182 | + return getName(); |
| 1183 | + } |
| 1184 | + |
| 1185 | + private void handleIncreaseIndent(final Element elem, final Document doc, final String pad) |
| 1186 | + throws BadLocationException { |
| 1187 | + final int start = elem.getStartOffset(); |
| 1188 | + int end = elem.getEndOffset() - 1; // Why always true?? |
| 1189 | + doc.getText(start, end - start, s); |
| 1190 | + final int i = s.offset; |
| 1191 | + end = i + s.count; |
| 1192 | + if (end > i || (end == i && i == 0)) { |
| 1193 | + doc.insertString(start, pad, null); |
| 1194 | + } |
| 1195 | + } |
| 1196 | + |
| 1197 | + } |
| 1198 | + |
1010 | 1199 | }
|
0 commit comments