xref: /haiku/src/apps/webpositive/BookmarkBar.cpp (revision a629567a9001547736cfe892cdf992be16868fed)
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) == 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);
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);
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 
171 
172 BSize
173 BookmarkBar::MinSize()
174 {
175 	BSize size = BMenuBar::MinSize();
176 
177 	// We only need space to show the "more" button.
178 	size.width = 32;
179 
180 	return size;
181 }
182 
183 
184 // #pragma mark - private methods
185 
186 
187 void
188 BookmarkBar::_AddItem(ino_t inode, BEntry* entry)
189 {
190 	char name[B_FILE_NAME_LENGTH];
191 	entry->GetName(name);
192 
193 	// make sure the item doesn't already exists
194 	if (fItemsMap[inode] != NULL)
195 		return;
196 
197 	entry_ref ref;
198 	entry->GetRef(&ref);
199 
200 	IconMenuItem* item = NULL;
201 
202 	if (entry->IsDirectory()) {
203 		BNavMenu* menu = new BNavMenu(name, B_REFS_RECEIVED, Window());
204 		menu->SetNavDir(&ref);
205 		item = new IconMenuItem(menu, NULL,
206 			"application/x-vnd.Be-directory", B_MINI_ICON);
207 
208 	} else {
209 		BNode node(entry);
210 		BNodeInfo info(&node);
211 
212 		BMessage* message = new BMessage(B_REFS_RECEIVED);
213 		message->AddRef("refs", &ref);
214 		item = new IconMenuItem(name, message, &info, B_MINI_ICON);
215 	}
216 
217 	BMenuBar::AddItem(item, CountItems() - 1);
218 	fItemsMap[inode] = item;
219 
220 	// Move the item to the "more" menu if it overflows.
221 	BRect r = Bounds();
222 	FrameResized(r.Width(), r.Height());
223 }
224