xref: /haiku/src/kits/interface/Layout.cpp (revision e277f0be5755a37e30f098deb6fb7542ac850a47)
1 /*
2  * Copyright 2010, Haiku Inc.
3  * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <Layout.h>
9 
10 #include <syslog.h>
11 #include <new>
12 
13 #include <Message.h>
14 #include <View.h>
15 
16 #include "ViewLayoutItem.h"
17 
18 
19 using std::nothrow;
20 
21 
22 namespace {
23 	const char* const kLayoutItemField = "BLayout:items";
24 }
25 
26 
27 BLayout::BLayout()
28 	:
29 	fView(NULL),
30 	fItems(20)
31 {
32 }
33 
34 
35 BLayout::BLayout(BMessage* from)
36 	:
37 	BArchivable(BUnarchiver::PrepareArchive(from)),
38 	fView(NULL),
39 	fItems(20)
40 {
41 	BUnarchiver unarchiver(from);
42 
43 	int32 i = 0;
44 	while (unarchiver.EnsureUnarchived(kLayoutItemField, i++) == B_OK)
45 		;
46 }
47 
48 
49 BLayout::~BLayout()
50 {
51 	// this deletes all items
52 	SetView(NULL);
53 }
54 
55 
56 BView*
57 BLayout::View() const
58 {
59 	return fView;
60 }
61 
62 
63 BLayoutItem*
64 BLayout::AddView(BView* child)
65 {
66 	return AddView(-1, child);
67 }
68 
69 
70 BLayoutItem*
71 BLayout::AddView(int32 index, BView* child)
72 {
73 	if (BViewLayoutItem* item = new(nothrow) BViewLayoutItem(child)) {
74 		if (AddItem(index, item))
75 			return item;
76 		delete item;
77 	}
78 	return NULL;
79 }
80 
81 
82 bool
83 BLayout::AddItem(BLayoutItem* item)
84 {
85 	return AddItem(-1, item);
86 }
87 
88 
89 bool
90 BLayout::AddItem(int32 index, BLayoutItem* item)
91 {
92 	if (!fView || !item || fItems.HasItem(item))
93 		return false;
94 
95 	// if the item refers to a BView, we make sure it is added to the parent
96 	// view
97 	bool addedView = false;
98 	BView* view = item->View();
99 	if (view && view->fParent != fView
100 		&& !(addedView = fView->_AddChild(view, NULL)))
101 		return false;
102 
103 	// validate the index
104 	if (index < 0 || index > fItems.CountItems())
105 		index = fItems.CountItems();
106 
107 	if (fItems.AddItem(item, index) && ItemAdded(item, index)) {
108 		item->SetLayout(this);
109 		InvalidateLayout();
110 		return true;
111 	} else {
112 		// this check is necessary so that if an addition somewhere other
113 		// than the end of the list fails, we don't remove the wrong item
114 		if (fItems.ItemAt(index) == item)
115 			fItems.RemoveItem(index);
116 		if (addedView)
117 			view->_RemoveSelf();
118 		return false;
119 	}
120 }
121 
122 
123 bool
124 BLayout::RemoveView(BView* child)
125 {
126 	bool removed = false;
127 
128 	// a view can have any number of layout items - we need to remove them all
129 	for (int32 i = fItems.CountItems(); i-- > 0;) {
130 		BLayoutItem* item = ItemAt(i);
131 
132 		if (item->View() != child)
133 			continue;
134 
135 		RemoveItem(i);
136 		removed = true;
137 		delete item;
138 	}
139 
140 	return removed;
141 }
142 
143 
144 bool
145 BLayout::RemoveItem(BLayoutItem* item)
146 {
147 	int32 index = IndexOfItem(item);
148 	return (index >= 0 ? RemoveItem(index) : false);
149 }
150 
151 
152 BLayoutItem*
153 BLayout::RemoveItem(int32 index)
154 {
155 	if (index < 0 || index >= fItems.CountItems())
156 		return NULL;
157 
158 	BLayoutItem* item = (BLayoutItem*)fItems.RemoveItem(index);
159 
160 	// if the item refers to a BView, we make sure, it is removed from the
161 	// parent view
162 	BView* view = item->View();
163 	if (view && view->fParent == fView)
164 		view->_RemoveSelf();
165 
166 	item->SetLayout(NULL);
167 	ItemRemoved(item, index);
168 	InvalidateLayout();
169 
170 	return item;
171 }
172 
173 
174 BLayoutItem*
175 BLayout::ItemAt(int32 index) const
176 {
177 	return (BLayoutItem*)fItems.ItemAt(index);
178 }
179 
180 
181 int32
182 BLayout::CountItems() const
183 {
184 	return fItems.CountItems();
185 }
186 
187 
188 int32
189 BLayout::IndexOfItem(const BLayoutItem* item) const
190 {
191 	return fItems.IndexOf(item);
192 }
193 
194 
195 int32
196 BLayout::IndexOfView(BView* child) const
197 {
198 	int itemCount = fItems.CountItems();
199 	for (int32 i = 0; i < itemCount; i++) {
200 		BLayoutItem* item = (BLayoutItem*)fItems.ItemAt(i);
201 		if (dynamic_cast<BViewLayoutItem*>(item) && item->View() == child)
202 			return i;
203 	}
204 
205 	return -1;
206 }
207 
208 
209 void
210 BLayout::InvalidateLayout()
211 {
212 	if (fView)
213 		fView->InvalidateLayout();
214 }
215 
216 
217 status_t
218 BLayout::Archive(BMessage* into, bool deep) const
219 {
220 	BArchiver archiver(into);
221 	status_t err = BArchivable::Archive(into, deep);
222 
223 	if (deep) {
224 		int32 count = CountItems();
225 		for (int32 i = 0; i < count && err == B_OK; i++) {
226 			BLayoutItem* item = ItemAt(i);
227 			err = archiver.AddArchivable(kLayoutItemField, item, deep);
228 
229 			if (err == B_OK) {
230 				err = ItemArchived(into, item, i);
231 				if (err != B_OK)
232 					syslog(LOG_ERR, "ItemArchived() failed at index: %d.", i);
233 			}
234 		}
235 	}
236 
237 	return archiver.Finish(err);
238 }
239 
240 
241 status_t
242 BLayout::AllUnarchived(const BMessage* from)
243 {
244 	BUnarchiver unarchiver(from);
245 	status_t err = BArchivable::AllUnarchived(from);
246 	if (err != B_OK)
247 		return err;
248 
249 	int32 itemCount;
250 	unarchiver.ArchiveMessage()->GetInfo(kLayoutItemField, NULL, &itemCount);
251 	for (int32 i = 0; i < itemCount && err == B_OK; i++) {
252 		BLayoutItem* item;
253 		err = unarchiver.FindObject(kLayoutItemField,
254 			i, BUnarchiver::B_DONT_ASSUME_OWNERSHIP, item);
255 		if (err != B_OK)
256 			return err;
257 
258 		if (!fItems.AddItem(item, i) || !ItemAdded(item, i)) {
259 			fItems.RemoveItem(i);
260 			return B_ERROR;
261 		}
262 
263 		err = ItemUnarchived(from, item, i);
264 		if (err != B_OK) {
265 			fItems.RemoveItem(i);
266 			ItemRemoved(item, i);
267 			return err;
268 		}
269 
270 		item->SetLayout(this);
271 		unarchiver.AssumeOwnership(item);
272 	}
273 
274 	InvalidateLayout();
275 	return err;
276 }
277 
278 
279 status_t
280 BLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const
281 {
282 	return B_OK;
283 }
284 
285 
286 status_t
287 BLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item, int32 index)
288 {
289 	return B_OK;
290 }
291 
292 
293 bool
294 BLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
295 {
296 	return true;
297 }
298 
299 
300 void
301 BLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
302 {
303 }
304 
305 
306 void
307 BLayout::SetView(BView* view)
308 {
309 	if (view != fView) {
310 		fView = NULL;
311 
312 		// remove and delete all items
313 		for (int32 i = CountItems() - 1; i >= 0; i--)
314 			delete RemoveItem(i);
315 
316 		fView = view;
317 
318 		InvalidateLayout();
319 	}
320 }
321