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