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 }