mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
fixed bug where listing would jump when opening/closing large structures
or arrays
This commit is contained in:
parent
df971ee613
commit
7b7e730844
12 changed files with 175 additions and 23 deletions
|
@ -21,10 +21,12 @@ import java.math.BigInteger;
|
||||||
import docking.widgets.fieldpanel.Layout;
|
import docking.widgets.fieldpanel.Layout;
|
||||||
import docking.widgets.fieldpanel.LayoutModel;
|
import docking.widgets.fieldpanel.LayoutModel;
|
||||||
import docking.widgets.fieldpanel.field.Field;
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
|
import docking.widgets.fieldpanel.listener.IndexMapper;
|
||||||
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
||||||
import docking.widgets.fieldpanel.support.*;
|
import docking.widgets.fieldpanel.support.*;
|
||||||
import ghidra.app.util.viewer.field.*;
|
import ghidra.app.util.viewer.field.*;
|
||||||
import ghidra.app.util.viewer.format.FormatManager;
|
import ghidra.app.util.viewer.format.FormatManager;
|
||||||
|
import ghidra.app.util.viewer.util.AddressBasedIndexMapper;
|
||||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.Array;
|
import ghidra.program.model.data.Array;
|
||||||
|
@ -190,7 +192,7 @@ public class ListingModelAdapter implements LayoutModel, ListingModelListener {
|
||||||
public void modelSizeChanged() {
|
public void modelSizeChanged() {
|
||||||
preferredViewSize = null;
|
preferredViewSize = null;
|
||||||
for (LayoutModelListener listener : listeners) {
|
for (LayoutModelListener listener : listeners) {
|
||||||
listener.modelSizeChanged();
|
listener.modelSizeChanged(IndexMapper.IDENTITY_MAPPER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,11 +448,16 @@ public class ListingModelAdapter implements LayoutModel, ListingModelListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void resetIndexMap() {
|
protected void resetIndexMap() {
|
||||||
|
AddressIndexMap previous = addressToIndexMap.clone();
|
||||||
BigInteger indexCount = addressToIndexMap.getIndexCount();
|
BigInteger indexCount = addressToIndexMap.getIndexCount();
|
||||||
addressToIndexMap.reset();
|
addressToIndexMap.reset();
|
||||||
removeUnviewableAddressRanges();
|
removeUnviewableAddressRanges();
|
||||||
if (!addressToIndexMap.getIndexCount().equals(indexCount)) {
|
if (!addressToIndexMap.getIndexCount().equals(indexCount)) {
|
||||||
modelSizeChanged();
|
AddressBasedIndexMapper mapper =
|
||||||
|
new AddressBasedIndexMapper(previous, addressToIndexMap);
|
||||||
|
for (LayoutModelListener listener : listeners) {
|
||||||
|
listener.modelSizeChanged(mapper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||||
private LayoutModelListener layoutModelListener = new LayoutModelListener() {
|
private LayoutModelListener layoutModelListener = new LayoutModelListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void modelSizeChanged() {
|
public void modelSizeChanged(IndexMapper mapper) {
|
||||||
updateProviders();
|
updateProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed 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 ghidra.app.util.viewer.util;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import docking.widgets.fieldpanel.listener.IndexMapper;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
|
||||||
|
/** Implementation of IndexMapper that uses an old and new AddressIndexMap to map indexes
|
||||||
|
* when the AddressIndexMap changes.
|
||||||
|
*/
|
||||||
|
public class AddressBasedIndexMapper implements IndexMapper {
|
||||||
|
|
||||||
|
private AddressIndexMap from;
|
||||||
|
private AddressIndexMap to;
|
||||||
|
|
||||||
|
public AddressBasedIndexMapper(AddressIndexMap from, AddressIndexMap to) {
|
||||||
|
this.from = from;
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger map(BigInteger value) {
|
||||||
|
Address address = from.getAddress(value);
|
||||||
|
if (address == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return to.getIndex(address);
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,6 +81,19 @@ public class AddressIndexMap {
|
||||||
buildMapping();
|
buildMapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AddressIndexMap(AddressIndexMap source) {
|
||||||
|
this.numAddresses = source.numAddresses;
|
||||||
|
indexList = source.indexList;
|
||||||
|
addressList = source.addressList;
|
||||||
|
currentViewAddressSet = source.currentViewAddressSet;
|
||||||
|
originalAddressSet = source.getOriginalAddressSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AddressIndexMap clone() {
|
||||||
|
return new AddressIndexMap(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total number of addresses
|
* Returns the total number of addresses
|
||||||
* @return the number of addresses in the view
|
* @return the number of addresses in the view
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.byteviewer;
|
package ghidra.app.plugin.core.byteviewer;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.format.DataFormatModel;
|
|
||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
@ -28,8 +25,10 @@ import docking.widgets.fieldpanel.Layout;
|
||||||
import docking.widgets.fieldpanel.LayoutModel;
|
import docking.widgets.fieldpanel.LayoutModel;
|
||||||
import docking.widgets.fieldpanel.field.EmptyTextField;
|
import docking.widgets.fieldpanel.field.EmptyTextField;
|
||||||
import docking.widgets.fieldpanel.field.Field;
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
|
import docking.widgets.fieldpanel.listener.IndexMapper;
|
||||||
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
||||||
import docking.widgets.fieldpanel.support.SingleRowLayout;
|
import docking.widgets.fieldpanel.support.SingleRowLayout;
|
||||||
|
import ghidra.app.plugin.core.format.DataFormatModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the LayoutModel for ByteViewer Components.
|
* Implements the LayoutModel for ByteViewer Components.
|
||||||
|
@ -88,7 +87,7 @@ class ByteViewerLayoutModel implements LayoutModel {
|
||||||
|
|
||||||
public void indexSetChanged() {
|
public void indexSetChanged() {
|
||||||
for (LayoutModelListener listener : listeners) {
|
for (LayoutModelListener listener : listeners) {
|
||||||
listener.modelSizeChanged();
|
listener.modelSizeChanged(IndexMapper.IDENTITY_MAPPER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +116,7 @@ class ByteViewerLayoutModel implements LayoutModel {
|
||||||
/**
|
/**
|
||||||
* Returns the total number of valid indexes.
|
* Returns the total number of valid indexes.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public BigInteger getNumIndexes() {
|
public BigInteger getNumIndexes() {
|
||||||
return numIndexes;
|
return numIndexes;
|
||||||
}
|
}
|
||||||
|
@ -127,8 +127,8 @@ class ByteViewerLayoutModel implements LayoutModel {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<Field> fields = new ArrayList<Field>(8);
|
List<Field> fields = new ArrayList<Field>(8);
|
||||||
for (int i = 0; i < factorys.length; i++) {
|
for (FieldFactory factory : factorys) {
|
||||||
Field field = factorys[i].getField(index);
|
Field field = factory.getField(index);
|
||||||
if (field != null) {
|
if (field != null) {
|
||||||
fields.add(field);
|
fields.add(field);
|
||||||
}
|
}
|
||||||
|
@ -137,8 +137,8 @@ class ByteViewerLayoutModel implements LayoutModel {
|
||||||
if (factorys.length > 0) {
|
if (factorys.length > 0) {
|
||||||
FontMetrics fm = factorys[0].getMetrics();
|
FontMetrics fm = factorys[0].getMetrics();
|
||||||
int height = fm.getMaxAscent() + fm.getMaxDescent();
|
int height = fm.getMaxAscent() + fm.getMaxDescent();
|
||||||
fields.add(new EmptyTextField(height, factorys[0].getStartX(), 0,
|
fields.add(
|
||||||
factorys[0].getWidth()));
|
new EmptyTextField(height, factorys[0].getStartX(), 0, factorys[0].getWidth()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fields.add(new EmptyTextField(20, 0, 0, 10));
|
fields.add(new EmptyTextField(20, 0, 0, 10));
|
||||||
|
|
|
@ -30,6 +30,7 @@ import docking.help.HelpService;
|
||||||
import docking.widgets.fieldpanel.*;
|
import docking.widgets.fieldpanel.*;
|
||||||
import docking.widgets.fieldpanel.field.EmptyTextField;
|
import docking.widgets.fieldpanel.field.EmptyTextField;
|
||||||
import docking.widgets.fieldpanel.field.Field;
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
|
import docking.widgets.fieldpanel.listener.IndexMapper;
|
||||||
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
||||||
import docking.widgets.fieldpanel.support.SingleRowLayout;
|
import docking.widgets.fieldpanel.support.SingleRowLayout;
|
||||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||||
|
@ -759,7 +760,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
||||||
indexPanelWidth = getIndexPanelWidth(blocks);
|
indexPanelWidth = getIndexPanelWidth(blocks);
|
||||||
}
|
}
|
||||||
indexFactory.setIndexMap(indexMap, indexPanelWidth);
|
indexFactory.setIndexMap(indexMap, indexPanelWidth);
|
||||||
indexPanel.modelSizeChanged();
|
indexPanel.modelSizeChanged(IndexMapper.IDENTITY_MAPPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -968,7 +969,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
||||||
|
|
||||||
void indexSetChanged() {
|
void indexSetChanged() {
|
||||||
for (LayoutModelListener listener : layoutListeners) {
|
for (LayoutModelListener listener : layoutListeners) {
|
||||||
listener.modelSizeChanged();
|
listener.modelSizeChanged(IndexMapper.IDENTITY_MAPPER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import docking.widgets.SearchLocation;
|
||||||
import docking.widgets.fieldpanel.Layout;
|
import docking.widgets.fieldpanel.Layout;
|
||||||
import docking.widgets.fieldpanel.LayoutModel;
|
import docking.widgets.fieldpanel.LayoutModel;
|
||||||
import docking.widgets.fieldpanel.field.*;
|
import docking.widgets.fieldpanel.field.*;
|
||||||
|
import docking.widgets.fieldpanel.listener.IndexMapper;
|
||||||
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
||||||
import docking.widgets.fieldpanel.support.*;
|
import docking.widgets.fieldpanel.support.*;
|
||||||
import ghidra.app.decompiler.*;
|
import ghidra.app.decompiler.*;
|
||||||
|
@ -121,15 +122,15 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void modelSizeChanged() {
|
public void modelSizeChanged(IndexMapper mapper) {
|
||||||
for (int i = 0; i < listeners.size(); ++i) {
|
for (int i = 0; i < listeners.size(); ++i) {
|
||||||
listeners.get(i).modelSizeChanged();
|
listeners.get(i).modelSizeChanged(mapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void modelChanged() {
|
public void modelChanged() {
|
||||||
for (int i = 0; i < listeners.size(); ++i) {
|
for (int i = 0; i < listeners.size(); ++i) {
|
||||||
listeners.get(i).modelSizeChanged();
|
listeners.get(i).modelSizeChanged(IndexMapper.IDENTITY_MAPPER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -961,23 +961,45 @@ public class FieldPanel extends JPanel
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
// BigLayoutModelListener
|
// BigLayoutModelListener
|
||||||
public void modelSizeChanged() {
|
public void modelSizeChanged(IndexMapper indexMapper) {
|
||||||
BigInteger anchorIndex = layouts.isEmpty() ? BigInteger.ZERO : layouts.get(0).getIndex();
|
BigInteger anchorIndex =
|
||||||
|
layouts.isEmpty() ? BigInteger.ZERO : indexMapper.map(layouts.get(0).getIndex());
|
||||||
int anchorOffset = layouts.isEmpty() ? 0 : layouts.get(0).getYPos();
|
int anchorOffset = layouts.isEmpty() ? 0 : layouts.get(0).getYPos();
|
||||||
Point cursorPoint = getCursorPoint();
|
Point cursorPoint = getCursorPoint();
|
||||||
AnchoredLayout layout = findLayoutOnScreen(cursorPosition.getIndex());
|
BigInteger cursorIndex = indexMapper.map(cursorPosition.getIndex());
|
||||||
|
AnchoredLayout layout = findLayoutOnScreen(cursorIndex);
|
||||||
if (layout != null) {
|
if (layout != null) {
|
||||||
anchorIndex = cursorPosition.getIndex();
|
anchorIndex = cursorIndex;
|
||||||
anchorOffset = layout.getYPos();
|
anchorOffset = layout.getYPos();
|
||||||
}
|
}
|
||||||
notifyScrollListenerModelChanged();
|
notifyScrollListenerModelChanged();
|
||||||
layouts = layoutHandler.positionLayoutsAroundAnchor(anchorIndex, anchorOffset);
|
layouts = layoutHandler.positionLayoutsAroundAnchor(anchorIndex, anchorOffset);
|
||||||
|
|
||||||
|
updateHighlight(indexMapper);
|
||||||
cursorHandler.updateCursor(cursorPoint);
|
cursorHandler.updateCursor(cursorPoint);
|
||||||
notifyScrollListenerViewChangedAndRepaint();
|
notifyScrollListenerViewChangedAndRepaint();
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateHighlight(IndexMapper mapper) {
|
||||||
|
if (highlight.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FieldSelection oldHighlight = highlight;
|
||||||
|
highlight = new FieldSelection();
|
||||||
|
for (FieldRange range : oldHighlight) {
|
||||||
|
FieldLocation start = range.getStart();
|
||||||
|
FieldLocation end = range.getEnd();
|
||||||
|
BigInteger startIndex = mapper.map(start.getIndex());
|
||||||
|
BigInteger endIndex = mapper.map(end.getIndex());
|
||||||
|
if (startIndex != null && endIndex != null) {
|
||||||
|
start.setIndex(startIndex);
|
||||||
|
end.setIndex(endIndex);
|
||||||
|
highlight.addRange(start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintComponent(Graphics g) {
|
protected void paintComponent(Graphics g) {
|
||||||
model.flushChanges();
|
model.flushChanges();
|
||||||
|
|
|
@ -24,6 +24,7 @@ import javax.swing.JFrame;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.*;
|
import docking.widgets.fieldpanel.*;
|
||||||
import docking.widgets.fieldpanel.field.*;
|
import docking.widgets.fieldpanel.field.*;
|
||||||
|
import docking.widgets.fieldpanel.listener.IndexMapper;
|
||||||
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
||||||
import docking.widgets.fieldpanel.support.*;
|
import docking.widgets.fieldpanel.support.*;
|
||||||
import docking.widgets.indexedscrollpane.IndexedScrollPane;
|
import docking.widgets.indexedscrollpane.IndexedScrollPane;
|
||||||
|
@ -53,7 +54,7 @@ public class TestBigLayoutModel implements LayoutModel {
|
||||||
public void setNumIndexes(BigInteger n) {
|
public void setNumIndexes(BigInteger n) {
|
||||||
this.numIndexes = n;
|
this.numIndexes = n;
|
||||||
for (LayoutModelListener listener : listeners) {
|
for (LayoutModelListener listener : listeners) {
|
||||||
listener.modelSizeChanged();
|
listener.modelSizeChanged(IndexMapper.IDENTITY_MAPPER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed 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 docking.widgets.fieldpanel.listener;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IndexMapper that always maps an index back to itself.
|
||||||
|
*/
|
||||||
|
class IdentityMapper implements IndexMapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger map(BigInteger value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed 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 docking.widgets.fieldpanel.listener;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import docking.widgets.fieldpanel.FieldPanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for mapping indexes when the LayoutModel changes. In other words, if the mapping
|
||||||
|
* of layout indexes to some data model changes and you want the {@link FieldPanel} to continue
|
||||||
|
* to display the same model data on the screen, the IndexMapper can be used to convert old
|
||||||
|
* indexes to new indexes.
|
||||||
|
*/
|
||||||
|
public interface IndexMapper {
|
||||||
|
public IndexMapper IDENTITY_MAPPER = new IdentityMapper();
|
||||||
|
|
||||||
|
public BigInteger map(BigInteger value);
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -22,8 +21,10 @@ public interface LayoutModelListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called whenever the number of indexes changed
|
* Called whenever the number of indexes changed
|
||||||
|
* @param indexMapper Maps indexes from before the model size change to indexes after
|
||||||
|
* the model size changed.
|
||||||
*/
|
*/
|
||||||
void modelSizeChanged();
|
void modelSizeChanged(IndexMapper indexMapper);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the data at an index or range of indexes changes.
|
* Called when the data at an index or range of indexes changes.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue