xref: /haiku/src/apps/webpositive/BookmarkBar.cpp (revision 87f4776937505e3014251c9c3434be78ae29d7d0)
1 /*
2  * Copyright 2014, Adrien Destugues <pulkomandy@pulkomandy.tk>.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "BookmarkBar.h"
8 
9 #include <Directory.h>
10 #include <Entry.h>
11 #include <IconMenuItem.h>
12 #include <Messenger.h>
13 #include <Window.h>
14 
15 #include "NavMenu.h"
16 
17 #include <stdio.h>
18 
19 
20 BookmarkBar::BookmarkBar(const char* title, BHandler* target,
21 	const entry_ref* navDir)
22 	:
23 	BMenuBar(title)
24 {
25 	SetFlags(Flags() | B_FRAME_EVENTS);
26 	BEntry(navDir).GetNodeRef(&fNodeRef);
27 
28 	fOverflowMenu = new BMenu(B_UTF8_ELLIPSIS);
29 	AddItem(fOverflowMenu);
30 }
31 
32 
33 BookmarkBar::~BookmarkBar()
34 {
35 	stop_watching(BMessenger(this));
36 }
37 
38 
39 void
40 BookmarkBar::AttachedToWindow()
41 {
42 	BMenuBar::AttachedToWindow();
43 	watch_node(&fNodeRef, B_WATCH_DIRECTORY, BMessenger(this));
44 
45 	// Enumerate initial directory content
46 	BDirectory dir(&fNodeRef);
47 	BEntry bookmark;
48 	while (dir.GetNextEntry(&bookmark, true) == B_OK) {
49 		node_ref ref;
50 		if (bookmark.GetNodeRef(&ref) == B_OK)
51 			_AddItem(ref.node, &bookmark);
52 	}
53 }
54 
55 
56 void
57 BookmarkBar::MessageReceived(BMessage* message)
58 {
59 	switch (message->what) {
60 		case B_NODE_MONITOR:
61 		{
62 			int32 opcode = message->FindInt32("opcode");
63 			ino_t inode = message->FindInt64("node");
64 			switch (opcode) {
65 				case B_ENTRY_CREATED:
66 				{
67 					entry_ref ref;
68 					const char* name;
69 
70 					message->FindInt32("device", &ref.device);
71 					message->FindInt64("directory", &ref.directory);
72 					message->FindString("name", &name);
73 					ref.set_name(name);
74 
75 					BEntry entry(&ref, true);
76 					if (entry.InitCheck() == B_OK)
77 						_AddItem(inode, &entry);
78 					break;
79 				}
80 				case B_ENTRY_MOVED:
81 				{
82 					entry_ref ref;
83 					const char *name;
84 
85 					message->FindInt32("device", &ref.device);
86 					message->FindInt64("to directory", &ref.directory);
87 					message->FindString("name", &name);
88 					ref.set_name(name);
89 
90 					if (fItemsMap[inode] == NULL) {
91 						BEntry entry(&ref, true);
92 						_AddItem(inode, &entry);
93 						break;
94 					} else {
95 						// Existing item. Check if it's a rename or a move
96 						// to some other folder.
97 						ino_t from, to;
98 						message->FindInt64("to directory", &to);
99 						message->FindInt64("from directory", &from);
100 						if (from == to) {
101 							const char *name;
102 							if (message->FindString("name", &name) == B_OK)
103 								fItemsMap[inode]->SetLabel(name);
104 
105 							BMessage* itemMessage = new BMessage(
106 								B_REFS_RECEIVED);
107 							itemMessage->AddRef("refs", &ref);
108 							fItemsMap[inode]->SetMessage(itemMessage);
109 
110 							break;
111 						}
112 					}
113 
114 					// fall through: the item was moved from here to
115 					// elsewhere, remove it from the bar.
116 				}
117 				case B_ENTRY_REMOVED:
118 				{
119 					IconMenuItem* item = fItemsMap[inode];
120 					RemoveItem(item);
121 					fOverflowMenu->RemoveItem(item);
122 					fItemsMap.erase(inode);
123 					delete item;
124 				}
125 			}
126 			return;
127 		}
128 	}
129 
130 	BMenuBar::MessageReceived(message);
131 }
132 
133 
134 void
135 BookmarkBar::FrameResized(float width, float height)
136 {
137 	int32 count = CountItems() - 1;
138 		// We don't touch the "more" menu
139 	int32 i = 0;
140 	float rightmost = 0.f;
141 	while (i < count) {
142 		BMenuItem* item = ItemAt(i);
143 		BRect frame = item->Frame();
144 		if (frame.right > width - 32)
145 			break;
146 		rightmost = frame.right;
147 		i++;
148 	}
149 
150 	if (i == count) {
151 		// See if we can move some items from the "more" menu in the remaining
152 		// space.
153 		BMenuItem* extraItem = fOverflowMenu->ItemAt(0);
154 		while (extraItem != NULL) {
155 			BRect frame = extraItem->Frame();
156 			if (frame.Width() + rightmost > width - 32)
157 				break;
158 
159 			AddItem(fOverflowMenu->RemoveItem((int32)0), i);
160 			i++;
161 
162 			extraItem = fOverflowMenu->ItemAt(0);
163 		}
164 	} else {
165 		// Remove any overflowing item and move them to the "more" menu.
166 		for (int j = i; j < count; j++)
167 			fOverflowMenu->AddItem(RemoveItem(j));
168 	}
169 
170 	BMenuBar::FrameResized(width, height);
171 }
172 
173 
174 BSize
175 BookmarkBar::MinSize()
176 {
177 	BSize size = BMenuBar::MinSize();
178 
179 	// We only need space to show the "more" button.
180 	size.width = 32;
181 
182 	// We need enough vertical space to show bookmark icons.
183 	if (size.height < 20)
184 		size.height = 20;
185 
186 	return size;
187 }
188 
189 
190 // #pragma mark - private methods
191 
192 
193 void
194 BookmarkBar::_AddItem(ino_t inode, BEntry* entry)
195 {
196 	char name[B_FILE_NAME_LENGTH];
197 	entry->GetName(name);
198 
199 	// make sure the item doesn't already exists
200 	if (fItemsMap[inode] != NULL)
201 		return;
202 
203 	entry_ref ref;
204 	entry->GetRef(&ref);
205 
206 	IconMenuItem* item = NULL;
207 
208 	if (entry->IsDirectory()) {
209 		BNavMenu* menu = new BNavMenu(name, B_REFS_RECEIVED, Window());
210 		menu->SetNavDir(&ref);
211 		item = new IconMenuItem(menu, NULL,
212 			"application/x-vnd.Be-directory", B_MINI_ICON);
213 
214 	} else {
215 		BNode node(entry);
216 		BNodeInfo info(&node);
217 
218 		BMessage* message = new BMessage(B_REFS_RECEIVED);
219 		message->AddRef("refs", &ref);
220 		item = new IconMenuItem(name, message, &info, B_MINI_ICON);
221 	}
222 
223 	int32 count = CountItems();
224 
225 	BMenuBar::AddItem(item, count - 1);
226 	fItemsMap[inode] = item;
227 
228 	// Move the item to the "more" menu if it overflows.
229 	BRect r = Bounds();
230 	FrameResized(r.Width(), r.Height());
231 }
232