wmi

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

inputbar.cpp (11583B)


      1 // Copyright (c) 2003 - 2009 Anselm R Garbe <anselm@garbe.us>
      2 // See LICENSE for license details.
      3 
      4 #include "inputbar.h"
      5 
      6 extern "C" {
      7 #include <assert.h>
      8 #include <stdlib.h>
      9 }
     10 
     11 #include "action.h"
     12 #include "actions.h"
     13 #include "binder.h"
     14 #include "kernel.h"
     15 #include "label.h"
     16 #include "logger.h"
     17 #include "monitor.h"
     18 #include "prompt.h"
     19 #include "theme.h"
     20 #include "workspace.h"
     21 #include "xcore.h"
     22 
     23 InputBar::InputBar(Monitor *monitor, Rectangle *rect)
     24     : Bar(monitor, rect)
     25 {
     26 
     27     LOGDEBUG("initializing input");
     28     currAction_ = 0;
     29     prompt_ = 0;
     30     promptCounter_ = 0;
     31     isArgument_ = false;
     32     partitionBegin_ = selected_ = entryBegin_;
     33     args_ = "";
     34     LOGDEBUG("creating input");
     35 }
     36 
     37 InputBar::~InputBar() {
     38 }
     39 
     40 void InputBar::selectPrevEntry() {
     41 
     42     if (entryBegin_ == entryEnd_) {
     43         // nothing to select, empty list
     44         return;
     45     }
     46 
     47     if (selected_ != partitionBegin_) {
     48         selected_--;
     49     }
     50     else {
     51 
     52         if (partitionBegin_ != entryBegin_) {
     53 
     54             selected_ = --partitionBegin_;
     55             partitionBegin_ = prevPartitions_.top();
     56             prevPartitions_.pop();
     57         }
     58         else {
     59             clearPrevPartitionsStack();
     60         }
     61     }
     62 }
     63 
     64 void InputBar::selectNextEntry() {
     65 
     66     if (entryBegin_ == entryEnd_) {
     67         // nothing to select, empty list
     68         return;
     69     }
     70 
     71     Sstring::iterator tmp = selected_;
     72     selected_++;
     73 
     74     if (selected_ == entryEnd_) {
     75         selected_ = partitionBegin_ = entryBegin_;
     76         clearPrevPartitionsStack();
     77         return;
     78     }
     79 
     80     if (tmp == partitionEnd_) {
     81         prevPartitions_.push(partitionBegin_);
     82         partitionBegin_ = selected_;
     83     }
     84 }
     85 
     86 void InputBar::illuminate() {
     87 
     88     if (!isVisible()) {
     89         return;
     90     }
     91 
     92     drawBorder();
     93     LOGDEBUG("input update");
     94 
     95     // draw background
     96     label_->setAlignment(Label::CENTER);
     97     label_->setText("");
     98     label_->setX(1);
     99     label_->setWidth(width() - 2);
    100     label_->update(theme_->BAR_BACKGROUND, theme_->BAR_TEXT,
    101                    theme_->BAR_SHINE, theme_->BAR_SHADOW);
    102 
    103     // draw prompt
    104     label_->setText(prompt_ ? prompt_->prompt() : "");
    105     label_->adjustWidth();
    106     label_->update(theme_->BAR_BACKGROUND, theme_->BAR_TEXT,
    107                    theme_->BAR_SHINE, theme_->BAR_SHADOW);
    108     label_->setX(label_->x() + label_->width());
    109 
    110     // draw text
    111     label_->setText(text_);
    112     label_->adjustWidth();
    113     label_->update(theme_->BAR_BACKGROUND, theme_->BAR_TEXT,
    114                    theme_->BAR_SHINE, theme_->BAR_SHADOW);
    115     label_->setX(label_->x() + label_->width());
    116 
    117     if (partitionBegin_ != entryBegin_) {
    118         label_->setText("<<");
    119         label_->adjustWidth();
    120         label_->update(theme_->BAR_BACKGROUND, theme_->BAR_TEXT,
    121                        theme_->BAR_SHINE, theme_->BAR_SHADOW);
    122         label_->setX(label_->x() + label_->width());
    123     }
    124 
    125     label_->setText(">>");
    126     unsigned int nextWidth = label_->adjustWidth();
    127 
    128     for (Sstring::iterator it = partitionBegin_; it != entryEnd_; it++)
    129     {
    130         string currStr = *it;
    131         label_->setText(currStr);
    132         label_->adjustWidth();
    133 
    134         // draw entry
    135         if (label_->x() + label_->width() < (width() - nextWidth)) {
    136 
    137             if (selected_ != it) {
    138                 label_->update(theme_->LABEL_BACKGROUND_NORMAL,
    139                                theme_->LABEL_TEXT_NORMAL,
    140                                theme_->LABEL_SHINE_NORMAL,
    141                                theme_->LABEL_SHADOW_NORMAL);
    142             }
    143             else {
    144                 label_->update(theme_->LABEL_BACKGROUND_FOCUSSED,
    145                                theme_->LABEL_TEXT_FOCUSSED,
    146                                theme_->LABEL_SHINE_FOCUSSED,
    147                                theme_->LABEL_SHADOW_FOCUSSED,
    148                                true, true);
    149             }
    150         }
    151         else {
    152             partitionEnd_ = --it;
    153             label_->setText(">>");
    154             label_->adjustWidth();
    155             label_->update(theme_->BAR_BACKGROUND, theme_->BAR_TEXT,
    156                            theme_->BAR_SHINE, theme_->BAR_SHADOW);
    157             break;
    158         }
    159         label_->setX(label_->x() + label_->width());
    160     }
    161 }
    162 
    163 string InputBar::text(bool onExecEnter) {
    164 
    165     if ((selected_ == entryBegin_) && (entryBegin_ == entryEnd_)) {
    166         return text_;
    167     }
    168     else {
    169         // If an input begins with '/' character we use also the prefix path
    170         if (onExecEnter && (text_.length() > 0) && (text_[0] == '/')) {
    171             text_ = text_.substr(0, text_.find_last_of('/') + 1);
    172             return text_ + *selected_;
    173         }
    174         return *selected_;
    175     }
    176 }
    177 
    178 void InputBar::clearPrevPartitionsStack() {
    179     while (!prevPartitions_.empty()) {
    180         prevPartitions_.pop();
    181     }
    182 }
    183 
    184 void InputBar::removeChar() {
    185 
    186     if (!isVisible() || !prompt_) {
    187         return;
    188     }
    189 
    190     int textLength = text_.length();
    191     if (textLength < 1) {
    192         return;
    193     }
    194 
    195     if (prompt_->toQuery() && names_.size()>0) {
    196         unsigned int numNames = names_.size();
    197         for (; (numNames == names_.size()) && (textLength >= 0);
    198                 textLength--)
    199         {
    200 #ifdef POSIX_REGEX
    201 		string s = Binder::instance()->popRegexPattern();
    202 		LOGDEBUG("popping from STACK: " + s);
    203 		queryText(s);
    204 		textLength=s.length();
    205 #else
    206             queryText(text_.substr(0, textLength));
    207 #endif
    208         }
    209     }
    210     else {
    211         queryText(text_.substr(0, textLength - 1));
    212     }
    213     illuminate();
    214 }
    215 
    216 void InputBar::queryText(string text) {
    217 
    218     if (!isVisible() || !prompt_) {
    219         return;
    220     }
    221 
    222     QueryNamesForPrefix toQuery = prompt_->toQuery();
    223     names_.clear();
    224 
    225     if (toQuery) {
    226         text_ = ((*Binder::instance()).*toQuery)(text, &names_);
    227         entryBegin_ = names_.begin();
    228         entryEnd_ = names_.end();;
    229         selected_ = partitionBegin_ = entryBegin_;
    230         clearPrevPartitionsStack();
    231     }
    232     else {
    233         partitionBegin_ = selected_ = entryBegin_;
    234         text_ = text;
    235     }
    236 }
    237 
    238 void InputBar::handleInput(XKeyEvent *event) {
    239 
    240     static const char *home = getenv("HOME");
    241     static bool isNext = false;
    242     char buffer[32];
    243     int count;
    244 
    245     if (!isVisible()) {
    246         return;
    247     }
    248     static KeySym sym;
    249     // patch contributed by Marcin Pawlik
    250     sym = XCORE->lookupNextKeySym(event, &count, buffer);
    251     if (IsFunctionKey(sym) || IsKeypadKey(sym) ||
    252             IsMiscFunctionKey(sym) || IsPFKey(sym) ||
    253             IsPrivateKeypadKey(sym))
    254     {
    255         return; // ignore
    256     }
    257 
    258     bool reset = false;
    259     if (event->state & ControlMask) {
    260         switch (sym) {
    261         case XK_H:
    262         case XK_h:
    263             sym = XK_BackSpace;
    264             break;
    265         case XK_J:
    266         case XK_j:
    267             sym = XK_Return;
    268             break;
    269         case XK_G:
    270         case XK_g:
    271             sym = XK_Escape;
    272             break;
    273         case XK_U:
    274         case XK_u:
    275             reset = true;
    276             break;
    277         }
    278     }
    279 
    280     switch (sym) {
    281     case XK_BackSpace:
    282         removeChar();
    283         break;
    284     case XK_Right:
    285         selectNextEntry();
    286         break;
    287     case XK_Left:
    288         selectPrevEntry();
    289         break;
    290     case XK_Return:
    291         handlePressedReturn();
    292         break;
    293     case XK_Escape:
    294         escape();
    295         break;
    296     case XK_Num_Lock:
    297         break;
    298     default:
    299         if (reset) {
    300             text_ = "";
    301             queryText(text_);
    302         }
    303         else if ((count == 1) && !iscntrl(buffer[0])) {
    304             buffer[count] = 0;
    305             string text = "";
    306             if (home && (buffer[0] == '~')) {
    307                 text = text_ + home;
    308             }
    309             else {
    310                 text = text_ + buffer;
    311             }
    312             queryText(text);
    313             text_ = text;
    314         }
    315         else if (sym == XK_Tab) {
    316             string text = text_;
    317             if (!isNext) {
    318                 queryText(text_);
    319             }
    320             isNext = (text == text_);
    321             if (isNext) {
    322                 selectNextEntry();
    323             }
    324         }
    325 #ifdef POSIX_REGEX
    326 	if (text_=="") {Binder::instance()->clearRegexPattern();}else{
    327 	if (!names_.empty()) Binder::instance()->pushRegexPattern(text_);}
    328 	
    329 #endif
    330         break;
    331     }
    332     if (sym != XK_Tab) {
    333         isNext = false;
    334     }
    335     illuminate();
    336 }
    337 
    338 void InputBar::escape() {
    339 
    340     promptCounter_ = 0;
    341     args_ = "";
    342     prompt_ = 0;
    343     currAction_ = 0;
    344 #ifdef POSIX_REGEX
    345     Binder::instance()->clearRegexPattern();
    346 #endif
    347     XCORE->ungrabKeyboard();
    348     hide();
    349 }
    350 
    351 void InputBar::handlePressedReturn() {
    352 
    353     string actionKey;
    354     bool isDirectInvocation = false;
    355 
    356     LOGDEBUG("fetching selection");
    357     actionKey = text();
    358     unsigned int pos = actionKey.find_first_of('+');
    359     if (!isArgument_ && (pos != string::npos)) {
    360         args_ = actionKey.substr(pos + 1);
    361         actionKey = actionKey.substr(0, pos);
    362         isDirectInvocation = true;
    363     }
    364     LOGDEBUG("selection fetched");
    365     if (isArgument_ || (actionKey != "") || isDirectInvocation) {
    366 
    367         if (!isArgument_ || isDirectInvocation)
    368         { // fetch action
    369             LOGDEBUG("fetching action: " + actionKey );
    370             currAction_ = Util::get(KERNEL->actionBindings(), actionKey);
    371         }
    372 
    373         if (currAction_) {
    374 
    375             if (!isArgument_ &&
    376                 (currAction_->promptsCount() > 0) && !isDirectInvocation)
    377             {
    378                 // set prompt of first argument
    379                 prompt_ = currAction_->prompt(0);
    380                 isArgument_ = true;
    381                 queryText("");
    382             }
    383             else if (isArgument_) {
    384 
    385                 // fetch args
    386                 if (args_.length() > 0) {
    387                     args_ += '+';
    388                 }
    389                 args_ += text((currAction_->getToPerform() == &Actions::execute) ||
    390                               (currAction_->getToPerform() == &Actions::executeTerm));
    391 
    392                 promptCounter_++;
    393                 if (promptCounter_ < currAction_->promptsCount()) {
    394                     prompt_ = currAction_->prompt(promptCounter_);
    395                 }
    396                 queryText(""); // keyboard is already grabbed
    397             }
    398             illuminate();
    399 
    400             if ((promptCounter_ == currAction_->promptsCount()) ||
    401                     isDirectInvocation)
    402             {
    403                 LOGDEBUG("going to perform action");
    404                 if (isArgument_ || isDirectInvocation) {
    405                     LOGDEBUG("got args: " + args_);
    406                     currAction_->setArgument((char *)args_.c_str());
    407                 }
    408                 // otherwise default invocation
    409                 currAction_->perform();
    410                 escape();
    411             }
    412             else {
    413                 return;
    414             }
    415         }
    416     }
    417 }
    418 
    419 void InputBar::runKey(Prompt *prompt) {
    420     if (!isVisible()) {
    421         show();
    422     }
    423     XCORE->grabKeyboard(window());
    424     currAction_ = 0;
    425     prompt_ = prompt;
    426     isArgument_ = false;
    427     queryText("");
    428     illuminate();
    429 }
    430 
    431 void InputBar::runArgument(Action *action) {
    432     if (!action->isValid()) {
    433         return;
    434     }
    435     if (!isVisible()) {
    436         show();
    437     }
    438     XCORE->grabKeyboard(window());
    439     currAction_ = action;
    440     // set prompt of first argument
    441     prompt_ = currAction_->prompt(0);
    442     isArgument_ = true;
    443     queryText("");
    444     illuminate();
    445 }
    446 
    447 void InputBar::handleMotionNotify(XMotionEvent *event) {
    448     // nothing todo
    449 }
    450 
    451 void InputBar::handleButtonPress(XButtonEvent *event) {
    452     escape();
    453 }
    454 
    455 void InputBar::handleButtonRelease(XButtonEvent *event) {
    456     // nothing todo
    457 }
    458 
    459 bool InputBar::isArgument() const {
    460     return isArgument_;
    461 }