Lines 54-59
Link Here
|
54 |
import java.awt.Graphics2D; |
54 |
import java.awt.Graphics2D; |
55 |
import java.awt.Point; |
55 |
import java.awt.Point; |
56 |
import java.awt.Rectangle; |
56 |
import java.awt.Rectangle; |
|
|
57 |
import java.awt.Shape; |
57 |
import java.awt.Stroke; |
58 |
import java.awt.Stroke; |
58 |
import java.awt.datatransfer.Clipboard; |
59 |
import java.awt.datatransfer.Clipboard; |
59 |
import java.awt.datatransfer.DataFlavor; |
60 |
import java.awt.datatransfer.DataFlavor; |
Lines 128-134
Link Here
|
128 |
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils; |
129 |
import org.netbeans.modules.editor.lib2.RectangularSelectionUtils; |
129 |
import org.netbeans.modules.editor.lib2.actions.EditorActionUtilities; |
130 |
import org.netbeans.modules.editor.lib2.actions.EditorActionUtilities; |
130 |
import org.netbeans.modules.editor.lib2.highlighting.CaretOverwriteModeHighlighting; |
131 |
import org.netbeans.modules.editor.lib2.highlighting.CaretOverwriteModeHighlighting; |
131 |
import org.netbeans.modules.editor.lib2.view.DocumentView; |
|
|
132 |
import org.netbeans.modules.editor.lib2.view.LockedViewHierarchy; |
132 |
import org.netbeans.modules.editor.lib2.view.LockedViewHierarchy; |
133 |
import org.netbeans.modules.editor.lib2.view.ViewHierarchy; |
133 |
import org.netbeans.modules.editor.lib2.view.ViewHierarchy; |
134 |
import org.netbeans.modules.editor.lib2.view.ViewHierarchyEvent; |
134 |
import org.netbeans.modules.editor.lib2.view.ViewHierarchyEvent; |
Lines 164-169
Link Here
|
164 |
// -J-Dorg.netbeans.editor.BaseCaret.level=FINEST |
164 |
// -J-Dorg.netbeans.editor.BaseCaret.level=FINEST |
165 |
private static final Logger LOG = Logger.getLogger(EditorCaret.class.getName()); |
165 |
private static final Logger LOG = Logger.getLogger(EditorCaret.class.getName()); |
166 |
|
166 |
|
|
|
167 |
static final long serialVersionUID = 0L; |
168 |
|
167 |
static { |
169 |
static { |
168 |
RectangularSelectionCaretAccessor.register(new RectangularSelectionCaretAccessor() { |
170 |
RectangularSelectionCaretAccessor.register(new RectangularSelectionCaretAccessor() { |
169 |
@Override |
171 |
@Override |
Lines 183-190
Link Here
|
183 |
}); |
185 |
}); |
184 |
} |
186 |
} |
185 |
|
187 |
|
186 |
static final long serialVersionUID = 0L; |
|
|
187 |
|
188 |
/** |
188 |
/** |
189 |
* Non-empty list of individual carets in the order they were created. |
189 |
* Non-empty list of individual carets in the order they were created. |
190 |
* At least one item is always present. |
190 |
* At least one item is always present. |
Lines 236-245
Link Here
|
236 |
/** |
236 |
/** |
237 |
* Whether blinking caret is currently visible on the screen. |
237 |
* Whether blinking caret is currently visible on the screen. |
238 |
* <br> |
238 |
* <br> |
239 |
* This changes from true to false after each tick of a timer |
239 |
* This changes from true to false and back as caret(s) blink. |
240 |
* (assuming <code>visible == true</code>). |
240 |
* <br> |
|
|
241 |
* If false then original locations do not need to be repainted. |
241 |
*/ |
242 |
*/ |
242 |
private boolean blinkVisible; |
243 |
private boolean showing; |
243 |
|
244 |
|
244 |
/** |
245 |
/** |
245 |
* Determine if a possible selection would be displayed or not. |
246 |
* Determine if a possible selection would be displayed or not. |
Lines 254-261
Link Here
|
254 |
|
255 |
|
255 |
private MouseState mouseState = MouseState.DEFAULT; |
256 |
private MouseState mouseState = MouseState.DEFAULT; |
256 |
|
257 |
|
257 |
/** Timer used for blinking the caret */ |
258 |
/** |
258 |
private Timer flasher; |
259 |
* Default delay for caret blinking if the system is responsive enough. |
|
|
260 |
* Zero if caret blinking is disabled. |
261 |
*/ |
262 |
private int blinkDefaultDelay; |
263 |
|
264 |
/** |
265 |
* Currently used delay according to system responsiveness. |
266 |
*/ |
267 |
private int blinkCurrentDelay; |
268 |
|
269 |
/** |
270 |
* Last time when blink timer action was invoked or zero if the blinking delay |
271 |
* should not be examined upon next timer action invocation. |
272 |
* If there are too many carets being painted and the system becomes busy |
273 |
* the blinkCurrentDelay may be increased to lower the blinking frequency |
274 |
* and allow the system to be responsive. |
275 |
*/ |
276 |
private long lastBlinkTime; |
277 |
|
278 |
/** |
279 |
* Carets blinking timer. |
280 |
*/ |
281 |
private Timer blinkTimer; |
282 |
|
283 |
private ActionListener weakTimerListener; |
259 |
|
284 |
|
260 |
private Action selectWordAction; |
285 |
private Action selectWordAction; |
261 |
private Action selectLineAction; |
286 |
private Action selectLineAction; |
Lines 269-286
Link Here
|
269 |
private CaretTransaction activeTransaction; |
294 |
private CaretTransaction activeTransaction; |
270 |
|
295 |
|
271 |
/** |
296 |
/** |
272 |
* Items from previous transaction(s) that need their visual rectangle |
|
|
273 |
* to get repainted to clear the previous caret representation visually. |
274 |
*/ |
275 |
private GapList<CaretItem> pendingRepaintRemovedItemsList; |
276 |
|
277 |
/** |
278 |
* Items from previous transaction(s) that need their visual bounds |
279 |
* to be recomputed and caret to be repainted then. |
280 |
*/ |
281 |
private GapList<CaretItem> pendingUpdateVisualBoundsItemsList; |
282 |
|
283 |
/** |
284 |
* Caret item to which the view should scroll or null for no scrolling. |
297 |
* Caret item to which the view should scroll or null for no scrolling. |
285 |
*/ |
298 |
*/ |
286 |
private CaretItem scrollToItem; |
299 |
private CaretItem scrollToItem; |
Lines 381-386
Link Here
|
381 |
* If there is a selection, the mark will not be the same as the dot. |
394 |
* If there is a selection, the mark will not be the same as the dot. |
382 |
* @return mark offset >=0 |
395 |
* @return mark offset >=0 |
383 |
*/ |
396 |
*/ |
|
|
397 |
|
384 |
@Override |
398 |
@Override |
385 |
public int getMark() { |
399 |
public int getMark() { |
386 |
return getLastCaret().getMark(); |
400 |
return getLastCaret().getMark(); |
Lines 606-612
Link Here
|
606 |
* or no document installed in the text component. |
620 |
* or no document installed in the text component. |
607 |
* <br> |
621 |
* <br> |
608 |
* Note that adding a new caret to offset where another caret is already located may lead |
622 |
* Note that adding a new caret to offset where another caret is already located may lead |
609 |
* to its immediate removal. |
623 |
* or no document installed in the text component. |
610 |
*/ |
624 |
*/ |
611 |
public int addCaret(@NonNull Position dotPos, @NonNull Position markPos) { |
625 |
public int addCaret(@NonNull Position dotPos, @NonNull Position markPos) { |
612 |
return runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0, |
626 |
return runTransaction(CaretTransaction.RemoveType.NO_REMOVE, 0, |
Lines 749-757
Link Here
|
749 |
} |
763 |
} |
750 |
} |
764 |
} |
751 |
synchronized (listenerList) { |
765 |
synchronized (listenerList) { |
752 |
if (flasher != null) { |
766 |
if (blinkTimer != null) { |
753 |
if (this.visible) { |
767 |
if (this.visible) { |
754 |
flasher.stop(); |
768 |
blinkTimer.stop(); |
755 |
} |
769 |
} |
756 |
if (LOG.isLoggable(Level.FINER)) { |
770 |
if (LOG.isLoggable(Level.FINER)) { |
757 |
LOG.finer((visible ? "Starting" : "Stopping") + // NOI18N |
771 |
LOG.finer((visible ? "Starting" : "Stopping") + // NOI18N |
Lines 759-767
Link Here
|
759 |
} |
773 |
} |
760 |
this.visible = visible; |
774 |
this.visible = visible; |
761 |
if (visible) { |
775 |
if (visible) { |
762 |
flasher.start(); |
776 |
blinkTimer.start(); |
763 |
} else { |
777 |
} else { |
764 |
flasher.stop(); |
778 |
blinkTimer.stop(); |
765 |
} |
779 |
} |
766 |
} |
780 |
} |
767 |
} |
781 |
} |
Lines 811-821
Link Here
|
811 |
Boolean b = (Boolean) c.getClientProperty(EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY); |
825 |
Boolean b = (Boolean) c.getClientProperty(EditorUtilities.CARET_OVERWRITE_MODE_PROPERTY); |
812 |
overwriteMode = (b != null) ? b : false; |
826 |
overwriteMode = (b != null) ? b : false; |
813 |
updateOverwriteModeLayer(true); |
827 |
updateOverwriteModeLayer(true); |
814 |
setBlinkVisible(true); |
828 |
setShowing(true); |
815 |
|
829 |
|
816 |
// Attempt to assign initial bounds - usually here the component |
830 |
// Attempt to assign initial bounds - usually here the component |
817 |
// is not yet added to the component hierarchy. |
831 |
// is not yet added to the component hierarchy. |
818 |
updateAllCaretsBounds(); |
832 |
requestUpdateAllCaretsBounds(); |
819 |
|
833 |
|
820 |
if(getLastCaretItem().getCaretBounds() == null) { |
834 |
if(getLastCaretItem().getCaretBounds() == null) { |
821 |
// For null bounds wait for the component to get resized |
835 |
// For null bounds wait for the component to get resized |
Lines 848-854
Link Here
|
848 |
} |
862 |
} |
849 |
|
863 |
|
850 |
synchronized (listenerList) { |
864 |
synchronized (listenerList) { |
851 |
if (flasher != null) { |
865 |
if (blinkTimer != null) { |
852 |
setBlinkRate(0); |
866 |
setBlinkRate(0); |
853 |
} |
867 |
} |
854 |
} |
868 |
} |
Lines 867-897
Link Here
|
867 |
@Override |
881 |
@Override |
868 |
public void paint(Graphics g) { |
882 |
public void paint(Graphics g) { |
869 |
JTextComponent c = component; |
883 |
JTextComponent c = component; |
870 |
if (c == null) return; |
884 |
if (c == null || !isShowing()) { |
871 |
|
885 |
return; |
872 |
// Check whether the caret was moved but the component was not |
|
|
873 |
// validated yet and therefore the caret bounds are still null |
874 |
// and if so compute the bounds and scroll the view if necessary. |
875 |
// TODO - could this be done by an extra flag rather than bounds checking?? |
876 |
CaretItem lastCaret = getLastCaretItem(); |
877 |
if (getDot() != 0 && lastCaret.getCaretBounds() == null) { |
878 |
update(true); |
879 |
} |
886 |
} |
880 |
|
887 |
|
|
|
888 |
// Only paint carets that are part of the clip - use sorted carets |
889 |
Rectangle clipBounds = g.getClipBounds(); |
881 |
List<CaretInfo> carets = getSortedCarets(); |
890 |
List<CaretInfo> carets = getSortedCarets(); |
882 |
for (CaretInfo caretInfo : carets) { // TODO only paint the items in the clipped area - use binary search to located first item |
891 |
int low = 0; |
883 |
CaretItem caretItem = caretInfo.getCaretItem(); |
892 |
int caretsSize = carets.size(); |
884 |
if (LOG.isLoggable(Level.FINEST)) { |
893 |
int high = caretsSize - 1; |
885 |
LOG.finest("BaseCaret.paint(): caretBounds=" + caretItem.getCaretBounds() + dumpVisibility() + '\n'); |
894 |
while (low <= high) { |
|
|
895 |
int mid = (low + high) >>> 1; |
896 |
CaretInfo caretInfo = carets.get(mid); |
897 |
Rectangle midBounds = caretInfo.getCaretItem().getCaretBounds(); |
898 |
|
899 |
if (midBounds.y + midBounds.height <= clipBounds.y) { |
900 |
low = mid + 1; |
901 |
} else { // No exact match (may be multiple carets on sinle line) |
902 |
high = mid - 1; |
886 |
} |
903 |
} |
887 |
if (caretItem.getCaretBounds() != null && isVisible() && blinkVisible) { |
904 |
} |
888 |
paintCaret(g, caretItem); |
905 |
|
|
|
906 |
Color origColor = g.getColor(); |
907 |
try { |
908 |
g.setColor(c.getCaretColor()); |
909 |
// Use "low" index (which is higher than "high" at end of binary-search) |
910 |
for (int i = low; i < caretsSize; i++) { |
911 |
CaretInfo caretInfo = carets.get(i); |
912 |
CaretItem caretItem = caretInfo.getCaretItem(); |
913 |
if (LOG.isLoggable(Level.FINEST)) { |
914 |
LOG.finest("BaseCaret.paint(): caretBounds=" + caretItem.getCaretBounds() + dumpVisibility() + '\n'); |
915 |
} |
916 |
if (caretItem.getCaretBounds() != null) { |
917 |
Rectangle caretBounds = caretItem.getCaretBounds(); |
918 |
switch (type) { |
919 |
case THICK_LINE_CARET: |
920 |
g.fillRect(caretBounds.x, caretBounds.y, this.thickCaretWidth, caretBounds.height - 1); |
921 |
break; |
922 |
|
923 |
case THIN_LINE_CARET: |
924 |
int upperX = caretItem.getCaretBounds().x; |
925 |
g.drawLine((int) upperX, caretItem.getCaretBounds().y, caretItem.getCaretBounds().x, |
926 |
(caretItem.getCaretBounds().y + caretItem.getCaretBounds().height - 1)); |
927 |
break; |
928 |
|
929 |
case BLOCK_CARET: |
930 |
// Use a CaretOverwriteModeHighlighting layer to paint the caret |
931 |
break; |
932 |
|
933 |
default: |
934 |
throw new IllegalStateException("Invalid caret type=" + type); |
935 |
} |
936 |
} |
889 |
} |
937 |
} |
|
|
938 |
|
939 |
// Possibly service rectangular selection |
890 |
if (rectangularSelection && rsPaintRect != null && g instanceof Graphics2D) { |
940 |
if (rectangularSelection && rsPaintRect != null && g instanceof Graphics2D) { |
891 |
Graphics2D g2d = (Graphics2D) g; |
941 |
Graphics2D g2d = (Graphics2D) g; |
892 |
Stroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] {4, 2}, 0); |
942 |
Stroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] {4, 2}, 0); |
893 |
Stroke origStroke = g2d.getStroke(); |
943 |
Stroke origStroke = g2d.getStroke(); |
894 |
Color origColor = g2d.getColor(); |
|
|
895 |
try { |
944 |
try { |
896 |
// Render translucent rectangle |
945 |
// Render translucent rectangle |
897 |
Color selColor = c.getSelectionColor(); |
946 |
Color selColor = c.getSelectionColor(); |
Lines 913-921
Link Here
|
913 |
|
962 |
|
914 |
} finally { |
963 |
} finally { |
915 |
g2d.setStroke(origStroke); |
964 |
g2d.setStroke(origStroke); |
916 |
g2d.setColor(origColor); |
|
|
917 |
} |
965 |
} |
918 |
} |
966 |
} |
|
|
967 |
} finally { |
968 |
g.setColor(origColor); |
919 |
} |
969 |
} |
920 |
} |
970 |
} |
921 |
|
971 |
|
Lines 932-952
Link Here
|
932 |
LOG.finer("setBlinkRate(" + rate + ")" + dumpVisibility() + '\n'); // NOI18N |
982 |
LOG.finer("setBlinkRate(" + rate + ")" + dumpVisibility() + '\n'); // NOI18N |
933 |
} |
983 |
} |
934 |
synchronized (listenerList) { |
984 |
synchronized (listenerList) { |
935 |
if (flasher == null && rate > 0) { |
985 |
this.blinkDefaultDelay = rate; |
936 |
flasher = new Timer(rate, listenerImpl); |
986 |
this.blinkCurrentDelay = rate; |
|
|
987 |
if (blinkTimer == null && rate > 0) { |
988 |
blinkTimer = new Timer(rate, null); |
989 |
blinkTimer.addActionListener(weakTimerListener = WeakListeners.create( |
990 |
ActionListener.class, listenerImpl, blinkTimer)); |
937 |
} |
991 |
} |
938 |
if (flasher != null) { |
992 |
if (blinkTimer != null) { |
939 |
if (rate > 0) { |
993 |
if (rate > 0) { |
940 |
if (flasher.getDelay() != rate) { |
994 |
if (blinkTimer.getDelay() != rate) { |
941 |
flasher.setDelay(rate); |
995 |
blinkTimer.setDelay(rate); |
942 |
} |
996 |
} |
943 |
} else { // zero rate - don't blink |
997 |
} else { // zero rate - don't blink |
944 |
flasher.stop(); |
998 |
blinkTimer.stop(); |
945 |
flasher.removeActionListener(listenerImpl); |
999 |
if (weakTimerListener != null) { |
946 |
flasher = null; |
1000 |
blinkTimer.removeActionListener(weakTimerListener); |
947 |
setBlinkVisible(true); |
1001 |
} |
|
|
1002 |
blinkTimer = null; |
1003 |
setShowing(true); |
948 |
if (LOG.isLoggable(Level.FINER)){ |
1004 |
if (LOG.isLoggable(Level.FINER)){ |
949 |
LOG.finer("Zero blink rate - no blinking. flasher=null; blinkVisible=true"); // NOI18N |
1005 |
LOG.finer("Zero blink rate - no blinking. blinkTimer=null; showing=true"); // NOI18N |
950 |
} |
1006 |
} |
951 |
} |
1007 |
} |
952 |
} |
1008 |
} |
Lines 956-962
Link Here
|
956 |
@Override |
1012 |
@Override |
957 |
public int getBlinkRate() { |
1013 |
public int getBlinkRate() { |
958 |
synchronized (listenerList) { |
1014 |
synchronized (listenerList) { |
959 |
return (flasher != null) ? flasher.getDelay() : 0; |
1015 |
return blinkDefaultDelay; |
960 |
} |
1016 |
} |
961 |
} |
1017 |
} |
962 |
|
1018 |
|
Lines 1122-1153
Link Here
|
1122 |
synchronized (listenerList) { |
1178 |
synchronized (listenerList) { |
1123 |
GapList<CaretItem> replaceItems = activeTransaction.getReplaceItems(); |
1179 |
GapList<CaretItem> replaceItems = activeTransaction.getReplaceItems(); |
1124 |
if (replaceItems != null) { |
1180 |
if (replaceItems != null) { |
|
|
1181 |
caretItems = replaceItems; |
1125 |
diffCount = replaceItems.size() - caretItems.size(); |
1182 |
diffCount = replaceItems.size() - caretItems.size(); |
1126 |
caretItems = replaceItems; |
|
|
1127 |
sortedCaretItems = activeTransaction.getSortedCaretItems(); |
1128 |
for (CaretItem caretItem : caretItems) { |
1183 |
for (CaretItem caretItem : caretItems) { |
1129 |
if (caretItem.isInfoObsolete()) { |
1184 |
if (caretItem.getAndClearInfoObsolete()) { |
1130 |
caretItem.clearInfoObsolete(); |
|
|
1131 |
caretItem.clearInfo(); |
1185 |
caretItem.clearInfo(); |
1132 |
} |
1186 |
} |
1133 |
} |
1187 |
} |
1134 |
assert (sortedCaretItems != null) : "Null sortedCaretItems! removeType=" + removeType; // NOI18N |
1188 |
sortedCaretItems = activeTransaction.getSortedCaretItems(); |
1135 |
} |
1189 |
assert (sortedCaretItems != null) : "Null sortedCaretItems! removeType=" + removeType; // NOI18N |
1136 |
if (activeTransaction.isAnyChange()) { |
|
|
1137 |
caretInfos = null; |
1138 |
sortedCaretInfos = null; |
1139 |
} |
1190 |
} |
1140 |
} |
1191 |
} |
1141 |
pendingRepaintRemovedItemsList = activeTransaction. |
1192 |
if (activeTransaction.isAnyChange()) { |
1142 |
addRemovedItems(pendingRepaintRemovedItemsList); |
1193 |
caretInfos = null; |
1143 |
pendingUpdateVisualBoundsItemsList = activeTransaction. |
1194 |
sortedCaretInfos = null; |
1144 |
addUpdateVisualBoundsItems(pendingUpdateVisualBoundsItemsList); |
1195 |
} |
1145 |
if (pendingUpdateVisualBoundsItemsList != null || pendingRepaintRemovedItemsList != null) { |
1196 |
// Repaint bounds of removed items |
1146 |
// For now clear the lists and use old way TODO update to selective updating and rendering |
1197 |
GapList<CaretItem> removedItems = activeTransaction.allRemovedItems(); |
|
|
1198 |
if (removedItems != null) { |
1199 |
for (CaretItem removedItem : removedItems) { |
1200 |
Rectangle caretBounds = removedItem.getCaretBounds(); |
1201 |
if (caretBounds != null) { |
1202 |
c.repaint(caretBounds); |
1203 |
} |
1204 |
} |
1205 |
} |
1206 |
if (activeTransaction.isAnyChange()) { |
1147 |
fireStateChanged(); |
1207 |
fireStateChanged(); |
1148 |
dispatchUpdate(true); |
1208 |
dispatchUpdate(true); |
1149 |
pendingRepaintRemovedItemsList = null; |
|
|
1150 |
pendingUpdateVisualBoundsItemsList = null; |
1151 |
} |
1209 |
} |
1152 |
return diffCount; |
1210 |
return diffCount; |
1153 |
} finally { |
1211 |
} finally { |
Lines 1174-1211
Link Here
|
1174 |
} |
1232 |
} |
1175 |
} |
1233 |
} |
1176 |
|
1234 |
|
1177 |
private void moveDotCaret(int offset, CaretItem caret) throws IllegalStateException { |
1235 |
private void updateRectangularSelectionDotRect() { // Assumes update caret bounds of getLastCaretItem() |
1178 |
JTextComponent c = component; |
1236 |
if (rectangularSelection) { |
1179 |
AbstractDocument doc; |
1237 |
Rectangle caretBounds = getLastCaretItem().getCaretBounds(); |
1180 |
if (c != null && (doc = activeDoc) != null) { |
1238 |
if (caretBounds != null) { |
1181 |
if (offset >= 0 && offset <= doc.getLength()) { |
1239 |
if (rsDotRect != null) { |
1182 |
doc.readLock(); |
1240 |
rsDotRect.y = caretBounds.y; |
1183 |
try { |
1241 |
rsDotRect.height = caretBounds.height; |
1184 |
int oldCaretPos = caret.getDot(); |
1242 |
} else { |
1185 |
if (offset == oldCaretPos) { // no change |
1243 |
rsDotRect = caretBounds; |
1186 |
return; |
|
|
1187 |
} |
1188 |
caret.setDotPos(doc.createPosition(offset)); |
1189 |
// Selection highlighting should be handled automatically by highlighting layers |
1190 |
if (rectangularSelection) { |
1191 |
Rectangle r = c.modelToView(offset); |
1192 |
if (rsDotRect != null) { |
1193 |
rsDotRect.y = r.y; |
1194 |
rsDotRect.height = r.height; |
1195 |
} else { |
1196 |
rsDotRect = r; |
1197 |
} |
1198 |
updateRectangularSelectionPaintRect(); |
1199 |
} |
1200 |
} catch (BadLocationException e) { |
1201 |
throw new IllegalStateException(e.toString()); |
1202 |
// position is incorrect |
1203 |
} finally { |
1204 |
doc.readUnlock(); |
1205 |
} |
1244 |
} |
1206 |
} |
1245 |
} |
1207 |
fireStateChanged(); |
1246 |
updateRectangularSelectionPaintRect(); |
1208 |
dispatchUpdate(true); |
|
|
1209 |
} |
1247 |
} |
1210 |
} |
1248 |
} |
1211 |
|
1249 |
|
Lines 1235-1241
Link Here
|
1235 |
}; |
1273 |
}; |
1236 |
|
1274 |
|
1237 |
// Always fire in EDT |
1275 |
// Always fire in EDT |
1238 |
if (inAtomicUnlock) { // Cannot fire within atomic lock9 |
1276 |
if (inAtomicUnlock) { // Cannot fire within atomic lock |
1239 |
SwingUtilities.invokeLater(runnable); |
1277 |
SwingUtilities.invokeLater(runnable); |
1240 |
} else { |
1278 |
} else { |
1241 |
ViewUtils.runInEDT(runnable); |
1279 |
ViewUtils.runInEDT(runnable); |
Lines 1333-1411
Link Here
|
1333 |
} |
1371 |
} |
1334 |
|
1372 |
|
1335 |
/** |
1373 |
/** |
1336 |
* Assign new caret bounds into <code>caretBounds</code> variable. |
1374 |
* Schedule recomputation of visual bounds of all carets. |
1337 |
* |
|
|
1338 |
* @return true if the new caret bounds were successfully computed |
1339 |
* and assigned or false otherwise. |
1340 |
*/ |
1375 |
*/ |
1341 |
private boolean updateAllCaretsBounds() { |
1376 |
private void requestUpdateAllCaretsBounds() { |
1342 |
JTextComponent c = component; |
1377 |
JTextComponent c = component; |
1343 |
AbstractDocument doc; |
1378 |
AbstractDocument doc; |
1344 |
boolean ret = false; |
|
|
1345 |
if (c != null && (doc = activeDoc) != null) { |
1379 |
if (c != null && (doc = activeDoc) != null) { |
1346 |
doc.readLock(); |
1380 |
doc.readLock(); |
1347 |
try { |
1381 |
try { |
1348 |
List<CaretInfo> sortedCarets = getSortedCarets(); |
1382 |
List<CaretInfo> sortedCarets = getSortedCarets(); |
1349 |
for (CaretInfo caret : sortedCarets) { |
1383 |
for (CaretInfo caret : sortedCarets) { |
1350 |
ret |= updateRealCaretBounds(caret.getCaretItem(), doc, c); |
1384 |
caret.getCaretItem().markUpdateCaretBounds(); |
1351 |
} |
1385 |
} |
1352 |
} finally { |
1386 |
} finally { |
1353 |
doc.readUnlock(); |
1387 |
doc.readUnlock(); |
1354 |
} |
1388 |
} |
1355 |
} |
1389 |
} |
1356 |
return ret; |
|
|
1357 |
} |
1390 |
} |
1358 |
|
1391 |
|
1359 |
private boolean updateCaretBounds(CaretItem caret) { |
|
|
1360 |
JTextComponent c = component; |
1361 |
boolean ret = false; |
1362 |
AbstractDocument doc; |
1363 |
if (c != null && (doc = activeDoc) != null) { |
1364 |
doc.readLock(); |
1365 |
try { |
1366 |
ret = updateRealCaretBounds(caret, doc, c); |
1367 |
} finally { |
1368 |
doc.readUnlock(); |
1369 |
} |
1370 |
} |
1371 |
return ret; |
1372 |
} |
1373 |
|
1374 |
private boolean updateRealCaretBounds(CaretItem caret, Document doc, JTextComponent c) { |
1375 |
Position dotPos = caret.getDotPosition(); |
1376 |
int offset = dotPos == null? 0 : dotPos.getOffset(); |
1377 |
if (offset > doc.getLength()) { |
1378 |
offset = doc.getLength(); |
1379 |
} |
1380 |
Rectangle newCaretBounds; |
1381 |
try { |
1382 |
DocumentView docView = DocumentView.get(c); |
1383 |
if (docView != null) { |
1384 |
// docView.syncViewsRebuild(); // Make sure pending views changes are resolved |
1385 |
} |
1386 |
newCaretBounds = c.getUI().modelToView( |
1387 |
c, offset, Position.Bias.Forward); |
1388 |
// [TODO] Temporary fix - impl should remember real bounds computed by paintCustomCaret() |
1389 |
if (newCaretBounds != null) { |
1390 |
newCaretBounds.width = Math.max(newCaretBounds.width, 2); |
1391 |
} |
1392 |
|
1393 |
} catch (BadLocationException e) { |
1394 |
|
1395 |
newCaretBounds = null; |
1396 |
} |
1397 |
if (newCaretBounds != null) { |
1398 |
if (LOG.isLoggable(Level.FINE)) { |
1399 |
LOG.log(Level.FINE, "updateCaretBounds: old={0}, new={1}, offset={2}", |
1400 |
new Object[]{caret.getCaretBounds(), newCaretBounds, offset}); //NOI18N |
1401 |
} |
1402 |
caret.setCaretBounds(newCaretBounds); |
1403 |
return true; |
1404 |
} else { |
1405 |
return false; |
1406 |
} |
1407 |
} |
1408 |
|
1409 |
private void modelChanged(Document oldDoc, Document newDoc) { |
1392 |
private void modelChanged(Document oldDoc, Document newDoc) { |
1410 |
if (oldDoc != null) { |
1393 |
if (oldDoc != null) { |
1411 |
// ideally the oldDoc param shouldn't exist and only listenDoc should be used |
1394 |
// ideally the oldDoc param shouldn't exist and only listenDoc should be used |
Lines 1450-1481
Link Here
|
1450 |
} |
1433 |
} |
1451 |
} |
1434 |
} |
1452 |
|
1435 |
|
1453 |
private void paintCaret(Graphics g, CaretItem caret) { |
|
|
1454 |
JTextComponent c = component; |
1455 |
if (c != null) { |
1456 |
g.setColor(c.getCaretColor()); |
1457 |
Rectangle caretBounds = caret.getCaretBounds(); |
1458 |
switch (type) { |
1459 |
case THICK_LINE_CARET: |
1460 |
g.fillRect(caretBounds.x, caretBounds.y, this.thickCaretWidth, caretBounds.height - 1); |
1461 |
break; |
1462 |
|
1463 |
case THIN_LINE_CARET: |
1464 |
int upperX = caret.getCaretBounds().x; |
1465 |
g.drawLine((int) upperX, caret.getCaretBounds().y, caret.getCaretBounds().x, |
1466 |
(caret.getCaretBounds().y + caret.getCaretBounds().height - 1)); |
1467 |
break; |
1468 |
|
1469 |
case BLOCK_CARET: |
1470 |
// Use a CaretOverwriteModeHighlighting layer to paint the caret |
1471 |
break; |
1472 |
|
1473 |
default: |
1474 |
throw new IllegalStateException("Invalid caret type=" + type); |
1475 |
} |
1476 |
} |
1477 |
} |
1478 |
|
1479 |
void dispatchUpdate() { |
1436 |
void dispatchUpdate() { |
1480 |
JTextComponent c = component; |
1437 |
JTextComponent c = component; |
1481 |
if (c != null) { |
1438 |
if (c != null) { |
Lines 1525-1635
Link Here
|
1525 |
} |
1482 |
} |
1526 |
Document doc = c.getDocument(); |
1483 |
Document doc = c.getDocument(); |
1527 |
if (doc != null) { |
1484 |
if (doc != null) { |
1528 |
List<CaretInfo> sortedCarets = getSortedCarets(); |
1485 |
LockedViewHierarchy lvh = ViewHierarchy.get(c).lock(); |
1529 |
for (CaretInfo caret : sortedCarets) { |
1486 |
try { |
1530 |
CaretItem caretItem = caret.getCaretItem(); |
1487 |
CaretItem lastCaretItem = getLastCaretItem(); |
1531 |
Rectangle oldCaretBounds = caretItem.getCaretBounds(); // no need to deep copy |
1488 |
List<CaretInfo> sortedCarets = getSortedCarets(); |
1532 |
if (oldCaretBounds != null) { |
1489 |
for (CaretInfo caret : sortedCarets) { |
1533 |
c.repaint(oldCaretBounds); |
1490 |
CaretItem caretItem = caret.getCaretItem(); |
1534 |
} |
1491 |
if (caretItem.getAndClearUpdateCaretBounds()) { |
1535 |
|
1492 |
Shape caretShape = lvh.modelToView(caretItem.getDot(), Position.Bias.Forward); |
1536 |
// note - the order is important ! caret bounds must be updated even if the fold flag is true. |
1493 |
if (caretShape != null) { |
1537 |
if (updateCaretBounds(caretItem) || updateAfterFoldHierarchyChange) { |
1494 |
Rectangle newCaretBounds = caretShape.getBounds(); |
1538 |
Rectangle scrollBounds = new Rectangle(caretItem.getCaretBounds()); |
1495 |
Rectangle oldCaretBounds = caretItem.setCaretBoundsWithRepaint(newCaretBounds, c); |
1539 |
|
1496 |
// Only scroll the view for the LAST caret to be visible |
1540 |
// Optimization to avoid extra repaint: |
1497 |
if (scrollViewToCaret && caretItem == lastCaretItem) { |
1541 |
// If the caret bounds were not yet assigned then attempt |
1498 |
Rectangle scrollBounds = newCaretBounds; // Must possibly be cloned upon change |
1542 |
// to scroll the window so that there is an extra vertical space |
1499 |
|
1543 |
// for the possible horizontal scrollbar that may appear |
1500 |
// For null old bounds (likely at begining of component displayment) ensure that a possible |
1544 |
// if the line-view creation process finds line-view that |
1501 |
// horizontal scrollbar would hide the caret so enlarge the scroll bounds by hscrollbar height. |
1545 |
// is too wide and so the horizontal scrollbar will appear |
1502 |
if (oldCaretBounds == null) { |
1546 |
// consuming an extra vertical space at the bottom. |
1503 |
Component viewport = c.getParent(); |
1547 |
if (oldCaretBounds == null) { |
1504 |
if (viewport instanceof JViewport) { |
1548 |
Component viewport = c.getParent(); |
1505 |
Component scrollPane = viewport.getParent(); |
1549 |
if (viewport instanceof JViewport) { |
1506 |
if (scrollPane instanceof JScrollPane) { |
1550 |
Component scrollPane = viewport.getParent(); |
1507 |
JScrollBar hScrollBar = ((JScrollPane) scrollPane).getHorizontalScrollBar(); |
1551 |
if (scrollPane instanceof JScrollPane) { |
1508 |
if (hScrollBar != null) { |
1552 |
JScrollBar hScrollBar = ((JScrollPane) scrollPane).getHorizontalScrollBar(); |
1509 |
int hScrollBarHeight = hScrollBar.getPreferredSize().height; |
1553 |
if (hScrollBar != null) { |
1510 |
Dimension extentSize = ((JViewport) viewport).getExtentSize(); |
1554 |
int hScrollBarHeight = hScrollBar.getPreferredSize().height; |
1511 |
// If the extent size is high enough then extend |
1555 |
Dimension extentSize = ((JViewport) viewport).getExtentSize(); |
1512 |
// the scroll region by extra vertical space |
1556 |
// If the extent size is high enough then extend |
1513 |
if (extentSize.height >= caretItem.getCaretBounds().height + hScrollBarHeight) { |
1557 |
// the scroll region by extra vertical space |
1514 |
scrollBounds = new Rectangle(scrollBounds); // Clone |
1558 |
if (extentSize.height >= caretItem.getCaretBounds().height + hScrollBarHeight) { |
1515 |
scrollBounds.height += hScrollBarHeight; |
1559 |
scrollBounds.height += hScrollBarHeight; |
1516 |
} |
|
|
1517 |
} |
1518 |
} |
1560 |
} |
1519 |
} |
1561 |
} |
1520 |
} |
|
|
1521 |
|
1522 |
if (LOG.isLoggable(Level.FINER)) { |
1523 |
LOG.finer("Scrolling to: " + scrollBounds); |
1524 |
} |
1525 |
c.scrollRectToVisible(scrollBounds); |
1562 |
} |
1526 |
} |
1563 |
} |
1527 |
} |
1564 |
} |
1528 |
} |
1565 |
|
|
|
1566 |
Rectangle visibleBounds = c.getVisibleRect(); |
1567 |
|
1568 |
// If folds have changed attempt to scroll the view so that |
1569 |
// relative caret's visual position gets retained |
1570 |
// (the absolute position will change because of collapsed/expanded folds). |
1571 |
boolean doScroll = scrollViewToCaret; |
1572 |
boolean explicit = false; |
1573 |
if (oldCaretBounds != null && (!scrollViewToCaret || updateAfterFoldHierarchyChange)) { |
1574 |
int oldRelY = oldCaretBounds.y - visibleBounds.y; |
1575 |
// Only fix if the caret is within visible bounds and the new x or y coord differs from the old one |
1576 |
if (LOG.isLoggable(Level.FINER)) { |
1577 |
LOG.log(Level.FINER, "oldCaretBounds: {0}, visibleBounds: {1}, caretBounds: {2}", |
1578 |
new Object[]{oldCaretBounds, visibleBounds, caretItem.getCaretBounds()}); |
1579 |
} |
1580 |
if (oldRelY >= 0 && oldRelY < visibleBounds.height |
1581 |
&& (oldCaretBounds.y != caretItem.getCaretBounds().y || oldCaretBounds.x != caretItem.getCaretBounds().x)) { |
1582 |
doScroll = true; // Perform explicit scrolling |
1583 |
explicit = true; |
1584 |
int oldRelX = oldCaretBounds.x - visibleBounds.x; |
1585 |
// Do not retain the horizontal caret bounds by scrolling |
1586 |
// since many modifications do not explicitly say that they are typing modifications |
1587 |
// and this would cause problems like #176268 |
1588 |
// scrollBounds.x = Math.max(caretBounds.x - oldRelX, 0); |
1589 |
scrollBounds.y = Math.max(caretItem.getCaretBounds().y - oldRelY, 0); |
1590 |
// scrollBounds.width = visibleBounds.width; |
1591 |
scrollBounds.height = visibleBounds.height; |
1592 |
} |
1593 |
} |
1594 |
|
1595 |
// Historically the caret is expected to appear |
1596 |
// in the middle of the window if setDot() gets called |
1597 |
// e.g. by double-clicking in Navigator. |
1598 |
// If the caret bounds are more than a caret height below the present |
1599 |
// visible view bounds (or above the view bounds) |
1600 |
// then scroll the window so that the caret is in the middle |
1601 |
// of the visible window to see the context around the caret. |
1602 |
// This should work fine with PgUp/Down because these |
1603 |
// scroll the view explicitly. |
1604 |
if (scrollViewToCaret |
1605 |
&& !explicit |
1606 |
&& // #219580: if the preceding if-block computed new scrollBounds, it cannot be offset yet more |
1607 |
/* # 70915 !updateAfterFoldHierarchyChange && */ (caretItem.getCaretBounds().y > visibleBounds.y + visibleBounds.height + caretItem.getCaretBounds().height |
1608 |
|| caretItem.getCaretBounds().y + caretItem.getCaretBounds().height < visibleBounds.y - caretItem.getCaretBounds().height)) { |
1609 |
// Scroll into the middle |
1610 |
scrollBounds.y -= (visibleBounds.height - caretItem.getCaretBounds().height) / 2; |
1611 |
scrollBounds.height = visibleBounds.height; |
1612 |
} |
1613 |
if (LOG.isLoggable(Level.FINER)) { |
1614 |
LOG.finer("Resetting fold flag, current: " + updateAfterFoldHierarchyChange); |
1615 |
} |
1616 |
updateAfterFoldHierarchyChange = false; |
1617 |
|
1618 |
// Ensure that the viewport will be scrolled either to make the caret visible |
1619 |
// or to retain cart's relative visual position against the begining of the viewport's visible rectangle. |
1620 |
if (doScroll) { |
1621 |
if (LOG.isLoggable(Level.FINER)) { |
1622 |
LOG.finer("Scrolling to: " + scrollBounds); |
1623 |
} |
1624 |
c.scrollRectToVisible(scrollBounds); |
1625 |
if (!c.getVisibleRect().intersects(scrollBounds)) { |
1626 |
// HACK: see #219580: for some reason, the scrollRectToVisible may fail. |
1627 |
c.scrollRectToVisible(scrollBounds); |
1628 |
} |
1629 |
} |
1630 |
resetBlink(); |
1631 |
c.repaint(caretItem.getCaretBounds()); |
1632 |
} |
1529 |
} |
|
|
1530 |
} finally { |
1531 |
lvh.unlock(); |
1633 |
} |
1532 |
} |
1634 |
} |
1533 |
} |
1635 |
} |
1534 |
} |
Lines 1747-1767
Link Here
|
1747 |
} |
1646 |
} |
1748 |
|
1647 |
|
1749 |
private String dumpVisibility() { |
1648 |
private String dumpVisibility() { |
1750 |
return "visible=" + isVisible() + ", blinkVisible=" + blinkVisible; |
1649 |
return "visible=" + isVisible() + ", showing=" + showing; |
1751 |
} |
1650 |
} |
1752 |
|
1651 |
|
1753 |
/*private*/ void resetBlink() { |
1652 |
/*private*/ void resetBlink() { |
1754 |
boolean visible = isVisible(); |
1653 |
boolean visible = isVisible(); |
1755 |
synchronized (listenerList) { |
1654 |
synchronized (listenerList) { |
1756 |
if (flasher != null) { |
1655 |
if (blinkTimer != null) { |
1757 |
flasher.stop(); |
1656 |
blinkTimer.stop(); |
1758 |
setBlinkVisible(true); |
1657 |
setShowing(true); |
|
|
1658 |
lastBlinkTime = System.currentTimeMillis(); |
1759 |
if (visible) { |
1659 |
if (visible) { |
1760 |
if (LOG.isLoggable(Level.FINER)){ |
1660 |
if (LOG.isLoggable(Level.FINER)){ |
1761 |
LOG.finer("Reset blinking (caret already visible)" + // NOI18N |
1661 |
LOG.finer("Reset blinking (caret already visible)" + // NOI18N |
1762 |
" - starting the caret blinking timer: " + dumpVisibility() + '\n'); // NOI18N |
1662 |
" - starting the caret blinking timer: " + dumpVisibility() + '\n'); // NOI18N |
1763 |
} |
1663 |
} |
1764 |
flasher.start(); |
1664 |
blinkTimer.start(); |
1765 |
} else { |
1665 |
} else { |
1766 |
if (LOG.isLoggable(Level.FINER)){ |
1666 |
if (LOG.isLoggable(Level.FINER)){ |
1767 |
LOG.finer("Reset blinking (caret not visible)" + // NOI18N |
1667 |
LOG.finer("Reset blinking (caret not visible)" + // NOI18N |
Lines 1771-1780
Link Here
|
1771 |
} |
1671 |
} |
1772 |
} |
1672 |
} |
1773 |
} |
1673 |
} |
1774 |
|
1674 |
|
1775 |
/*private*/ void setBlinkVisible(boolean blinkVisible) { |
1675 |
/** |
|
|
1676 |
* Return true if the caret is visible and it should currently be painted on the screen. |
1677 |
* This flag is being toggled by caret blinking timer. |
1678 |
* |
1679 |
* @return true if caret is currently painted on the screen. |
1680 |
*/ |
1681 |
boolean isShowing() { |
1682 |
return showing; |
1683 |
} |
1684 |
|
1685 |
/** |
1686 |
* Set whether the carets should physically be showing on screen. |
1687 |
* <br> |
1688 |
* It's set to from true to false and vice versa by caret blinking timer. |
1689 |
* |
1690 |
* @param showing true if the carets should be showing on screen or false if not. |
1691 |
*/ |
1692 |
/*private*/ void setShowing(boolean showing) { |
1776 |
synchronized (listenerList) { |
1693 |
synchronized (listenerList) { |
1777 |
this.blinkVisible = blinkVisible; |
1694 |
this.showing = showing; |
1778 |
} |
1695 |
} |
1779 |
updateOverwriteModeLayer(false); |
1696 |
updateOverwriteModeLayer(false); |
1780 |
} |
1697 |
} |
Lines 1785-1791
Link Here
|
1785 |
CaretOverwriteModeHighlighting overwriteModeHighlighting = (CaretOverwriteModeHighlighting) |
1702 |
CaretOverwriteModeHighlighting overwriteModeHighlighting = (CaretOverwriteModeHighlighting) |
1786 |
c.getClientProperty(CaretOverwriteModeHighlighting.class); |
1703 |
c.getClientProperty(CaretOverwriteModeHighlighting.class); |
1787 |
if (overwriteModeHighlighting != null) { |
1704 |
if (overwriteModeHighlighting != null) { |
1788 |
overwriteModeHighlighting.setVisible(visible && blinkVisible); |
1705 |
overwriteModeHighlighting.setVisible(visible && showing); |
1789 |
} |
1706 |
} |
1790 |
} |
1707 |
} |
1791 |
} |
1708 |
} |
Lines 1944-1958
Link Here
|
1944 |
return false; |
1861 |
return false; |
1945 |
} |
1862 |
} |
1946 |
|
1863 |
|
1947 |
private void refresh() { |
|
|
1948 |
updateType(); |
1949 |
SwingUtilities.invokeLater(new Runnable() { |
1950 |
public @Override void run() { |
1951 |
updateAllCaretsBounds(); // the line height etc. may have change |
1952 |
} |
1953 |
}); |
1954 |
} |
1955 |
|
1956 |
private static String logMouseEvent(MouseEvent evt) { |
1864 |
private static String logMouseEvent(MouseEvent evt) { |
1957 |
return "x=" + evt.getX() + ", y=" + evt.getY() + ", clicks=" + evt.getClickCount() //NOI18N |
1865 |
return "x=" + evt.getX() + ", y=" + evt.getY() + ", clicks=" + evt.getClickCount() //NOI18N |
1958 |
+ ", component=" + s2s(evt.getComponent()) //NOI18N |
1866 |
+ ", component=" + s2s(evt.getComponent()) //NOI18N |
Lines 1964-1976
Link Here
|
1964 |
} |
1872 |
} |
1965 |
|
1873 |
|
1966 |
private final class ListenerImpl extends ComponentAdapter |
1874 |
private final class ListenerImpl extends ComponentAdapter |
1967 |
implements DocumentListener, AtomicLockListener, MouseListener, MouseMotionListener, FocusListener, ViewHierarchyListener, |
1875 |
implements ActionListener, DocumentListener, AtomicLockListener, MouseListener, |
1968 |
PropertyChangeListener, ActionListener, PreferenceChangeListener, KeyListener |
1876 |
MouseMotionListener, FocusListener, ViewHierarchyListener, |
|
|
1877 |
PropertyChangeListener, PreferenceChangeListener, KeyListener |
1969 |
{ |
1878 |
{ |
1970 |
|
1879 |
|
1971 |
ListenerImpl() { |
1880 |
ListenerImpl() { |
1972 |
} |
1881 |
} |
1973 |
|
1882 |
|
|
|
1883 |
@Override |
1884 |
public void actionPerformed(ActionEvent e) { |
1885 |
JTextComponent c = component; |
1886 |
if (c != null) { |
1887 |
setShowing(!showing); |
1888 |
List<CaretInfo> sortedCarets = getSortedCarets(); // TODO only repaint carets showing on screen |
1889 |
for (CaretInfo caret : sortedCarets) { |
1890 |
CaretItem caretItem = caret.getCaretItem(); |
1891 |
if (caretItem.getCaretBounds() != null) { |
1892 |
Rectangle repaintRect = caretItem.getCaretBounds(); |
1893 |
c.repaint(repaintRect); |
1894 |
} |
1895 |
} |
1896 |
// Check if the system is responsive enough to blink at the current blink rate |
1897 |
long tm = System.currentTimeMillis(); |
1898 |
if (tm - (blinkCurrentDelay + (blinkCurrentDelay >> 2)) > 0L) { |
1899 |
|
1900 |
} |
1901 |
} |
1902 |
} |
1903 |
|
1974 |
public @Override void preferenceChange(PreferenceChangeEvent evt) { |
1904 |
public @Override void preferenceChange(PreferenceChangeEvent evt) { |
1975 |
String setingName = evt == null ? null : evt.getKey(); |
1905 |
String setingName = evt == null ? null : evt.getKey(); |
1976 |
if (setingName == null || SimpleValueNames.CARET_BLINK_RATE.equals(setingName)) { |
1906 |
if (setingName == null || SimpleValueNames.CARET_BLINK_RATE.equals(setingName)) { |
Lines 1979-1985
Link Here
|
1979 |
rate = EditorPreferencesDefaults.defaultCaretBlinkRate; |
1909 |
rate = EditorPreferencesDefaults.defaultCaretBlinkRate; |
1980 |
} |
1910 |
} |
1981 |
setBlinkRate(rate); |
1911 |
setBlinkRate(rate); |
1982 |
refresh(); |
|
|
1983 |
} |
1912 |
} |
1984 |
} |
1913 |
} |
1985 |
|
1914 |
|
Lines 2051-2075
Link Here
|
2051 |
} |
1980 |
} |
2052 |
} |
1981 |
} |
2053 |
|
1982 |
|
2054 |
// ActionListener methods |
|
|
2055 |
/** |
2056 |
* Fired when blink timer fires |
2057 |
*/ |
2058 |
public @Override void actionPerformed(ActionEvent evt) { |
2059 |
JTextComponent c = component; |
2060 |
if (c != null) { |
2061 |
setBlinkVisible(!blinkVisible); |
2062 |
List<CaretInfo> sortedCarets = getSortedCarets(); // TODO only repaint carets showing on screen |
2063 |
for (CaretInfo caret : sortedCarets) { |
2064 |
CaretItem caretItem = caret.getCaretItem(); |
2065 |
if (caretItem.getCaretBounds() != null) { |
2066 |
Rectangle repaintRect = caretItem.getCaretBounds(); |
2067 |
c.repaint(repaintRect); |
2068 |
} |
2069 |
} |
2070 |
} |
2071 |
} |
2072 |
|
2073 |
// DocumentListener methods |
1983 |
// DocumentListener methods |
2074 |
public @Override void insertUpdate(DocumentEvent evt) { |
1984 |
public @Override void insertUpdate(DocumentEvent evt) { |
2075 |
JTextComponent c = component; |
1985 |
JTextComponent c = component; |
Lines 2078-2085
Link Here
|
2078 |
int offset = evt.getOffset(); |
1988 |
int offset = evt.getOffset(); |
2079 |
final int endOffset = offset + evt.getLength(); |
1989 |
final int endOffset = offset + evt.getLength(); |
2080 |
if (offset == 0) { |
1990 |
if (offset == 0) { |
2081 |
// Manually shift carets at offset zero |
1991 |
// Manually shift carets at offset zero - do this always even when inside atomic lock |
2082 |
runTransaction(CaretTransaction.RemoveType.DOCUMENT_INSERT_ZERO_OFFSET, endOffset, null, null); |
1992 |
runTransaction(CaretTransaction.RemoveType.DOCUMENT_INSERT_ZERO_OFFSET, 0, null, null); |
2083 |
} |
1993 |
} |
2084 |
// [TODO] proper undo solution |
1994 |
// [TODO] proper undo solution |
2085 |
modified = true; |
1995 |
modified = true; |
Lines 2241-2247
Link Here
|
2241 |
|
2151 |
|
2242 |
case CHAR_SELECTION: |
2152 |
case CHAR_SELECTION: |
2243 |
if (evt.isAltDown() && evt.isShiftDown()) { |
2153 |
if (evt.isAltDown() && evt.isShiftDown()) { |
2244 |
moveDotCaret(offset, getLastCaretItem()); |
2154 |
moveDot(offset); |
2245 |
} else { |
2155 |
} else { |
2246 |
moveDot(offset); // Will do setDot() if no selection |
2156 |
moveDot(offset); // Will do setDot() if no selection |
2247 |
adjustRectangularSelectionMouseX(evt.getX(), evt.getY()); // also fires state change |
2157 |
adjustRectangularSelectionMouseX(evt.getX(), evt.getY()); // also fires state change |
Lines 2405-2411
Link Here
|
2405 |
|
2315 |
|
2406 |
case CHAR_SELECTION: |
2316 |
case CHAR_SELECTION: |
2407 |
if (evt.isAltDown() && evt.isShiftDown()) { |
2317 |
if (evt.isAltDown() && evt.isShiftDown()) { |
2408 |
moveDotCaret(offset, getLastCaretItem()); |
2318 |
moveDot(offset); |
2409 |
} else { |
2319 |
} else { |
2410 |
moveDot(offset); |
2320 |
moveDot(offset); |
2411 |
adjustRectangularSelectionMouseX(evt.getX(), evt.getY()); |
2321 |
adjustRectangularSelectionMouseX(evt.getX(), evt.getY()); |
Lines 2566-2573
Link Here
|
2566 |
@Override |
2476 |
@Override |
2567 |
public void keyReleased(KeyEvent e) { |
2477 |
public void keyReleased(KeyEvent e) { |
2568 |
} |
2478 |
} |
|
|
2479 |
|
2480 |
} // End of ListenerImpl class |
2481 |
|
2482 |
private final class Blinker implements Runnable { |
2569 |
|
2483 |
|
2570 |
} // End of ListenerImpl class |
2484 |
/** |
|
|
2485 |
* Fired when blink task gets executed. |
2486 |
*/ |
2487 |
@Override |
2488 |
public void run() { |
2489 |
JTextComponent c = component; |
2490 |
if (c != null) { |
2491 |
setShowing(!showing); |
2492 |
// Repaint all carets |
2493 |
List<CaretInfo> sortedCarets = getSortedCarets(); // TODO only repaint carets showing on screen |
2494 |
for (CaretInfo caret : sortedCarets) { |
2495 |
CaretItem caretItem = caret.getCaretItem(); |
2496 |
if (caretItem.getCaretBounds() != null) { |
2497 |
Rectangle repaintRect = caretItem.getCaretBounds(); |
2498 |
if (repaintRect != null) { |
2499 |
c.repaint(repaintRect); |
2500 |
} |
2501 |
} |
2502 |
} |
2503 |
} |
2504 |
} |
2505 |
|
2506 |
} |
2571 |
|
2507 |
|
2572 |
|
2508 |
|
2573 |
private enum CaretType { |
2509 |
private enum CaretType { |