/* * Copyright (c) 2008, Matthias Mann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Matthias Mann nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package de.matthiasmann.twl.model; import java.util.ArrayList; /** * A composite list model which concatinates several other list models. * Changes on the contained list models are forwarded to this model. * * @param <T> The type of the list entries * @author Matthias Mann */ public class CombinedListModel<T> extends SimpleListModel<T> { private final ArrayList<Sublist> sublists; private int[] sublistStarts; private SubListsModel subListsModel; public CombinedListModel() { this.sublists = new ArrayList<Sublist>(); this.sublistStarts = new int[1]; } public int getNumEntries() { return sublistStarts[sublistStarts.length-1]; } public T getEntry(int index) { Sublist sl = getSublistForIndex(index); if(sl != null) { return sl.getEntry(index - sl.startIndex); } throw new IndexOutOfBoundsException(); } @Override public Object getEntryTooltip(int index) { Sublist sl = getSublistForIndex(index); if(sl != null) { return sl.getEntryTooltip(index - sl.startIndex); } throw new IndexOutOfBoundsException(); } public int getNumSubLists() { return sublists.size(); } public void addSubList(ListModel<T> model) { addSubList(sublists.size(), model); } public void addSubList(int index, ListModel<T> model) { Sublist sl = new Sublist(model); sublists.add(index, sl); adjustStartOffsets(); int numEntries = sl.getNumEntries(); if(numEntries > 0) { fireEntriesInserted(sl.startIndex, sl.startIndex+numEntries-1); } if(subListsModel != null) { subListsModel.fireEntriesInserted(index, index); } } public int findSubList(ListModel<T> model) { for(int i=0 ; i<sublists.size() ; i++) { Sublist sl = sublists.get(i); if(sl.list == model) { return i; } } return -1; } public void removeAllSubLists() { for(int i=0 ; i<sublists.size() ; i++) { sublists.get(i).removeChangeListener(); } sublists.clear(); adjustStartOffsets(); fireAllChanged(); if(subListsModel != null) { subListsModel.fireAllChanged(); } } public boolean removeSubList(ListModel<T> model) { int index = findSubList(model); if(index >= 0) { removeSubList(index); return true; } return false; } public ListModel<T> removeSubList(int index) { Sublist sl = sublists.remove(index); sl.removeChangeListener(); adjustStartOffsets(); int numEntries = sl.getNumEntries(); if(numEntries > 0) { fireEntriesDeleted(sl.startIndex, sl.startIndex+numEntries-1); } if(subListsModel != null) { subListsModel.fireEntriesDeleted(index, index); } return sl.list; } public ListModel<ListModel<T>> getModelForSubLists() { if(subListsModel == null) { subListsModel = new SubListsModel(); } return subListsModel; } public int getStartIndexOfSublist(int sublistIndex) { return sublists.get(sublistIndex).startIndex; } private Sublist getSublistForIndex(int index) { int[] offsets = sublistStarts; int lo = 0; int hi = offsets.length - 1; while(lo <= hi) { int mid = (lo+hi) >>> 1; int delta = offsets[mid] - index; if(delta <= 0) { lo = mid + 1; } if(delta > 0) { hi = mid - 1; } } if(lo > 0 && lo <= sublists.size()) { Sublist sl = sublists.get(lo-1); assert sl.startIndex <= index; return sl; } return null; } void adjustStartOffsets() { int[] offsets = new int[sublists.size()+1]; int startIdx = 0; for(int idx=0 ; idx < sublists.size() ;) { Sublist sl = sublists.get(idx); sl.startIndex = startIdx; startIdx += sl.getNumEntries(); offsets[++idx] = startIdx; } this.sublistStarts = offsets; } class Sublist implements ChangeListener { final ListModel<T> list; int startIndex; public Sublist(ListModel<T> list) { this.list = list; this.list.addChangeListener(this); } public void removeChangeListener() { list.removeChangeListener(this); } public boolean matchPrefix(int index, String prefix) { return list.matchPrefix(index, prefix); } public int getNumEntries() { return list.getNumEntries(); } public Object getEntryTooltip(int index) { return list.getEntryTooltip(index); } public T getEntry(int index) { return list.getEntry(index); } public void entriesInserted(int first, int last) { adjustStartOffsets(); fireEntriesInserted(startIndex + first, startIndex + last); } public void entriesDeleted(int first, int last) { adjustStartOffsets(); fireEntriesDeleted(startIndex + first, startIndex + last); } public void entriesChanged(int first, int last) { fireEntriesChanged(startIndex + first, startIndex + last); } public void allChanged() { fireAllChanged(); } } class SubListsModel extends SimpleListModel<ListModel<T>> { public int getNumEntries() { return sublists.size(); } public ListModel<T> getEntry(int index) { return sublists.get(index).list; } } }