1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
23CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered
30trademarks of Be Incorporated in the United States and other countries. Other
31brand product names are registered trademarks or trademarks of their
32respective holders. All rights reserved.
33*/
34
35
36#include "GroupedMenu.h"
37
38#include <stdlib.h>
39#include <string.h>
40
41
42using namespace BPrivate;
43
44
45TMenuItemGroup::TMenuItemGroup(const char* name)
46	:
47	fMenu(NULL),
48	fFirstItemIndex(-1),
49	fItemsTotal(0),
50	fHasSeparator(false)
51{
52	if (name != NULL && name[0] != '\0')
53		fName = strdup(name);
54	else
55		fName = NULL;
56}
57
58
59TMenuItemGroup::~TMenuItemGroup()
60{
61	free((char*)fName);
62
63	if (fMenu == NULL) {
64		BMenuItem* item;
65		while ((item = RemoveItem((int32)0)) != NULL)
66			delete item;
67	}
68}
69
70
71bool
72TMenuItemGroup::AddItem(BMenuItem* item)
73{
74	if (!fList.AddItem(item))
75		return false;
76
77	if (fMenu)
78		fMenu->AddGroupItem(this, item, fList.IndexOf(item));
79
80	fItemsTotal++;
81	return true;
82}
83
84
85bool
86TMenuItemGroup::AddItem(BMenuItem* item, int32 atIndex)
87{
88	if (!fList.AddItem(item, atIndex))
89		return false;
90
91	if (fMenu)
92		fMenu->AddGroupItem(this, item, atIndex);
93
94	fItemsTotal++;
95	return true;
96}
97
98
99bool
100TMenuItemGroup::AddItem(BMenu* menu)
101{
102	BMenuItem* item = new BMenuItem(menu);
103	if (item == NULL)
104		return false;
105
106	if (!AddItem(item)) {
107		delete item;
108		return false;
109	}
110
111	return true;
112}
113
114
115bool
116TMenuItemGroup::AddItem(BMenu* menu, int32 atIndex)
117{
118	BMenuItem* item = new BMenuItem(menu);
119	if (item == NULL)
120		return false;
121
122	if (!AddItem(item, atIndex)) {
123		delete item;
124		return false;
125	}
126
127	return true;
128}
129
130
131bool
132TMenuItemGroup::RemoveItem(BMenuItem* item)
133{
134	if (fMenu)
135		fMenu->RemoveGroupItem(this, item);
136
137	return fList.RemoveItem(item);
138}
139
140
141bool
142TMenuItemGroup::RemoveItem(BMenu* menu)
143{
144	BMenuItem* item = menu->Superitem();
145	if (item == NULL)
146		return false;
147
148	return RemoveItem(item);
149}
150
151
152BMenuItem*
153TMenuItemGroup::RemoveItem(int32 index)
154{
155	BMenuItem* item = ItemAt(index);
156	if (item == NULL)
157		return NULL;
158
159	if (RemoveItem(item))
160		return item;
161
162	return NULL;
163}
164
165
166BMenuItem*
167TMenuItemGroup::ItemAt(int32 index)
168{
169	return static_cast<BMenuItem*>(fList.ItemAt(index));
170}
171
172
173int32
174TMenuItemGroup::CountItems()
175{
176	return fList.CountItems();
177}
178
179
180void
181TMenuItemGroup::Separated(bool separated)
182{
183	if (separated == fHasSeparator)
184		return;
185
186	fHasSeparator = separated;
187
188	if (separated)
189		fItemsTotal++;
190	else
191		fItemsTotal--;
192}
193
194
195bool
196TMenuItemGroup::HasSeparator()
197{
198	return fHasSeparator;
199}
200
201
202//	#pragma mark -
203
204
205TGroupedMenu::TGroupedMenu(const char* name)
206	: BMenu(name)
207{
208}
209
210
211TGroupedMenu::~TGroupedMenu()
212{
213	TMenuItemGroup* group;
214	while ((group = static_cast<TMenuItemGroup*>(fGroups.RemoveItem((int32)0)))
215			!= NULL) {
216		delete group;
217	}
218}
219
220
221bool
222TGroupedMenu::AddGroup(TMenuItemGroup* group)
223{
224	if (!fGroups.AddItem(group))
225		return false;
226
227	group->fMenu = this;
228
229	for (int32 i = 0; i < group->CountItems(); i++) {
230		AddGroupItem(group, group->ItemAt(i), i);
231	}
232
233	return true;
234}
235
236
237bool
238TGroupedMenu::AddGroup(TMenuItemGroup* group, int32 atIndex)
239{
240	if (!fGroups.AddItem(group, atIndex))
241		return false;
242
243	group->fMenu = this;
244
245	for (int32 i = 0; i < group->CountItems(); i++) {
246		AddGroupItem(group, group->ItemAt(i), i);
247	}
248
249	return true;
250}
251
252
253bool
254TGroupedMenu::RemoveGroup(TMenuItemGroup* group)
255{
256	if (group->HasSeparator()) {
257		delete RemoveItem(group->fFirstItemIndex);
258		group->Separated(false);
259	}
260
261	group->fMenu = NULL;
262	group->fFirstItemIndex = -1;
263
264	for (int32 i = 0; i < group->CountItems(); i++) {
265		RemoveItem(group->ItemAt(i));
266	}
267
268	return fGroups.RemoveItem(group);
269}
270
271
272TMenuItemGroup*
273TGroupedMenu::GroupAt(int32 index)
274{
275	return static_cast<TMenuItemGroup*>(fGroups.ItemAt(index));
276}
277
278
279int32
280TGroupedMenu::CountGroups()
281{
282	return fGroups.CountItems();
283}
284
285
286void
287TGroupedMenu::AddGroupItem(TMenuItemGroup* group, BMenuItem* item,
288	int32 atIndex)
289{
290	int32 groupIndex = fGroups.IndexOf(group);
291	bool addSeparator = false;
292
293	if (group->fFirstItemIndex == -1) {
294		// find new home for this group
295		if (groupIndex > 0) {
296			// add this group after an existing one
297			TMenuItemGroup* previous = GroupAt(groupIndex - 1);
298			group->fFirstItemIndex = previous->fFirstItemIndex
299				+ previous->fItemsTotal;
300			addSeparator = true;
301		} else {
302			// this is the first group
303			TMenuItemGroup* successor = GroupAt(groupIndex + 1);
304			if (successor != NULL) {
305				group->fFirstItemIndex = successor->fFirstItemIndex;
306				if (successor->fHasSeparator) {
307					// we'll need one as well
308					addSeparator = true;
309				}
310			} else {
311				group->fFirstItemIndex = CountItems();
312				if (group->fFirstItemIndex > 0)
313					addSeparator = true;
314			}
315		}
316
317		if (addSeparator) {
318			AddItem(new BSeparatorItem(), group->fFirstItemIndex);
319			group->Separated(true);
320		}
321	}
322
323	// insert item for real
324
325	AddItem(item,
326		atIndex + group->fFirstItemIndex + (group->HasSeparator() ? 1 : 0));
327
328	// move the groups after this one
329
330	for (int32 i = groupIndex + 1; i < CountGroups(); i++) {
331		group = GroupAt(i);
332		group->fFirstItemIndex += addSeparator ? 2 : 1;
333	}
334}
335
336
337void
338TGroupedMenu::RemoveGroupItem(TMenuItemGroup* group, BMenuItem* item)
339{
340	int32 groupIndex = fGroups.IndexOf(group);
341	bool removedSeparator = false;
342
343	if (group->CountItems() == 1) {
344		// this is the last item
345		if (group->HasSeparator()) {
346			RemoveItem(group->fFirstItemIndex);
347			group->Separated(false);
348			removedSeparator = true;
349		}
350	}
351
352	// insert item for real
353
354	RemoveItem(item);
355
356	// move the groups after this one
357
358	for (int32 i = groupIndex + 1; i < CountGroups(); i++) {
359		group = GroupAt(i);
360		group->fFirstItemIndex -= removedSeparator ? 2 : 1;
361	}
362}
363