root/src/apps/debuganalyzer/gui/ListSelectionModel.cpp
/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "ListSelectionModel.h"


// #pragma mark - ListSelectionModel


ListSelectionModel::ListSelectionModel()
{
}


ListSelectionModel::ListSelectionModel(const ListSelectionModel& other)
{
        *this = other;
}


ListSelectionModel::~ListSelectionModel()
{
}


void
ListSelectionModel::Clear()
{
        int32 selectedCount = fSelectedItems.Count();
        if (selectedCount > 0) {
                int32 firstSelected = fSelectedItems[0];
                int32 lastSelected = fSelectedItems[selectedCount - 1];

                fSelectedItems.Clear();

                _NotifyItemsDeselected(firstSelected, lastSelected - firstSelected + 1);
        }
}


bool
ListSelectionModel::SelectItems(int32 itemIndex, int32 count,
        bool extendSelection)
{
        int32 endItemIndex = itemIndex + count;

        int32 index;
        if (extendSelection) {
                if (count <= 0)
                        return true;

                index = _FindItem(itemIndex);

                // count already selected items
                int32 alreadySelectedCount = _CountSelectedItemsInRange(index,
                        endItemIndex);
                if (alreadySelectedCount == count)
                        return true;

                // make room for the new items
                if (!fSelectedItems.InsertUninitialized(index + alreadySelectedCount,
                                count - alreadySelectedCount)) {
                        return false;
                }
        } else {
                // TODO: Don't clear -- just resize to the right size!
                Clear();
                if (count <= 0)
                        return true;

                index = 0;
                if (!fSelectedItems.AddUninitialized(count))
                        return false;
        }

        for (int32 i = 0; i < count; i++)
                fSelectedItems[index + i] = itemIndex + i;

        _NotifyItemsSelected(itemIndex, count);

        return true;
}


void
ListSelectionModel::DeselectItems(int32 itemIndex, int32 count)
{
        int32 endItemIndex = itemIndex + count;
        int32 index = _FindItem(itemIndex);

        // count actually selected items
        int32 actuallySelectedCount = _CountSelectedItemsInRange(index,
                endItemIndex);
        if (actuallySelectedCount == 0)
                return;

        fSelectedItems.Remove(index, actuallySelectedCount);

        _NotifyItemsDeselected(itemIndex, count);
}


void
ListSelectionModel::ItemsAdded(int32 itemIndex, int32 count)
{
        if (count <= 0)
                return;

        // re-index following items
        int32 index = _FindItem(itemIndex);
        int32 selectedCount = fSelectedItems.Count();
        for (int32 i = index; i < selectedCount; i++)
                fSelectedItems[i] += count;
}


void
ListSelectionModel::ItemsRemoved(int32 itemIndex, int32 count)
{
        if (count <= 0)
                return;

        int32 index = _FindItem(itemIndex);

        // count selected items in the range
        int32 actuallySelectedCount = _CountSelectedItemsInRange(index,
                itemIndex + count);
        if (actuallySelectedCount > 0)
                fSelectedItems.Remove(index, actuallySelectedCount);

        // re-index following items
        int32 selectedCount = fSelectedItems.Count();
        for (int32 i = index; i < selectedCount; i++)
                fSelectedItems[i] -= count;
}


bool
ListSelectionModel::AddListener(Listener* listener)
{
        return fListeners.AddItem(listener);
}


void
ListSelectionModel::RemoveListener(Listener* listener)
{
        fListeners.RemoveItem(listener);
}


ListSelectionModel&
ListSelectionModel::operator=(const ListSelectionModel& other)
{
        Clear();

        fSelectedItems = other.fSelectedItems;

        int32 selectedCount = CountSelectedItems();
        if (selectedCount > 0) {
                int32 firstSelected = fSelectedItems[0];
                int32 lastSelected = fSelectedItems[selectedCount - 1];
                _NotifyItemsDeselected(firstSelected, lastSelected - firstSelected + 1);
        }

        return *this;
}


int32
ListSelectionModel::_FindItem(int32 itemIndex) const
{
        // binary search the index of the first item >= itemIndex
        int32 lower = 0;
        int32 upper = fSelectedItems.Count();

        while (lower < upper) {
                int32 mid = (lower + upper) / 2;

                if (fSelectedItems[mid] < itemIndex)
                        lower = mid + 1;
                else
                        upper = mid;
        }

        return lower;
}


int32
ListSelectionModel::_CountSelectedItemsInRange(int32 index,
        int32 endItemIndex) const
{
        int32 count = 0;
        int32 selectedCount = fSelectedItems.Count();
        for (int32 i = index; i < selectedCount; i++) {
                if (SelectedItemAt(i) >= endItemIndex)
                        break;
                count++;
        }

        return count;
}


void
ListSelectionModel::_NotifyItemsSelected(int32 index, int32 count)
{
        int32 listenerCount = fListeners.CountItems();
        for (int32 i = listenerCount - 1; i >= 0; i--)
                fListeners.ItemAt(i)->ItemsSelected(this, index, count);
}


void
ListSelectionModel::_NotifyItemsDeselected(int32 index, int32 count)
{
        int32 listenerCount = fListeners.CountItems();
        for (int32 i = listenerCount - 1; i >= 0; i--)
                fListeners.ItemAt(i)->ItemsDeselected(this, index, count);
}


// #pragma mark - Listener


ListSelectionModel::Listener::~Listener()
{
}


void
ListSelectionModel::Listener::ItemsSelected(ListSelectionModel* model,
        int32 index, int32 count)
{
}


void
ListSelectionModel::Listener::ItemsDeselected(ListSelectionModel* model,
        int32 index, int32 count)
{
}