wmi

git clone git://oldgit.suckless.org/wmi/
Log | Files | Refs | LICENSE

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 }