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 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 // We need enough vertical space to show bookmark icons. 181 if (size.height < 20) 182 size.height = 20; 183 184 return size; 185 } 186 187 188 // #pragma mark - private methods 189 190 191 void 192 BookmarkBar::_AddItem(ino_t inode, BEntry* entry) 193 { 194 char name[B_FILE_NAME_LENGTH]; 195 entry->GetName(name); 196 197 // make sure the item doesn't already exists 198 if (fItemsMap[inode] != NULL) 199 return; 200 201 entry_ref ref; 202 entry->GetRef(&ref); 203 204 IconMenuItem* item = NULL; 205 206 if (entry->IsDirectory()) { 207 BNavMenu* menu = new BNavMenu(name, B_REFS_RECEIVED, Window()); 208 menu->SetNavDir(&ref); 209 item = new IconMenuItem(menu, NULL, 210 "application/x-vnd.Be-directory", B_MINI_ICON); 211 212 } else { 213 BNode node(entry); 214 BNodeInfo info(&node); 215 216 BMessage* message = new BMessage(B_REFS_RECEIVED); 217 message->AddRef("refs", &ref); 218 item = new IconMenuItem(name, message, &info, B_MINI_ICON); 219 } 220 221 int32 count = CountItems(); 222 223 BMenuBar::AddItem(item, count - 1); 224 fItemsMap[inode] = item; 225 226 // Move the item to the "more" menu if it overflows. 227 BRect r = Bounds(); 228 FrameResized(r.Width(), r.Height()); 229 } 230