This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 100753
Collapse All | Expand All

(-)editor/lib2/src/org/netbeans/api/editor/EditorRegistry.java (-34 / +141 lines)
Lines 42-47 Link Here
42
package org.netbeans.api.editor;
42
package org.netbeans.api.editor;
43
43
44
import java.awt.Component;
44
import java.awt.Component;
45
import java.awt.event.ActionEvent;
46
import java.awt.event.ActionListener;
45
import java.awt.event.FocusEvent;
47
import java.awt.event.FocusEvent;
46
import java.awt.event.FocusListener;
48
import java.awt.event.FocusListener;
47
import java.beans.PropertyChangeEvent;
49
import java.beans.PropertyChangeEvent;
Lines 53-58 Link Here
53
import java.util.List;
55
import java.util.List;
54
import java.util.logging.Level;
56
import java.util.logging.Level;
55
import java.util.logging.Logger;
57
import java.util.logging.Logger;
58
import javax.swing.JComponent;
59
import javax.swing.Timer;
60
import javax.swing.event.AncestorEvent;
61
import javax.swing.event.AncestorListener;
56
import javax.swing.text.Document;
62
import javax.swing.text.Document;
57
import javax.swing.text.JTextComponent;
63
import javax.swing.text.JTextComponent;
58
import org.netbeans.lib.editor.util.ArrayUtilities;
64
import org.netbeans.lib.editor.util.ArrayUtilities;
Lines 89-95 Link Here
89
     * <br/>
95
     * <br/>
90
     * The focused component will become the first in the components list.
96
     * The focused component will become the first in the components list.
91
     * <br/>
97
     * <br/>
