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