xref: /haiku/src/apps/webpositive/BookmarkBar.cpp (revision 0c66734e4da3cdd60a0552014b0f46275a358796)
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 					if (fItemsMap[inode] == NULL) {
83 						entry_ref ref;
84 						const char *name;
85 
86 						message->FindInt32("device", &ref.device);
87 						message->FindInt64("to directory", &ref.directory);
88 						message->FindString("name", &name);
89 						ref.set_name(name);
90 
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 							break;
105 						}
106 					}
107 
108 					// fall through: the item was moved from here to
109 					// elsewhere, remove it from the bar.
110 				}
111 				case B_ENTRY_REMOVED:
112 				{
113 					IconMenuItem* item = fItemsMap[inode];
114 					RemoveItem(item);
115 					fOverflowMenu->RemoveItem(item);
116 					fItemsMap.erase(inode);
117 					delete item;
118 				}
119 			}
120 			return;
121 		}
122 	}
123 
124 	BMenuBar::MessageReceived(message);
125 }
126 
127 
128 void
129 BookmarkBar::FrameResized(float width, float height)
130 {
131 	int32 count = CountItems() - 1;
132 		// We don't touch the "more" menu
133 	int32 i = 0;
134 	float rightmost = 0.f;
135 	while (i < count) {
136 		BMenuItem* item = ItemAt(i);
137 		BRect frame = item->Frame();
138 		if (frame.right > width - 32)
139 			break;
140 		rightmost = frame.right;
141 		i++;
142 	}
143 
144 	if (i == count) {
145 		// See if we can move some items from the "more" menu in the remaining
146 		// space.
147 		BMenuItem* extraItem = fOverflowMenu->ItemAt(0);
148 		while (extraItem != NULL) {
149 			BRect frame = extraItem->Frame();
150 			if (frame.Width() + rightmost > width - 32)
151 				break;
152 
153 			AddItem(fOverflowMenu->RemoveItem((int32)0), i);
154 			i++;
155 
156 			extraItem = fOverflowMenu->ItemAt(0);
157 		}
158 	} else {
159 		// Remove any overflowing item and move them to the "more" menu.
160 		for (int j = i; j < count; j++)
161 			fOverflowMenu->AddItem(RemoveItem(j));
162 	}
163 }
164 
165 
166 BSize
167 BookmarkBar::MinSize()
168 {
169 	BSize size = BMenuBar::MinSize();
170 
171 	// We only need space to show the "more" button.
172 	size.width = 32;
173 
174 	return size;
175 }
176 
177 
178 // #pragma mark - private methods
179 
180 
181 void
182 BookmarkBar::_AddItem(ino_t inode, BEntry* entry)
183 {
184 	char name[B_FILE_NAME_LENGTH];
185 	entry->GetName(name);
186 
187 	// make sure the item doesn't already exists
188 	if (fItemsMap[inode] != NULL)
189 		return;
190 
191 	entry_ref ref;
192 	entry->GetRef(&ref);
193 
194 	IconMenuItem* item = NULL;
195 
196 	if (entry->IsDirectory()) {
197 		BNavMenu* menu = new BNavMenu(name, B_REFS_RECEIVED, Window());
198 		menu->SetNavDir(&ref);
199 		item = new IconMenuItem(menu, NULL,
200 			"application/x-vnd.Be-directory", B_MINI_ICON);
201 
202 	} else {
203 		BNode node(entry);
204 		BNodeInfo info(&node);
205 
206 		BMessage* message = new BMessage(B_REFS_RECEIVED);
207 		message->AddRef("refs", &ref);
208 		item = new IconMenuItem(name, message, &info, B_MINI_ICON);
209 	}
210 
211 	BMenuBar::AddItem(item, CountItems() - 1);
212 	fItemsMap[inode] = item;
213 
214 	// Move the item to the "more" menu if it overflows.
215 	BRect r = Bounds();
216 	FrameResized(r.Width(), r.Height());
217 }
218