92
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the a component
98
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be a component
93
     * losing the focus {@link FocusEvent#getOppositeComponent()}.
99
     * losing the focus {@link FocusEvent#getOppositeComponent()}.
94
     * The {@link java.beans.PropertyChangeEvent#getNewValue()} will be the text component gaining the focus.
100
     * The {@link java.beans.PropertyChangeEvent#getNewValue()} will be the text component gaining the focus.
95
     */
101
     */
Lines 117-122 Link Here
117
    public static final String FOCUSED_DOCUMENT_PROPERTY = "focusedDocument";
123
    public static final String FOCUSED_DOCUMENT_PROPERTY = "focusedDocument";
118
124
119
    /**
125
    /**
126
     * Fired when the last focused component (returned previously from {@link #lastFocusedComponent()})
127
     * was removed from component hierarchy (so it's likely that the component will be released completely
128
     * and garbage-collected).
129
     * <br/>
130
     * Such component will no longer be returned from {@link #componentList()}
131
     * or {@link #lastFocusedComponent()}.
132
     * <br/>
133
     * The {@link java.beans.PropertyChangeEvent#getOldValue()} will be the removed
134
     * last focused component and the {@link java.beans.PropertyChangeEvent#getNewValue()}
135
     * will be the component that would currently be returned from {@link #lastFocusedComponent()}.
136
     * <br/>
137
     * If {@link java.beans.PropertyChangeEvent#getNewValue()} returns <code>null</code>
138
     * then there are no longer any registered components
139
     * ({@link #componentList()} would return empty list). If the client
140
     * holds per-last-focused-component data it should clear them.
141
     */
142
    public static final String LAST_FOCUSED_REMOVED_PROPERTY = "lastFocusedRemoved";
143
144
    /**
120
     * Double linked list of weak references to text components.
145
     * Double linked list of weak references to text components.
121
     */
146
     */
122
    private static Item textComponentRefs;
147
    private static Item textComponentRefs;
Lines 213-228 Link Here
213
            Item item = new Item(c);
238
            Item item = new Item(c);
214
            c.putClientProperty(Item.class, item);
239
            c.putClientProperty(Item.class, item);
215
            c.addFocusListener(FocusL.INSTANCE);
240
            c.addFocusListener(FocusL.INSTANCE);
241
            c.addAncestorListener(AncestorL.INSTANCE);
216
            // Add to end of list
242
            // Add to end of list
217
            if (textComponentRefs == null)
243
            addAsLast(item);
218
                textComponentRefs = item;
219
            else {
220
                Item i = textComponentRefs;
221
                while (i.next != null)
222
                    i = i.next;
223
                i.next = item;
224
                item.previous = i;
225
            }
226
            if (LOG.isLoggable(Level.FINE)) {
244
            if (LOG.isLoggable(Level.FINE)) {
227
                LOG.log(Level.FINE, "REGISTERED new component as last item:\n" + dumpItemList());
245
                LOG.log(Level.FINE, "REGISTERED new component as last item:\n" + dumpItemList());
228
            }
246
            }
Lines 271-280 Link Here
271
        return c;
289
        return c;
272
    }
290
    }
273
    
291
    
292
    private static void addAsLast(Item item) {
293
        if (item.linked)
294
            return;
295
        item.linked = true;
296
        if (textComponentRefs == null) {
297
            textComponentRefs = item;
298
        } else {
299
            Item i = textComponentRefs;
300
            while (i.next != null)
301
                i = i.next;
302
            i.next = item;
303
            item.previous = i;
304
        }
305
        // Assuming item.next == null (done in removeItem() too).
306
    }
307
308
    private static void addAsFirst(Item item) {
309
        if (item.linked)
310
            return;
311
        item.linked = true;
312
        item.next = textComponentRefs;
313
        if (textComponentRefs != null)
314
            textComponentRefs.previous = item;
315
        textComponentRefs = item;
316
    }
317
274
    /**
318
    /**
275
     * Remove given entry and return a next one.
319
     * Remove given entry and return a next one.
276
     */
320
     */
277
    private static Item removeItem(Item item) {
321
    private static Item removeItem(Item item) {
322
        if (!item.linked)
323
            return null;
324
        item.linked = false;
278
        Item next = item.next;
325
        Item next = item.next;
279
        if (item.previous == null) { // Head
326
        if (item.previous == null) { // Head
280
            assert (textComponentRefs == item);
327
            assert (textComponentRefs == item);
Lines 288-312 Link Here
288
        return next;
335
        return next;
289
    }
336
    }
290
    
337
    
291
    private static void moveToHead(Item item) {
292
        if (LOG.isLoggable(Level.FINEST)) { // Debugging
293
            isItemInList(item);
294
        }
295
        removeItem(item);
296
        item.next = textComponentRefs;
297
        if (textComponentRefs != null)
298
            textComponentRefs.previous = item;
299
        textComponentRefs = item;
300
        if (LOG.isLoggable(Level.FINEST)) { // Debugging
301
            isItemInList(item);
302
            checkItemListConsistency();
303
        }
304
    }
305
    
306
    private static void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
307
        pcs.firePropertyChange(propertyName, oldValue, newValue);
308
    }
309
    
310
    private static boolean isItemInList(Item item) {
338
    private static boolean isItemInList(Item item) {
311
        Item i = textComponentRefs;
339
        Item i = textComponentRefs;
312
        while (i != null) {
340
        while (i != null) {
Lines 321-334 Link Here
321
        Item item = textComponentRefs;
349
        Item item = textComponentRefs;
322
        Item previous = null;
350
        Item previous = null;
323
        while (item != null) {
351
        while (item != null) {
324
            assert item.previous == previous;
352
            assert item.linked : "item=" + item + " is in list but not linked.";
353
            assert item.previous == previous : "Invalid previous of item=" + item;
325
            previous = item;
354
            previous = item;
326
            item = item.next;
355
            item = item.next;
327
        }
356
        }
328
        if (previous != null)
329
            assert previous.next == null;
330
    }
357
    }
331
358
359
    private static void moveToHead(Item item) {
360
        if (LOG.isLoggable(Level.FINEST)) { // Debugging
361
            isItemInList(item);
362
            checkItemListConsistency();
363
        }
364
        removeItem(item);
365
        addAsFirst(item);
366
        if (LOG.isLoggable(Level.FINEST)) { // Debugging
367
            isItemInList(item);
368
            checkItemListConsistency();
369
        }
370
    }
371
    
372
    private static void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
373
        pcs.firePropertyChange(propertyName, oldValue, newValue);
374
    }
375
    
332
    private static String dumpItemList() {
376
    private static String dumpItemList() {
333
        StringBuilder sb = new StringBuilder();
377
        StringBuilder sb = new StringBuilder();
334
        int i = 0;
378
        int i = 0;
Lines 344-350 Link Here
344
        return sb.toString();
388
        return sb.toString();
345
    }
389
    }
346
    
390
    
347
    private static String dumpComponent(JTextComponent c) {
391
    static String dumpComponent(JComponent c) {
348
        return "c[IHC=" + System.identityHashCode(c)
392
        return "c[IHC=" + System.identityHashCode(c)
349
                + "]=" + c;
393
                + "]=" + c;
350
    }
394
    }
Lines 358-366 Link Here
358
            super(c);
402
            super(c);
359
        }
403
        }
360
        
404
        
405
        boolean linked;
406
        
361
        Item next;
407
        Item next;
362
        
408
        
363
        Item previous;
409
        Item previous;
410
        
411
        Timer runningTimer;
412
413
        @Override
414
        public String toString() {
415
            return "component=" + get() + ", linked=" + linked +
416
                    ", hasTimer=" + (runningTimer != null) +
417
                    ", hasPrevious=" + (previous != null) + ", hasNext=" + (next != null);
418
        }
364
419
365
    }
420
    }
