1/*
2 * Copyright 2006-2007, 2023, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 *		Zardshard
8 */
9#ifndef MOVE_COMMAND_H
10#define MOVE_COMMAND_H
11
12#include <new>
13#include <stdio.h>
14
15#include <Catalog.h>
16#include <Locale.h>
17#include <StringFormat.h>
18
19#include "Command.h"
20#include "Container.h"
21#include "IconBuild.h"
22
23#undef B_TRANSLATION_CONTEXT
24#define B_TRANSLATION_CONTEXT "Icon-O-Matic-MoveItemsCmd"
25
26using std::nothrow;
27
28_USING_ICON_NAMESPACE
29
30
31/*! Moves a set of \c items to a different location in a \c container.
32
33	\note This class should be subclassed and the \c GetName member overridden.
34*/
35template <class Type>
36class MoveCommand : public Command {
37 public:
38								MoveCommand(
39									Container<Type>* container,
40									Type** items,
41									int32 count,
42									int32 toIndex);
43	virtual						~MoveCommand();
44
45	virtual	status_t			InitCheck();
46
47	virtual	status_t			Perform();
48	virtual status_t			Undo();
49
50	virtual void				GetName(BString& name);
51
52 protected:
53			Container<Type>*	fContainer;
54			Type**				fItems;
55			int32*				fIndices;
56			int32				fToIndex;
57			int32				fCount;
58};
59
60
61template <class Type>
62MoveCommand<Type>::MoveCommand(Container<Type>* container,
63		Type** items,
64		int32 count,
65		int32 toIndex)
66	: Command(),
67	  fContainer(container),
68	  fItems(items),
69	  fIndices(count > 0 ? new (nothrow) int32[count] : NULL),
70	  fToIndex(toIndex),
71	  fCount(count)
72{
73	if (!fContainer || !fItems || !fIndices)
74		return;
75
76	// init original shape indices and
77	// adjust toIndex compensating for items that
78	// are removed before that index
79	int32 itemsBeforeIndex = 0;
80	for (int32 i = 0; i < fCount; i++) {
81		fIndices[i] = fContainer->IndexOf(fItems[i]);
82		if (fIndices[i] >= 0 && fIndices[i] < fToIndex)
83			itemsBeforeIndex++;
84	}
85	fToIndex -= itemsBeforeIndex;
86}
87
88
89template <class Type>
90MoveCommand<Type>::~MoveCommand()
91{
92	delete[] fItems;
93	delete[] fIndices;
94}
95
96
97template <class Type>
98status_t
99MoveCommand<Type>::InitCheck()
100{
101	if (!fContainer || !fItems || !fIndices)
102		return B_NO_INIT;
103
104	// analyse the move, don't return B_OK in case
105	// the container state does not change...
106
107	int32 index = fIndices[0];
108		// NOTE: fIndices == NULL if fCount < 1
109
110	if (index != fToIndex) {
111		// a change is guaranteed
112		return B_OK;
113	}
114
115	// the insertion index is the same as the index of the first
116	// moved item, a change only occures if the indices of the
117	// moved items is not contiguous
118	bool isContiguous = true;
119	for (int32 i = 1; i < fCount; i++) {
120		if (fIndices[i] != index + 1) {
121			isContiguous = false;
122			break;
123		}
124		index = fIndices[i];
125	}
126	if (isContiguous) {
127		// the container state will not change because of the move
128		return B_ERROR;
129	}
130
131	return B_OK;
132}
133
134
135template <class Type>
136status_t
137MoveCommand<Type>::Perform()
138{
139	status_t ret = B_OK;
140
141	// remove paths from container
142	for (int32 i = 0; i < fCount; i++) {
143		if (fItems[i] && !fContainer->RemoveItem(fItems[i])) {
144			ret = B_ERROR;
145			break;
146		}
147	}
148	if (ret < B_OK)
149		return ret;
150
151	// add paths to container at the insertion index
152	int32 index = fToIndex;
153	for (int32 i = 0; i < fCount; i++) {
154		if (fItems[i] && !fContainer->AddItem(fItems[i], index++)) {
155			ret = B_ERROR;
156			break;
157		}
158	}
159
160	return ret;
161}
162
163
164template <class Type>
165status_t
166MoveCommand<Type>::Undo()
167{
168	status_t ret = B_OK;
169
170	// remove paths from container
171	for (int32 i = 0; i < fCount; i++) {
172		if (fItems[i] && !fContainer->RemoveItem(fItems[i])) {
173			ret = B_ERROR;
174			break;
175		}
176	}
177	if (ret < B_OK)
178		return ret;
179
180	// add paths to container at remembered indices
181	for (int32 i = 0; i < fCount; i++) {
182		if (fItems[i] && !fContainer->AddItem(fItems[i], fIndices[i])) {
183			ret = B_ERROR;
184			break;
185		}
186	}
187
188	return ret;
189}
190
191
192template <class Type>
193void
194MoveCommand<Type>::GetName(BString& name)
195{
196	static BStringFormat format(B_TRANSLATE("Move {0, plural, "
197		"one{item} other{items}}"));
198	format.Format(name, fCount);
199}
200#endif // MOVE_COMMAND_H
201