root/src/apps/icon-o-matic/generic/command/MoveCommand.h
/*
 * Copyright 2006-2007, 2023, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Stephan Aßmus <superstippi@gmx.de>
 *              Zardshard
 */
#ifndef MOVE_COMMAND_H
#define MOVE_COMMAND_H

#include <new>
#include <stdio.h>

#include <Catalog.h>
#include <Locale.h>
#include <StringFormat.h>

#include "Command.h"
#include "Container.h"
#include "IconBuild.h"

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Icon-O-Matic-MoveItemsCmd"

using std::nothrow;

_USING_ICON_NAMESPACE


/*! Moves a set of \c items to a different location in a \c container.

        \note This class should be subclassed and the \c GetName member overridden.
*/
template <class Type>
class MoveCommand : public Command {
 public:
                                                                MoveCommand(
                                                                        Container<Type>* container,
                                                                        Type** items,
                                                                        int32 count,
                                                                        int32 toIndex);
        virtual                                         ~MoveCommand();

        virtual status_t                        InitCheck();

        virtual status_t                        Perform();
        virtual status_t                        Undo();

        virtual void                            GetName(BString& name);

 protected:
                        Container<Type>*        fContainer;
                        Type**                          fItems;
                        int32*                          fIndices;
                        int32                           fToIndex;
                        int32                           fCount;
};


template <class Type>
MoveCommand<Type>::MoveCommand(Container<Type>* container,
                Type** items,
                int32 count,
                int32 toIndex)
        : Command(),
          fContainer(container),
          fItems(items),
          fIndices(count > 0 ? new (nothrow) int32[count] : NULL),
          fToIndex(toIndex),
          fCount(count)
{
        if (!fContainer || !fItems || !fIndices)
                return;

        // init original shape indices and
        // adjust toIndex compensating for items that
        // are removed before that index
        int32 itemsBeforeIndex = 0;
        for (int32 i = 0; i < fCount; i++) {
                fIndices[i] = fContainer->IndexOf(fItems[i]);
                if (fIndices[i] >= 0 && fIndices[i] < fToIndex)
                        itemsBeforeIndex++;
        }
        fToIndex -= itemsBeforeIndex;
}


template <class Type>
MoveCommand<Type>::~MoveCommand()
{
        delete[] fItems;
        delete[] fIndices;
}


template <class Type>
status_t
MoveCommand<Type>::InitCheck()
{
        if (!fContainer || !fItems || !fIndices)
                return B_NO_INIT;

        // analyse the move, don't return B_OK in case
        // the container state does not change...

        int32 index = fIndices[0];
                // NOTE: fIndices == NULL if fCount < 1

        if (index != fToIndex) {
                // a change is guaranteed
                return B_OK;
        }

        // the insertion index is the same as the index of the first
        // moved item, a change only occures if the indices of the
        // moved items is not contiguous
        bool isContiguous = true;
        for (int32 i = 1; i < fCount; i++) {
                if (fIndices[i] != index + 1) {
                        isContiguous = false;
                        break;
                }
                index = fIndices[i];
        }
        if (isContiguous) {
                // the container state will not change because of the move
                return B_ERROR;
        }

        return B_OK;
}


template <class Type>
status_t
MoveCommand<Type>::Perform()
{
        status_t ret = B_OK;

        // remove paths from container
        for (int32 i = 0; i < fCount; i++) {
                if (fItems[i] && !fContainer->RemoveItem(fItems[i])) {
                        ret = B_ERROR;
                        break;
                }
        }
        if (ret < B_OK)
                return ret;

        // add paths to container at the insertion index
        int32 index = fToIndex;
        for (int32 i = 0; i < fCount; i++) {
                if (fItems[i] && !fContainer->AddItem(fItems[i], index++)) {
                        ret = B_ERROR;
                        break;
                }
        }

        return ret;
}


template <class Type>
status_t
MoveCommand<Type>::Undo()
{
        status_t ret = B_OK;

        // remove paths from container
        for (int32 i = 0; i < fCount; i++) {
                if (fItems[i] && !fContainer->RemoveItem(fItems[i])) {
                        ret = B_ERROR;
                        break;
                }
        }
        if (ret < B_OK)
                return ret;

        // add paths to container at remembered indices
        for (int32 i = 0; i < fCount; i++) {
                if (fItems[i] && !fContainer->AddItem(fItems[i], fIndices[i])) {
                        ret = B_ERROR;
                        break;
                }
        }

        return ret;
}


template <class Type>
void
MoveCommand<Type>::GetName(BString& name)
{
        static BStringFormat format(B_TRANSLATE("Move {0, plural, "
                "one{item} other{items}}"));
        format.Format(name, fCount);
}
#endif // MOVE_COMMAND_H