366
    
421
    
Lines 385-394 Link Here
385
440
386
        public void propertyChange(PropertyChangeEvent evt) {
441
        public void propertyChange(PropertyChangeEvent evt) {
387
            if ("document".equals(evt.getPropertyName())) {
442
            if ("document".equals(evt.getPropertyName())) {
388
                focusedDocumentChange((JTextComponent)evt.getSource(), (Document)evt.getOldValue(), (Document)evt.getNewValue());
443
                focusedDocumentChange((JTextComponent)evt.getSource(),
444
                        (Document)evt.getOldValue(), (Document)evt.getNewValue());
445
            }
446
        }
447
448
    }
449
    
450
    private static final class AncestorL implements AncestorListener {
451
        
452
        static final AncestorL INSTANCE = new AncestorL();
453
        
454
        private static final int BEFORE_REMOVE_DELAY = 2000; // 2000ms delay
455
456
        public void ancestorAdded(AncestorEvent event) {
457
            Item item = (Item)event.getComponent().getClientProperty(Item.class);
458
            if (item.runningTimer != null) {
459
                item.runningTimer.stop();
460
                item.runningTimer = null;
389
            }
461
            }
462
            // If the component was removed from the component hierarchy and then
463
            // returned back to the hierarchy it will be readded to the component list.
464
            // If the item is not removed yet then the addToEnd() will do nothing.
465
            addAsLast(item);
466
            if (LOG.isLoggable(Level.FINER)) {
467
                LOG.fine("ancestorAdded: c=" + dumpComponent(event.getComponent()) + '\n');
468
            }
469
        }
470
471
        public void ancestorMoved(AncestorEvent event) {
390
        }
472
        }
391
473
474
        public void ancestorRemoved(AncestorEvent event) {
475
            final JComponent component = event.getComponent();
476
            Item item = (Item)component.getClientProperty(Item.class);
477
            if (LOG.isLoggable(Level.FINER)) {
478
                LOG.fine("ancestorRemoved: c=" + dumpComponent(event.getComponent()) + '\n');
479
            }
480
            item.runningTimer = new Timer(BEFORE_REMOVE_DELAY,
481
                new ActionListener() {
482
                    public void actionPerformed(ActionEvent e) {
483
                        Item item = (Item)component.getClientProperty(Item.class);
484
                        item.runningTimer.stop();
485
                        item.runningTimer = null;
486
                        // Remove component from item chain
487
                        removeItem(item);
488
                        firePropertyChange(LAST_FOCUSED_REMOVED_PROPERTY, component, lastFocusedComponent());
489
                        if (LOG.isLoggable(Level.FINE)) {
490
                            LOG.fine("Timer fired => component removed: c="
491
                                    + dumpComponent(component) + '\n');
492
                        }
493
                    }
494
                }
495
            );
496
            item.runningTimer.start();
497
        }
498
        
392
    }
499
    }
393
500
394
    private static final class PackageAccessor extends EditorApiPackageAccessor {
501
    private static final class PackageAccessor extends EditorApiPackageAccessor {

Return to bug 100753