-
Notifications
You must be signed in to change notification settings - Fork 938
Expand file tree
/
Copy pathBaseKit.java
More file actions
4478 lines (3939 loc) · 219 KB
/
Copy pathBaseKit.java
File metadata and controls
4478 lines (3939 loc) · 219 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.editor;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Reader;
import java.io.Writer;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.prefs.PreferenceChangeEvent;
import javax.swing.Action;
import javax.swing.InputMap;
import javax.swing.JEditorPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;
import javax.swing.text.Document;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.ViewFactory;
import javax.swing.text.Caret;
import javax.swing.text.JTextComponent;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.TextUI;
import javax.swing.text.AbstractDocument;
import static javax.swing.text.DefaultEditorKit.selectionBackwardAction;
import static javax.swing.text.DefaultEditorKit.selectionBeginLineAction;
import static javax.swing.text.DefaultEditorKit.selectionDownAction;
import static javax.swing.text.DefaultEditorKit.selectionEndLineAction;
import static javax.swing.text.DefaultEditorKit.selectionForwardAction;
import static javax.swing.text.DefaultEditorKit.selectionUpAction;
import javax.swing.text.EditorKit;
import javax.swing.text.Position;
import javax.swing.text.View;
import org.netbeans.api.editor.caret.CaretInfo;
import org.netbeans.api.editor.EditorActionRegistration;
import org.netbeans.api.editor.EditorActionRegistrations;
import org.netbeans.api.editor.EditorUtilities;
import org.netbeans.api.editor.caret.EditorCaret;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.settings.KeyBindingSettings;
import org.netbeans.api.editor.settings.SimpleValueNames;
import org.netbeans.lib.editor.util.CharSequenceUtilities;
import org.netbeans.lib.editor.util.ListenerList;
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
import org.netbeans.modules.editor.indent.api.Indent;
import org.netbeans.modules.editor.indent.api.IndentUtils;
import org.netbeans.modules.editor.indent.api.Reformat;
import org.netbeans.modules.editor.indent.spi.CodeStylePreferences;
import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults;
import org.netbeans.modules.editor.lib2.EditorPreferencesKeys;
import org.netbeans.modules.editor.lib.KitsTracker;
import org.netbeans.api.editor.NavigationHistory;
import org.netbeans.api.editor.caret.CaretMoveContext;
import org.netbeans.api.editor.caret.MoveCaretsOrigin;
import org.netbeans.api.editor.document.LineDocumentUtils;
import org.netbeans.spi.editor.caret.CaretMoveHandler;
import org.netbeans.lib.editor.util.swing.PositionRegion;
import org.netbeans.modules.editor.lib.SettingsConversions;
import org.netbeans.modules.editor.lib2.RectangularSelectionCaretAccessor;
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils;
import org.netbeans.modules.editor.lib2.actions.KeyBindingsUpdater;
import org.netbeans.modules.editor.lib2.typinghooks.DeletedTextInterceptorsManager;
import org.netbeans.modules.editor.lib2.typinghooks.TypedBreakInterceptorsManager;
import org.netbeans.modules.editor.lib2.typinghooks.TypedTextInterceptorsManager;
import org.netbeans.spi.editor.typinghooks.CamelCaseInterceptor;
import org.openide.awt.StatusDisplayer;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
/**
* Editor kit implementation for base document
*
* @author Miloslav Metelka
* @version 1.00
*/
public class BaseKit extends DefaultEditorKit {
/**
* Flag indicating that the JTextComponent.paste() is in progress.
* Checked in BaseDocument.read() to ignore clearing of the regions
* for trailing-whitespace-removal.
*/
static ThreadLocal<Boolean> IN_PASTE = new ThreadLocal<Boolean>();
// -J-Dorg.netbeans.editor.BaseKit.level=FINEST
private static final Logger LOG = Logger.getLogger(BaseKit.class.getName());
/** split the current line at cursor position */
public static final String splitLineAction = "split-line"; // NOI18N
/** Cycle through annotations on the current line */
public static final String annotationsCyclingAction = "annotations-cycling"; // NOI18N
/** Collapse a fold. Depends on the current caret position. */
public static final String collapseFoldAction = "collapse-fold"; //NOI18N
/** Expand a fold. Depends on the current caret position. */
public static final String expandFoldAction = "expand-fold"; //NOI18N
/** Collapse all existing folds in the document. */
public static final String collapseAllFoldsAction = "collapse-all-folds"; //NOI18N
/** Expand all existing folds in the document. */
public static final String expandAllFoldsAction = "expand-all-folds"; //NOI18N
/** Move one page up and make or extend selection */
public static final String selectionPageUpAction = "selection-page-up"; // NOI18N
/** Move one page down and make or extend selection */
public static final String selectionPageDownAction = "selection-page-down"; // NOI18N
/** Remove indentation */
public static final String removeTabAction = "remove-tab"; // NOI18N
/** Remove selected block or do nothing - useful for popup menu */
public static final String removeSelectionAction = "remove-selection"; // NOI18N
/** Expand the abbreviation */
public static final String abbrevExpandAction = "abbrev-expand"; // NOI18N
/** Reset the abbreviation accounting string */
public static final String abbrevResetAction = "abbrev-reset"; // NOI18N
/** Remove characters to the begining of the word or
* the previous word if caret is not directly at word */
public static final String removePreviousWordAction = "remove-word-previous"; // NOI18N
/** Remove characters to the end of the word or
* the next word if caret is not directly at word */
public static final String removeNextWordAction = "remove-word-next"; // NOI18N
/** Remove to the begining of the line */
public static final String removeLineBeginAction = "remove-line-begin"; // NOI18N
/** Remove line */
public static final String removeLineAction = "remove-line"; // NOI18N
public static final String moveSelectionElseLineUpAction = "move-selection-else-line-up"; // NOI18N
public static final String moveSelectionElseLineDownAction = "move-selection-else-line-down"; // NOI18N
public static final String copySelectionElseLineUpAction = "copy-selection-else-line-up"; // NOI18N
public static final String copySelectionElseLineDownAction = "copy-selection-else-line-down"; // NOI18N
/** Toggle the typing mode to overwrite mode or back to insert mode */
public static final String toggleTypingModeAction = "toggle-typing-mode"; // NOI18N
/** Change the selected text or current character to uppercase */
public static final String toUpperCaseAction = "to-upper-case"; // NOI18N
/** Change the selected text or current character to lowercase */
public static final String toLowerCaseAction = "to-lower-case"; // NOI18N
/** Switch the case of the selected text or current character */
public static final String switchCaseAction = "switch-case"; // NOI18N
/** Find next occurence action */
public static final String findNextAction = "find-next"; // NOI18N
/** Find previous occurence action */
public static final String findPreviousAction = "find-previous"; // NOI18N
/** Toggle highlight search action */
public static final String toggleHighlightSearchAction = "toggle-highlight-search"; // NOI18N
/** Find current word */
public static final String findSelectionAction = "find-selection"; // NOI18N
/** Undo action */
public static final String undoAction = "undo"; // NOI18N
/** Redo action */
public static final String redoAction = "redo"; // NOI18N
/** Word match next */
public static final String wordMatchNextAction = "word-match-next"; // NOI18N
/** Word match prev */
public static final String wordMatchPrevAction = "word-match-prev"; // NOI18N
/** Reindent Line action */
public static final String reindentLineAction = "reindent-line"; // NOI18N
/** Reformat Line action */
public static final String reformatLineAction = "reformat-line"; // NOI18N
/** Shift line right action */
public static final String shiftLineRightAction = "shift-line-right"; // NOI18N
/** Shift line left action */
public static final String shiftLineLeftAction = "shift-line-left"; // NOI18N
/** Action that scrolls the window so that caret is at the center of the window */
public static final String adjustWindowCenterAction = "adjust-window-center"; // NOI18N
/** Action that scrolls the window so that caret is at the top of the window */
public static final String adjustWindowTopAction = "adjust-window-top"; // NOI18N
/** Action that scrolls the window so that caret is at the bottom of the window */
public static final String adjustWindowBottomAction = "adjust-window-bottom"; // NOI18N
/** Action that moves the caret so that caret is at the center of the window */
public static final String adjustCaretCenterAction = "adjust-caret-center"; // NOI18N
/** Action that moves the caret so that caret is at the top of the window */
public static final String adjustCaretTopAction = "adjust-caret-top"; // NOI18N
/** Action that moves the caret so that caret is at the bottom of the window */
public static final String adjustCaretBottomAction = "adjust-caret-bottom"; // NOI18N
/** Format part of the document text using Indent */
public static final String formatAction = "format"; // NOI18N
/** Indent part of the document text using Indent */
public static final String indentAction = "indent"; // NOI18N
/** First non-white character on the line */
public static final String firstNonWhiteAction = "first-non-white"; // NOI18N
/** Last non-white character on the line */
public static final String lastNonWhiteAction = "last-non-white"; // NOI18N
/** First non-white character on the line */
public static final String selectionFirstNonWhiteAction = "selection-first-non-white"; // NOI18N
/** Last non-white character on the line */
public static final String selectionLastNonWhiteAction = "selection-last-non-white"; // NOI18N
/** Select the nearest identifier around caret */
public static final String selectIdentifierAction = "select-identifier"; // NOI18N
/** Select the next parameter (after the comma) in the given context */
public static final String selectNextParameterAction = "select-next-parameter"; // NOI18N
/** Go to the previous position stored in the jump-list */
public static final String jumpListNextAction = "jump-list-next"; // NOI18N
/** Go to the next position stored in the jump-list */
public static final String jumpListPrevAction = "jump-list-prev"; // NOI18N
/** Go to the last position in the previous component stored in the jump-list */
public static final String jumpListNextComponentAction = "jump-list-next-component"; // NOI18N
/** Go to the next position in the previous component stored in the jump-list */
public static final String jumpListPrevComponentAction = "jump-list-prev-component"; // NOI18N
/** Scroll window one line up */
public static final String scrollUpAction = "scroll-up"; // NOI18N
/** Scroll window one line down */
public static final String scrollDownAction = "scroll-down"; // NOI18N
/** Prefix of all macro-based actions */
public static final String macroActionPrefix = "macro-"; // NOI18N
/** Start recording of macro. Only one macro recording can be active at the time */
public static final String startMacroRecordingAction = "start-macro-recording"; //NOI18N
/** Stop the active recording */
public static final String stopMacroRecordingAction = "stop-macro-recording"; //NOI18N
/** Name of the action moving caret to the first column on the line */
public static final String lineFirstColumnAction = "caret-line-first-column"; // NOI18N
/** Insert the current Date and Time */
public static final String insertDateTimeAction = "insert-date-time"; // NOI18N
/** Name of the action moving caret to the first
* column on the line and extending the selection
*/
public static final String selectionLineFirstColumnAction = "selection-line-first-column"; // NOI18N
/** Name of the action for generating of Glyph Gutter popup menu*/
public static final String generateGutterPopupAction = "generate-gutter-popup"; // NOI18N
/** Toggle visibility of line numbers*/
public static final String toggleLineNumbersAction = "toggle-line-numbers"; // NOI18N
/** Paste and reformat code */
public static final String pasteFormatedAction = "paste-formated"; // NOI18N
/** Starts a new line in code */
public static final String startNewLineAction = "start-new-line"; // NOI18N
/** Cut text from caret position to line begining action. */
public static final String cutToLineBeginAction = "cut-to-line-begin"; // NOI18N
/** Cut text from caret position to line end action. */
public static final String cutToLineEndAction = "cut-to-line-end"; // NOI18N
/** Remove all trailing spaces in the document. */
public static final String removeTrailingSpacesAction = "remove-trailing-spaces"; //NOI18N
public static final String DOC_REPLACE_SELECTION_PROPERTY = "doc-replace-selection-property"; //NOI18N
private static final int KIT_CNT_PREALLOC = 7;
static final long serialVersionUID = -8570495408376659348L;
private static final Map<Class, BaseKit> kits = new HashMap<Class, BaseKit>(KIT_CNT_PREALLOC);
private static final Object KEYMAPS_AND_ACTIONS_LOCK = new String("BaseKit.KEYMAPS_AND_ACTIONS_LOCK"); //NOI18N
private static final Map<MimePath, KeybindingsAndPreferencesTracker> keymapTrackers = new WeakHashMap<MimePath, KeybindingsAndPreferencesTracker>(KIT_CNT_PREALLOC);
private static final Map<MimePath, MultiKeymap> kitKeymaps = new WeakHashMap<MimePath, MultiKeymap>(KIT_CNT_PREALLOC);
private static final Map<MimePath, Action[]> kitActions = new WeakHashMap<MimePath, Action[]>(KIT_CNT_PREALLOC);
private static final Map<MimePath, Map<String, Action>> kitActionMaps = new WeakHashMap<MimePath, Map<String, Action>>(KIT_CNT_PREALLOC);
private static CopyAction copyActionDef = new CopyAction();
private static CutAction cutActionDef = new CutAction();
private static PasteAction pasteActionDef = new PasteAction(false);
private static DeleteCharAction deletePrevCharActionDef = new DeleteCharAction(deletePrevCharAction, false);
private static DeleteCharAction deleteNextCharActionDef = new DeleteCharAction(deleteNextCharAction, true);
private static ActionFactory.RemoveSelectionAction removeSelectionActionDef = new ActionFactory.RemoveSelectionAction();
private static final Action insertTabActionDef = new InsertTabAction();
private static final Action removeTabActionDef = new ActionFactory.RemoveTabAction();
private static final Action insertBreakActionDef = new InsertBreakAction();
private static ActionFactory.UndoAction undoActionDef = new ActionFactory.UndoAction();
private static ActionFactory.RedoAction redoActionDef = new ActionFactory.RedoAction();
public static final int MAGIC_POSITION_MAX = Integer.MAX_VALUE - 1;
private final SearchableKit searchableKit;
private boolean keyBindingsUpdaterInited;
/**
* Navigational boundaries for "home" and "end" actions. If defined on the target component,
* home/end will move the caret first to the boundary, and only after that proceeds as usual (to the start/end of line).
* The property must contain {@link PositionRegion} instance
*/
private static final String PROP_NAVIGATE_BOUNDARIES = "NetBeansEditor.navigateBoundaries"; // NOI18N
/**
* Gets an editor kit from its implemetation class.
*
* <p>Please be careful when using this method and make sure that you understand
* how it works and what the deference is from using <code>MimeLookup</code>.
* This method simply creates an instance of <code>BaseKit</code> from
* its implementation class passed in as a parameter. It completely ignores
* the registry of editor kits in <code>MimeLookup</code>, which has severe
* consequences.
*
* <div class="nonnormative">
* <p>The usuall pattern for using editor kits is to start with a mime type
* of a document (ie. file) that you want to edit, then use some registry
* for editor kits to look up the kit for your mime type and finally set the
* kit in a <code>JTextComponent</code>, let it create a <code>Document</code>
* and load it with data. The registry can generally be anything, but in Netbeans
* we use <code>MimeLookup</code> (JDK for example uses
* <code>JEditorPane.createEditorKitForContentType</code>).
*
* <p>The editor kits are registered in <code>MimeLookup</code> for each
* particular mime type and the registry itself does not impose any rules on
* the editor kit implementations other than extending the <code>EditorKit</code>
* class. This for example means that the same implemantation of <code>EditorKit</code>
* can be used for multiple mime types. This is exactly how XML editor kit
* is reused for various flavors of XML documents (e.g. ant build scripts,
* web app descriptors, etc).
*
* <p>Netbeans did not always have <code>MimeLookup</code>
* and it also used a different approach for registering and retrieving
* editor kits. This old approach was based on implemetation classes rather than on mime
* types and while it is still more or less functional for the old kit
* implementations, it is fundamentally broken and should not be used any more.
* The code below demonstrates probably the biggest mistake when thinking
* in the old ways.
*
* <pre>
* // WARNING: The code below is a demonstration of a common mistake that
* // people do when using <code>BaseKit.getKit</code>.
*
* JTextComponent component = ...; // Let's say we have a component
* Class kitClass = Utilities.getKitClass(component);
* String mimeType = BaseKit.getKit(kitClass).getContentType();
* </pre>
*
* <p>The problem with the above code is that it blindely assumes that each
* kit class can be uniquely mapped to a mime type. This is not true! The
* same can be achieved in much easier way, which always works.
*
* <pre>
* JTextComponent component = ...; // Let's say we have a component
* String mimeType = component.getUI().getEditorKit(component).getContentType();
* </pre>
* </div>
*
* @param kitClass An implementation class of the editor kit that should
* be returned. If the <code>kitClass</code> is not <code>BaseKit</code> or
* its subclass the instance of bare <code>BaseKit</code> will be returned.
*
* @return An instance of the <code>kitClass</code> or <code>BaseKit</code>.
* @deprecated Use <code>CloneableEditorSupport.getEditorKit</code> or
* <code>MimeLookup</code> instead to find <code>EditorKit</code> for a mime
* type.
*/
@Deprecated
public static BaseKit getKit(Class kitClass) {
if (kitClass != null && BaseKit.class.isAssignableFrom(kitClass) && BaseKit.class != kitClass) {
String mimeType = KitsTracker.getInstance().findMimeType(kitClass);
if (mimeType != null) {
EditorKit kit = MimeLookup.getLookup(MimePath.parse(mimeType)).lookup(EditorKit.class);
if (kit instanceof BaseKit) {
return (BaseKit) kit;
}
}
} else {
kitClass = BaseKit.class;
}
synchronized (kits) {
Class classToTry = kitClass;
for(;;) {
BaseKit kit = (BaseKit)kits.get(classToTry);
if (kit == null) {
try {
kit = (BaseKit)classToTry.getDeclaredConstructor().newInstance();
kits.put(classToTry, kit);
return kit;
} catch (ReflectiveOperationException e) {
LOG.log(Level.WARNING, "Can't instantiate editor kit from: " + classToTry, e); //NOI18N
}
//NOI18N
if (classToTry != BaseKit.class) {
classToTry = BaseKit.class;
} else {
throw new IllegalStateException("Can't create editor kit for: " + kitClass); //NOI18N
}
} else {
return kit;
}
}
}
}
/**
* Creates a new instance of <code>BaseKit</code>.
*
* <div class="nonnormative">
* <p>You should not need to instantiate editor kits
* directly under normal circumstances. There is a few ways how you can get
* instance of <code>EditorKit</code> depending on what you already have
* available:
*
* <ul>
* <li><b>mime type</b> - Use <code>CloneableEditorSupport.getEditorKit(yourMimeType)</code>
* to get the <code>EditorKit</code> registered for your mime type or use
* the following code <code>MimeLookup.getLookup(MimePath.parse(yourMimeType)).lookup(EditorKit.class)</code>
* and check for <code>null</code>.
* <li><b>JTextComponent</b> - Simply call
* <code>JTextComponent.getUI().getEditorKit(JTextComponent)</code> passing
* in the same component.
* </ul>
* </div>
*/
public BaseKit() {
// possibly register
synchronized (kits) {
if (kits.get(this.getClass()) == null) {
kits.put(this.getClass(), this); // register itself
}
}
// Directly implementing searchable editor kit would require module dependency changes
// of any modules using BaseKit reference so make a wrapper instead
org.netbeans.modules.editor.lib2.actions.EditorActionUtilities.registerSearchableKit(this,
searchableKit = new SearchableKit(this));
}
/** Clone this editor kit */
public @Override Object clone() {
return this; // no need to create another instance
}
/** Fetches a factory that is suitable for producing
* views of any models that are produced by this
* kit. The default is to have the UI produce the
* factory, so this method has no implementation.
*
* @return the view factory
*/
public @Override ViewFactory getViewFactory() {
return org.netbeans.modules.editor.lib2.view.ViewFactoryImpl.INSTANCE;
}
/** Create caret to navigate through document */
public @Override Caret createCaret() {
return new EditorCaret();
}
/** Create empty document */
public @Override Document createDefaultDocument() {
return new BaseDocument(this.getClass(), true);
}
/**
* Create new instance of syntax coloring scanner
* @param doc document to operate on. It can be null in the cases the syntax
* creation is not related to the particular document
*
* @deprecated Please use Lexer instead, for details see
* <a href="@org-netbeans-modules-lexer@/index.html">Lexer</a>.
*/
@Deprecated
public Syntax createSyntax(Document doc) {
return new DefaultSyntax();
}
/**
* Create the syntax used for formatting.
*
* @deprecated Please use Editor Indentation API instead, for details see
* <a href="@org-netbeans-modules-editor-indent@/index.html">Editor Indentation</a>.
*/
@Deprecated
public Syntax createFormatSyntax(Document doc) {
return createSyntax(doc);
}
/**
* Create syntax support
*
* @deprecated Please use Lexer instead, for details see
* <a href="@org-netbeans-modules-lexer@/index.html">Lexer</a>.
*/
@Deprecated
public SyntaxSupport createSyntaxSupport(BaseDocument doc) {
return new SyntaxSupport(doc);
}
// XXX: formatting cleanup
// /**
// * Create the formatter appropriate for this kit
// * @deprecated Please use Editor Indentation API instead, for details see
// * <a href="@org-netbeans-modules-editor-indent@/index.html">Editor Indentation</a>.
// */
// public Formatter createFormatter() {
// return new Formatter(this.getClass());
// }
/** Create text UI */
protected BaseTextUI createTextUI() {
return new BaseTextUI();
}
/** Create extended UI */
protected EditorUI createEditorUI() {
return new EditorUI();
}
/**
* Create extended UI for printing a document.
* @deprecated this method is no longer being called by {@link EditorUI}.
* {@link #createPrintEditorUI(BaseDocument, boolean, boolean)} is being
* called instead.
*/
@Deprecated
protected EditorUI createPrintEditorUI(BaseDocument doc) {
return new EditorUI(doc);
}
/**
* Create extended UI for printing a document.
*
* @param doc document for which the extended UI is being created.
* @param usePrintColoringMap use printing coloring settings instead
* of the regular ones.
* @param lineNumberEnabled if set to false the line numbers will not be printed.
* If set to true the visibility of line numbers depends on the settings
* for the line number visibility.
*/
protected EditorUI createPrintEditorUI(BaseDocument doc,
boolean usePrintColoringMap, boolean lineNumberEnabled) {
return new EditorUI(doc, usePrintColoringMap, lineNumberEnabled);
}
public MultiKeymap getKeymap() {
synchronized (KEYMAPS_AND_ACTIONS_LOCK) {
MimePath mimePath = MimePath.parse(getContentType());
MultiKeymap km = (MultiKeymap)kitKeymaps.get(mimePath);
if (km == null) { // keymap not yet constructed
// construct new keymap
km = new MultiKeymap("Keymap for " + mimePath.getPath()); // NOI18N
// retrieve key bindings for this kit and super kits
KeyBindingSettings kbs = MimeLookup.getLookup(mimePath).lookup(KeyBindingSettings.class);
List<org.netbeans.api.editor.settings.MultiKeyBinding> mkbList = kbs.getKeyBindings();
List<JTextComponent.KeyBinding> editorMkbList = new ArrayList<JTextComponent.KeyBinding>();
for(org.netbeans.api.editor.settings.MultiKeyBinding mkb : mkbList) {
List<KeyStroke> keyStrokes = mkb.getKeyStrokeList();
MultiKeyBinding editorMkb = new MultiKeyBinding(keyStrokes.toArray(new KeyStroke[0]), mkb.getActionName());
editorMkbList.add(editorMkb);
}
// go through all levels and collect key bindings
km.load(editorMkbList.toArray(new JTextComponent.KeyBinding[0]), getActionMap());
km.setDefaultAction(getActionMap().get(defaultKeyTypedAction));
kitKeymaps.put(mimePath, km);
}
return km;
}
}
/** Inserts content from the given stream. */
public @Override void read(Reader in, Document doc, int pos) throws IOException, BadLocationException {
if (doc instanceof BaseDocument) {
((BaseDocument)doc).read(in, pos); // delegate it to document
} else {
super.read(in, doc, pos);
}
}
/** Writes content from a document to the given stream */
public @Override void write(Writer out, Document doc, int pos, int len) throws IOException, BadLocationException {
if (doc instanceof BaseDocument) {
((BaseDocument)doc).write(out, pos, len);
} else {
super.write(out, doc, pos, len);
}
}
/** Creates map with [name, action] pairs from the given
* array of actions.
*/
public static void addActionsToMap(Map<String, Action> map, Action[] actions, String logActionsType) {
boolean fineLoggable = LOG.isLoggable(Level.FINE);
if (fineLoggable) {
LOG.fine(logActionsType + " start --------------------\n");
}
for (int i = 0; i < actions.length; i++) {
Action a = actions[i];
if (a == null) {
LOG.info("actions[] contains null at index " + i +
((i > 0) ? ". Preceding action is " + actions[i - 1] : "."));
continue;
}
String name = (String) a.getValue(Action.NAME);
if (name == null) {
LOG.info("Null Action.NAME property of action " + a);
continue;
}
if (fineLoggable) {
String overriding = map.containsKey(name) ? " OVERRIDING\n" : "\n"; // NOI18N
LOG.fine(" " + name + ": " + a + overriding); // NOI18N
}
map.put(name, a); // NOI18N
}
if (fineLoggable) {
LOG.fine(logActionsType + " end ----------------------\n");
}
}
/** Converts map with [name, action] back
* to array of actions.
*/
public static Action[] mapToActions(Map map) {
Action[] actions = new Action[map.size()];
int i = 0;
for (Iterator iter = map.values().iterator() ; iter.hasNext() ;) {
actions[i++] = (Action)iter.next();
}
return actions;
}
/** Called after the kit is installed into JEditorPane */
public @Override void install(JEditorPane c) {
assert (SwingUtilities.isEventDispatchThread()) // expected in AWT only
: "BaseKit.install() incorrectly called from non-AWT thread."; // NOI18N
BaseTextUI ui = createTextUI();
c.setUI(ui);
c.addPropertyChangeListener(ClearUIForNullKitListener.INSTANCE);
String propName = "netbeans.editor.noinputmethods"; // NOI18N
Object noInputMethods = System.getProperty(propName);
boolean enableIM;
if (noInputMethods != null) {
enableIM = !Boolean.getBoolean(propName);
} else {
Preferences prefs = MimeLookup.getLookup(getContentType()).lookup(Preferences.class);
enableIM = prefs.getBoolean(EditorPreferencesKeys.INPUT_METHODS_ENABLED, EditorPreferencesDefaults.defaultInputMethodsEnabled); //NOI18N
}
c.enableInputMethods(enableIM);
org.netbeans.lib.editor.hyperlink.HyperlinkOperation.ensureRegistered(c, getContentType());
// Mark that the editor's multi keymap adheres to context API in status displayer
c.putClientProperty("context-api-aware", Boolean.TRUE); // NOI18N
// Add default help IDs derived from the kit's mime type, #61618.
// If the kit itself is HelpCtx.Provider it will be called from CloneableEditor.getHelpCtx()
if (!(this instanceof HelpCtx.Provider)) {
HelpCtx.setHelpIDString(c, getContentType().replace('/', '.').replace('+', '.')); //NOI18N
}
// setup the keymap tracker and initialize the keymap
MultiKeymap keymap;
synchronized (KEYMAPS_AND_ACTIONS_LOCK) {
MimePath mimePath = MimePath.get(getContentType());
KeybindingsAndPreferencesTracker tracker = keymapTrackers.get(mimePath);
if (tracker == null) {
tracker = new KeybindingsAndPreferencesTracker(mimePath.getPath());
keymapTrackers.put(mimePath, tracker);
}
tracker.addComponent(c);
keymap = getKeymap();
}
c.setKeymap(keymap);
c.addAncestorListener(new AncestorListener() {
private JScrollPane scrollPane;
private InputMap origMap;
int condition = JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT;
@Override
public void ancestorAdded(AncestorEvent event) {
Component c = (Component) event.getSource();
Component parent;
if ((parent = c.getParent()) instanceof JViewport) {
c = parent;
if ((parent = c.getParent()) instanceof JScrollPane) {
scrollPane = (JScrollPane) parent;
origMap = scrollPane.getInputMap(condition);
scrollPane.setInputMap(condition, null);
}
}
}
@Override
public void ancestorRemoved(AncestorEvent event) {
if (scrollPane != null && scrollPane.getInputMap(condition) == null) {
// Restore original input map
scrollPane.setInputMap(condition, origMap);
}
}
@Override
public void ancestorMoved(AncestorEvent event) {
}
});
executeInstallActions(c);
}
protected void executeInstallActions(JEditorPane c) {
MimePath mimePath = MimePath.parse(getContentType());
Preferences prefs = MimeLookup.getLookup(mimePath).lookup(Preferences.class);
@SuppressWarnings("unchecked")
List<String> actionNamesList = (List<String>) SettingsConversions.callFactory(
prefs, mimePath, EditorPreferencesKeys.KIT_INSTALL_ACTION_NAME_LIST, null); //NOI18N
List<Action> actionsList = translateActionNameList(actionNamesList); // translate names to actions
for (Action a : actionsList) {
a.actionPerformed(new ActionEvent(c, ActionEvent.ACTION_PERFORMED, "")); // NOI18N
}
}
public @Override void deinstall(JEditorPane c) {
assert (SwingUtilities.isEventDispatchThread()) // expected in AWT only
: "BaseKit.deinstall() incorrectly called from non-AWT thread."; // NOI18N
executeDeinstallActions(c);
// reset the keymap and remove the component from the tracker
c.setKeymap(null);
synchronized (KEYMAPS_AND_ACTIONS_LOCK) {
MimePath mimePath = MimePath.get(getContentType());
KeybindingsAndPreferencesTracker tracker = keymapTrackers.get(mimePath);
if (tracker != null) {
tracker.removeComponent(c);
}
}
BaseTextUI.uninstallUIWatcher(c);
// #41209: reset ancestor override flag if previously set
if (c.getClientProperty("ancestorOverride") != null) { // NOI18N
c.putClientProperty("ancestorOverride", Boolean.FALSE); // NOI18N
}
}
protected void executeDeinstallActions(JEditorPane c) {
MimePath mimePath = MimePath.parse(getContentType());
Preferences prefs = MimeLookup.getLookup(mimePath).lookup(Preferences.class);
@SuppressWarnings("unchecked")
List<String> actionNamesList = (List<String>) SettingsConversions.callFactory(
prefs, mimePath, EditorPreferencesKeys.KIT_DEINSTALL_ACTION_NAME_LIST, null); //NOI18N
List<Action> actionsList = translateActionNameList(actionNamesList); // translate names to actions
for (Action a : actionsList) {
a.actionPerformed(new ActionEvent(c, ActionEvent.ACTION_PERFORMED, "")); // NOI18N
}
}
/** Initialize document by adding the draw-layers for example. */
protected void initDocument(BaseDocument doc) {
}
/** Create actions that this kit supports. To use the actions of the parent kit
* it's better instead of using super.createActions() to use
* getKit(super.getClass()).getActions() because it can reuse existing
* parent actions.
*/
protected Action[] createActions() {
return new Action[] {
// new DefaultKeyTypedAction() - overriden in ExtKit
insertBreakActionDef,
insertTabActionDef,
deletePrevCharActionDef,
deleteNextCharActionDef,
cutActionDef,
copyActionDef,
pasteActionDef,
new PasteAction(true),
removeTabActionDef,
//new ActionFactory.RemoveWordAction(), #47709
removeSelectionActionDef,
undoActionDef,
redoActionDef,
//new ActionFactory.ToggleLineNumbersAction(),
new ActionFactory.ToggleRectangularSelectionAction(),
// Self test actions
// new EditorDebug.SelfTestAction(),
// new EditorDebug.DumpPlanesAction(),
// new EditorDebug.DumpSyntaxMarksAction()
};
}
protected Action[] getCustomActions() {
MimePath mimePath = MimePath.parse(getContentType());
Preferences prefs = MimeLookup.getLookup(mimePath).lookup(Preferences.class);
@SuppressWarnings("unchecked")
List<? extends Action> customActions = (List<? extends Action>) SettingsConversions.callFactory(
prefs, mimePath, EditorPreferencesKeys.CUSTOM_ACTION_LIST, null); //NOI18N
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(EditorPreferencesKeys.CUSTOM_ACTION_LIST + " for '" + getContentType() + "' {"); //NOI18N
if (customActions != null) {
for(Action a : customActions) {
LOG.fine(" " + a); //NOI18N
}
}
LOG.fine("} End of " + EditorPreferencesKeys.CUSTOM_ACTION_LIST + " for '" + getContentType() + "'"); //NOI18N
}
return customActions == null ? null : customActions.toArray(new Action[0]);
}
/**
* @deprecated Without any replacement.
*/
@Deprecated
protected Action[] getMacroActions() {
return new Action[0];
}
/** Get actions associated with this kit. createActions() is called
* to get basic list and then customActions are added.
*/
public @Override final Action[] getActions() {
Action[] actions = (Action []) addActionsToMap()[0];
if (!keyBindingsUpdaterInited) {
keyBindingsUpdaterInited = true;
KeyBindingsUpdater.get(getContentType()).addKit(this); // Update key bindings in actions
}
return actions;
}
/* package */ Map<String, Action> getActionMap() {
return (Map<String, Action>) addActionsToMap()[1];
}
private Object[] addActionsToMap() {
synchronized (KEYMAPS_AND_ACTIONS_LOCK) {
MimePath mimePath = MimePath.parse(getContentType());
Action[] actions = kitActions.get(mimePath);
Map<String, Action> actionMap = kitActionMaps.get(mimePath);
if (actions == null || actionMap == null) {
// Initialize actions - use the following actions:
// 1. Declared "global" actions (declared in the xml layer under "Editors/Actions")
// 2. Declared "mime-type" actions (declared in the xml layer under "Editors/mime-type/Actions")
// 3. Result of createActions()
// 4. Custom actions (EditorPreferencesKeys.CUSTOM_ACTION_LIST)
// Higher levels override actions with same Action.NAME
actions = getDeclaredActions(); // non-null
actionMap = new HashMap<String, Action>(actions.length << 1);
addActionsToMap(actionMap, actions, "Declared actions"); // NOI18N
Action[] createActionsMethodResult = createActions();
if (createActionsMethodResult != null) {
addActionsToMap(actionMap, createActionsMethodResult, "Actions from createActions()"); // NOI18N
}
// add custom actions
Action[] customActions = getCustomActions();
if (customActions != null) {
addActionsToMap(actionMap, customActions, "Custom actions"); // NOI18N
}
actions = actionMap.values().toArray(new Action[actionMap.values().size()]);
kitActions.put(mimePath, actions);
kitActionMaps.put(mimePath, actionMap);
// At this moment the actions are constructed completely
// The actions will be updated now if necessary
updateActions();
}
return new Object [] { actions, actionMap };
}
}
/**
* Get actions declared in the xml layer. They may be overriden by result
* of <code>createActions()</code> and finally by result of <code>getCustomActions()</code>.
*
* @return non-null list of declared actions.
*/
protected Action[] getDeclaredActions() {
return new Action[0];
}
/** Update the actions right after their creation was finished.
* The <code>getActions()</code> and <code>getActionByName()</code>
* can be used safely in this method.
* The implementation must call <code>super.updateActions()</code> so that
* the updating in parent is performed too.
*/
protected void updateActions() {
}