menu.cpp (11204B)
1 // Copyright (c) 2003 - 2009 Anselm R Garbe <anselm@garbe.us> 2 // See LICENSE for license details. 3 4 #include "menu.h" 5 6 #include <sstream> 7 8 #include "action.h" 9 #include "clientbar.h" 10 #include "cursors.h" 11 #include "draw.h" 12 #include "font.h" 13 #include "kernel.h" 14 #include "label.h" 15 #include "logger.h" 16 #include "monitor.h" 17 #include "theme.h" 18 #include "util.h" 19 #include "workspace.h" 20 #include "xcore.h" 21 #include "xfont.h" 22 23 /// Item class implementation 24 25 Item::Item(string name, void *data, Type type) { 26 data_ = data; 27 type_ = type; 28 29 switch (type_) { 30 case WORKSPACE: 31 { 32 Workspace *workspace = (Workspace *)data; 33 Monitor *monitor = workspace->attached(); 34 if (workspace->clients()->size() 35 || monitor->detachedClients()->size() 36 || monitor->stickyClients()->size()) 37 { 38 addItem(new Item(name, 0, ROOTITEM)); 39 addItem(new Item("", 0, SEPARATOR)); 40 name_ = name + " >>"; 41 } 42 else { 43 name_ = name; 44 } 45 if (workspace == monitor->focused()) { 46 name_ = "* " + name_; 47 } 48 else { 49 name_ = " " + name_; 50 } 51 } 52 break; 53 case DCLIENT: 54 name_ = "+ " + name; 55 break; 56 case ROOTITEM: 57 name_ = "<< " + name; 58 break; 59 case SEPARATOR: 60 name_ = " -----------------------"; 61 break; 62 default: 63 name_ = " " + name; 64 break; 65 } 66 } 67 68 Item::~Item() { 69 // clean up subitems 70 while (items_.size()) { 71 Item *item = items_.front(); 72 items_.erase(items_.begin()); 73 delete item; 74 } 75 } 76 77 LItem *Item::items() { 78 return &items_; 79 } 80 81 void Item::addItem(Item *item) { 82 item->setParent(this); 83 items_.push_back(item); 84 } 85 86 void Item::removeItem(Item *item) { 87 item->setParent(0); 88 items_.remove(item); 89 } 90 91 Item::Type Item::type() const { 92 return type_; 93 } 94 95 string Item::name() const { 96 return name_; 97 } 98 99 unsigned int Item::size() { 100 return items_.size(); 101 } 102 103 Item *Item::parent() const { 104 return parent_; 105 } 106 107 void Item::setParent(Item *parent) { 108 parent_ = parent; 109 } 110 111 void *Item::data() const { 112 return data_; 113 } 114 115 /// Menu class implementation 116 117 Menu::Menu(Monitor *monitor) 118 : Widget(monitor, new Rectangle(0, 0, 1, 1)) 119 { 120 theme_ = this->monitor()->theme(); 121 root_ = subRoot_ = selected_ = 0; 122 123 label_ = new Label(this->monitor(), window(), Label::LEFT, gc()); 124 label_->setX(1); 125 label_->setY(1); 126 label_->setWidth(1); 127 label_->setHeight(this->monitor()->font()->height() + 2); 128 129 initActions(); 130 } 131 132 Menu::~Menu() { 133 134 } 135 136 void Menu::initActions() { 137 138 MBindings *actionBindings = KERNEL->actionBindings(); 139 string actions = Util::get(KERNEL->commonSettings(), "menu.actions"); 140 141 string actionName = ""; 142 bool proceed = actions.size(); 143 for (string::iterator it = actions.begin(); proceed; it++) { 144 145 if ((*it != ',') && (it != actions.end())) { 146 actionName += *it; 147 } 148 else { 149 Action *action = Util::get(actionBindings, actionName); 150 if (action) { 151 actions_.push_back(action); 152 } 153 else { 154 LOGWARN("unknown action '" + actionName + "' in menu setup"); 155 } 156 actionName = ""; 157 } 158 proceed = it != actions.end(); 159 } 160 161 } 162 163 void Menu::updateItemTree() { 164 165 if (root_) { 166 delete root_; 167 } 168 root_ = new Item("", 0, Item::ROOT); 169 subRoot_ = root_; 170 171 // now build up the item tree 172 for (LAction::iterator it = actions_.begin(); it != actions_.end(); it++) { 173 Action *action = *it; 174 if (action->isValid()) { 175 root_->addItem(new Item(action->id(), action, Item::ACTION)); 176 } 177 } 178 if (root_->items()->size()) { 179 root_->addItem(new Item("", 0, Item::SEPARATOR)); 180 } 181 182 // now add all workspaces 183 for (LWorkspace::iterator it = monitor()->begin(); 184 it != monitor()->end(); it++) 185 { 186 Workspace *workspace = *it; 187 Item *wsItem = new Item(workspace->name(), workspace, Item::WORKSPACE); 188 LClient *clients = workspace->clients(); 189 for (LClient::iterator it = clients->begin(); it != clients->end(); it++) { 190 Client *client = *it; 191 if (client->mode() != Client::STICKY) { 192 wsItem->addItem(new Item(client->name(), client, Item::CLIENT)); 193 } 194 } 195 CClient *stickyClients = monitor()->stickyClients(); 196 for (LClient::iterator it = stickyClients->begin(); 197 it != stickyClients->end(); it++) 198 { 199 Client *client = *it; 200 wsItem->addItem(new Item(client->name(), client, Item::CLIENT)); 201 } 202 CClient *detachedClients = monitor()->detachedClients(); 203 if (detachedClients->size()) { 204 if (workspace->clients()->size() || clients->size()) { 205 wsItem->addItem(new Item("", 0, Item::SEPARATOR)); 206 } 207 for (LClient::iterator it = detachedClients->begin(); 208 it != detachedClients->end(); it++) 209 { 210 Client *client = *it; 211 wsItem->addItem(new Item(client->name(), client, Item::DCLIENT)); 212 } 213 } 214 root_->addItem(wsItem); 215 } 216 } 217 218 void Menu::show() { 219 KERNEL->setMenuMode(true); 220 isVisible_ = true; 221 XCORE->showRaised(window()); 222 updateItemTree(); 223 manage(); 224 } 225 226 void Menu::hide() { 227 KERNEL->setMenuMode(false); 228 isVisible_ = false; 229 XCORE->hide(window()); 230 } 231 232 void Menu::illuminate() { 233 234 Rectangle rect(*this); 235 rect.setX(0); 236 rect.setY(0); 237 LOGDEBUG("drawing solid background"); 238 // draw solid background 239 XCORE->raise(window()); 240 XCORE->setForeground(gc(), theme_->BAR_BACKGROUND); 241 XCORE->fillRectangle(window(), gc(), &rect); 242 LItem *items = subRoot_->items(); 243 244 unsigned int offsetY = 1; 245 unsigned int i = 0; 246 for (LItem::iterator it = items->begin(); it != items->end(); it++) { 247 Item *item = *it; 248 label_->setY(offsetY + i * label_->height()); 249 label_->setText(item->name()); 250 if ((item == selected_) && (item->type() != Item::SEPARATOR)) { 251 label_->update(theme_->LABEL_BACKGROUND_FOCUSSED, 252 theme_->LABEL_TEXT_FOCUSSED, 253 theme_->LABEL_SHINE_FOCUSSED, 254 theme_->LABEL_SHADOW_FOCUSSED, 255 true, true); 256 } 257 else { 258 label_->update(theme_->LABEL_BACKGROUND_NORMAL, 259 theme_->LABEL_TEXT_NORMAL, 260 theme_->LABEL_SHINE_NORMAL, 261 theme_->LABEL_SHADOW_NORMAL); 262 } 263 i++; 264 } 265 266 Draw::drawRectBorder(window(), gc(), &rect, 267 theme_->BAR_SHINE, theme_->BAR_SHADOW); 268 } 269 270 void Menu::manage() { 271 272 if (!isVisible()) { 273 return; 274 } 275 276 unsigned int maxWidth = 0; 277 LItem *items = subRoot_->items(); 278 for (LItem::iterator it = items->begin(); it != items->end(); it++) { 279 Item *item = *it; 280 label_->setText(item->name()); 281 if (label_->textWidth() > maxWidth) { 282 maxWidth = label_->textWidth() + 10; 283 } 284 } 285 label_->setWidth(maxWidth); 286 setWidth(maxWidth + 2); 287 setHeight(items->size() * label_->height() + 2); 288 289 if (monitor()->clientBar()->y()) { 290 setY(monitor()->height() - monitor()->clientBar()->height() - height()); 291 } 292 else { 293 setY(monitor()->clientBar()->height()); 294 } 295 296 // time to illuminate menu 297 resize(); 298 illuminate(); 299 } 300 301 void Menu::handleButtonPress(XButtonEvent *event) { 302 XCORE->raise(window()); 303 if (selected_) { 304 switch (selected_->type()) { 305 case Item::WORKSPACE: 306 { 307 Workspace *workspace = (Workspace *)selected_->data(); 308 if (event->button == Button1) { 309 if (selected_->items()->size()) { 310 subRoot_ = selected_; 311 manage(); 312 } 313 else { // focus workspace 314 monitor()->focus(workspace); 315 escape(); 316 } 317 } 318 else if (event->button == Button3) { 319 monitor()->focus(workspace); 320 updateItemTree(); 321 manage(); 322 } 323 } 324 break; 325 case Item::ROOTITEM: 326 subRoot_ = subRoot_->parent(); 327 manage(); 328 break; 329 case Item::ACTION: 330 { 331 Action *action = (Action *)selected_->data(); 332 if (action->isValid()) { 333 action->perform(); 334 escape(); 335 } 336 } 337 break; 338 case Item::CLIENT: 339 { 340 Workspace *workspace = (Workspace *)subRoot_->data(); 341 Client *client = (Client *)selected_->data(); 342 if (event->button == Button3) { 343 monitor()->detachClient(client); 344 if (monitor()->focused() != workspace) { 345 monitor()->attachLastClient(); 346 } 347 } 348 else { 349 monitor()->focus(workspace); 350 workspace->focus(client); 351 if (event->button == Button2) { 352 XCORE->warpPointer(client->clientWindow(), 353 client->width() / 2, 354 client->height() / 2); 355 } 356 } 357 escape(); 358 } 359 break; 360 case Item::DCLIENT: 361 { 362 Client *client = (Client *)selected_->data(); 363 monitor()->attachDetachedClient((Workspace *)subRoot_->data(), 364 client); 365 if (event->button == Button2) { 366 XCORE->warpPointer(client->clientWindow(), 367 client->x() + client->width() / 2, 368 client->y() + client->height() / 2); 369 } 370 escape(); 371 } 372 break; 373 default: 374 break; 375 } 376 } 377 } 378 379 void Menu::escape() { 380 monitor()->clientBar()->toggleMenuMode(); 381 } 382 383 void Menu::handleMotionNotify(XMotionEvent *event) { 384 385 unsigned int selItem = event->y / label_->height(); 386 ostringstream oss; 387 oss << "eventY=" << event->y << " selItem=" << selItem; 388 LOGDEBUG(oss.str()); 389 390 unsigned int i = 0; 391 LItem *items = subRoot_->items(); 392 for (LItem::iterator it = items->begin(); it != items->end(); it++) { 393 Item *item = *it; 394 if (selItem == i) { 395 if (item == selected_) { 396 LOGDEBUG("no redraw"); 397 return; 398 } 399 selected_ = item; 400 illuminate(); 401 return; 402 } 403 i++; 404 } 405 selected_ = 0; 406 }