1 /* 2 * Copyright 1999-2010 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Jeremy Friesner 7 */ 8 9 10 #include "ShortcutsSpec.h" 11 12 #include <ctype.h> 13 #include <stdio.h> 14 15 #include <Beep.h> 16 #include <Catalog.h> 17 #include <Directory.h> 18 #include <Locale.h> 19 #include <NodeInfo.h> 20 #include <Path.h> 21 #include <Region.h> 22 #include <Window.h> 23 24 #include "ColumnListView.h" 25 26 #include "BitFieldTesters.h" 27 #include "Colors.h" 28 #include "CommandActuators.h" 29 #include "MetaKeyStateMap.h" 30 #include "ParseCommandLine.h" 31 32 33 #define CLASS "ShortcutsSpec : " 34 35 #undef B_TRANSLATE_CONTEXT 36 #define B_TRANSLATE_CONTEXT "ShortcutsSpec" 37 38 const float _height = 20.0f; 39 40 static MetaKeyStateMap sMetaMaps[ShortcutsSpec::NUM_META_COLUMNS]; 41 42 static bool sFontCached = false; 43 static BFont sViewFont; 44 static float sFontHeight; 45 static BBitmap* sActuatorBitmaps[2]; 46 47 const char* ShortcutsSpec::sShiftName; 48 const char* ShortcutsSpec::sControlName; 49 const char* ShortcutsSpec::sOptionName; 50 const char* ShortcutsSpec::sCommandName; 51 52 53 #define ICON_BITMAP_RECT BRect(0.0f, 0.0f, 15.0f, 15.0f) 54 #define ICON_BITMAP_SPACE B_COLOR_8_BIT 55 56 57 // Returns the (pos)'th char in the string, or '\0' if (pos) if off the end of 58 // the string 59 static char 60 GetLetterAt(const char* str, int pos) 61 { 62 for (int i = 0; i < pos; i++) { 63 if (str[i] == '\0') 64 return '\0'; 65 } 66 return str[pos]; 67 } 68 69 70 // Setup the states in a standard manner for a pair of meta-keys. 71 static void 72 SetupStandardMap(MetaKeyStateMap& map, const char* name, uint32 both, 73 uint32 left, uint32 right) 74 { 75 map.SetInfo(name); 76 77 // In this state, neither key may be pressed. 78 map.AddState("(None)", new HasBitsFieldTester(0, both)); 79 80 // Here, either may be pressed. (Remember both is NOT a 2-bit chord, it's 81 // another bit entirely) 82 map.AddState("Either", new HasBitsFieldTester(both)); 83 84 // Here, only the left may be pressed 85 map.AddState("Left", new HasBitsFieldTester(left, right)); 86 87 // Here, only the right may be pressed 88 map.AddState("Right", new HasBitsFieldTester(right, left)); 89 90 // Here, both must be pressed. 91 map.AddState("Both", new HasBitsFieldTester(left | right)); 92 } 93 94 95 MetaKeyStateMap& 96 GetNthKeyMap(int which) 97 { 98 return sMetaMaps[which]; 99 } 100 101 102 static BBitmap* 103 MakeActuatorBitmap(bool lit) 104 { 105 BBitmap* map = new BBitmap(ICON_BITMAP_RECT, ICON_BITMAP_SPACE, true); 106 const rgb_color yellow = {255, 255, 0}; 107 const rgb_color red = {200, 200, 200}; 108 const rgb_color black = {0, 0, 0}; 109 const BPoint points[10] = { 110 BPoint(8, 0), BPoint(9.8, 5.8), BPoint(16, 5.8), 111 BPoint(11, 9.0), BPoint(13, 16), BPoint(8, 11), 112 BPoint(3, 16), BPoint(5, 9.0), BPoint(0, 5.8), 113 BPoint(6.2, 5.8) }; 114 115 BView* view = new BView(BRect(0, 0, 16, 16), NULL, B_FOLLOW_ALL_SIDES, 0L); 116 map->AddChild(view); 117 map->Lock(); 118 view->SetHighColor(B_TRANSPARENT_32_BIT); 119 view->FillRect(ICON_BITMAP_RECT); 120 view->SetHighColor(lit ? yellow : red); 121 view->FillPolygon(points, 10); 122 view->SetHighColor(black); 123 view->StrokePolygon(points, 10); 124 map->Unlock(); 125 map->RemoveChild(view); 126 delete view; 127 return map; 128 } 129 130 131 /*static*/ void 132 ShortcutsSpec::InitializeMetaMaps() 133 { 134 static bool metaMapsInitialized = false; 135 if (metaMapsInitialized) 136 return; 137 metaMapsInitialized = true; 138 139 _InitModifierNames(); 140 141 SetupStandardMap(sMetaMaps[ShortcutsSpec::SHIFT_COLUMN_INDEX], sShiftName, 142 B_SHIFT_KEY, B_LEFT_SHIFT_KEY, B_RIGHT_SHIFT_KEY); 143 144 SetupStandardMap(sMetaMaps[ShortcutsSpec::CONTROL_COLUMN_INDEX], 145 sControlName, B_CONTROL_KEY, B_LEFT_CONTROL_KEY, B_RIGHT_CONTROL_KEY); 146 147 SetupStandardMap(sMetaMaps[ShortcutsSpec::COMMAND_COLUMN_INDEX], 148 sCommandName, B_COMMAND_KEY, B_LEFT_COMMAND_KEY, B_RIGHT_COMMAND_KEY); 149 150 SetupStandardMap(sMetaMaps[ShortcutsSpec::OPTION_COLUMN_INDEX], sOptionName 151 , B_OPTION_KEY, B_LEFT_OPTION_KEY, B_RIGHT_OPTION_KEY); 152 153 sActuatorBitmaps[0] = MakeActuatorBitmap(false); 154 sActuatorBitmaps[1] = MakeActuatorBitmap(true); 155 } 156 157 158 ShortcutsSpec::ShortcutsSpec(const char* cmd) 159 : 160 CLVListItem(0, false, false, _height), 161 fCommand(NULL), 162 fTextOffset(0), 163 fBitmap(ICON_BITMAP_RECT, ICON_BITMAP_SPACE), 164 fLastBitmapName(NULL), 165 fBitmapValid(false), 166 fKey(0), 167 fCursorPtsValid(false) 168 { 169 for (int i = 0; i < NUM_META_COLUMNS; i++) 170 fMetaCellStateIndex[i] = 0; 171 SetCommand(cmd); 172 } 173 174 175 ShortcutsSpec::ShortcutsSpec(const ShortcutsSpec& from) 176 : 177 CLVListItem(0, false, false, _height), 178 fCommand(NULL), 179 fTextOffset(from.fTextOffset), 180 fBitmap(ICON_BITMAP_RECT, ICON_BITMAP_SPACE), 181 fLastBitmapName(NULL), 182 fBitmapValid(false), 183 fKey(from.fKey), 184 fCursorPtsValid(false) 185 { 186 for (int i = 0; i < NUM_META_COLUMNS; i++) 187 fMetaCellStateIndex[i] = from.fMetaCellStateIndex[i]; 188 189 SetCommand(from.fCommand); 190 SetSelectedColumn(from.GetSelectedColumn()); 191 } 192 193 194 ShortcutsSpec::ShortcutsSpec(BMessage* from) 195 : 196 CLVListItem(0, false, false, _height), 197 fCommand(NULL), 198 fTextOffset(0), 199 fBitmap(ICON_BITMAP_RECT, ICON_BITMAP_SPACE), 200 fLastBitmapName(NULL), 201 fBitmapValid(false), 202 fCursorPtsValid(false) 203 { 204 const char* temp; 205 if (from->FindString("command", &temp) != B_NO_ERROR) { 206 printf(CLASS); 207 printf(" Error, no command string in archive BMessage!\n"); 208 temp = ""; 209 } 210 211 SetCommand(temp); 212 213 if (from->FindInt32("key", (int32*) &fKey) != B_NO_ERROR) { 214 printf(CLASS); 215 printf(" Error, no key int32 in archive BMessage!\n"); 216 } 217 218 for (int i = 0; i < NUM_META_COLUMNS; i++) 219 if (from->FindInt32("mcidx", i, (int32*)&fMetaCellStateIndex[i]) 220 != B_NO_ERROR) { 221 printf(CLASS); 222 printf(" Error, no modifiers int32 in archive BMessage!\n"); 223 } 224 } 225 226 227 void 228 ShortcutsSpec::SetCommand(const char* command) 229 { 230 delete[] fCommand; // out with the old (if any)... 231 fCommandLen = strlen(command) + 1; 232 fCommandNul = fCommandLen - 1; 233 fCommand = new char[fCommandLen]; 234 strcpy(fCommand, command); 235 _UpdateIconBitmap(); 236 } 237 238 239 const char* 240 ShortcutsSpec::GetColumnName(int i) 241 { 242 return sMetaMaps[i].GetName(); 243 } 244 245 246 status_t 247 ShortcutsSpec::Archive(BMessage* into, bool deep) const 248 { 249 status_t ret = BArchivable::Archive(into, deep); 250 if (ret != B_NO_ERROR) 251 return ret; 252 253 into->AddString("class", "ShortcutsSpec"); 254 255 // These fields are for our prefs panel's benefit only 256 into->AddString("command", fCommand); 257 into->AddInt32("key", fKey); 258 259 // Assemble a BitFieldTester for the input_server add-on to use... 260 MinMatchFieldTester test(NUM_META_COLUMNS, false); 261 for (int i = 0; i < NUM_META_COLUMNS; i++) { 262 // for easy parsing by prefs applet on load-in 263 into->AddInt32("mcidx", fMetaCellStateIndex[i]); 264 test.AddSlave(sMetaMaps[i].GetNthStateTester(fMetaCellStateIndex[i])); 265 } 266 267 BMessage testerMsg; 268 ret = test.Archive(&testerMsg); 269 if (ret != B_NO_ERROR) 270 return ret; 271 272 into->AddMessage("modtester", &testerMsg); 273 274 // And also create a CommandActuator for the input_server add-on to execute 275 CommandActuator* act = CreateCommandActuator(fCommand); 276 BMessage actMsg; 277 ret = act->Archive(&actMsg); 278 if (ret != B_NO_ERROR) 279 return ret; 280 delete act; 281 282 into->AddMessage("act", &actMsg); 283 return ret; 284 } 285 286 287 static bool IsValidActuatorName(const char* c); 288 static bool 289 IsValidActuatorName(const char* c) 290 { 291 return (strcmp(c, B_TRANSLATE("InsertString")) == 0 292 || strcmp(c, B_TRANSLATE("MoveMouse")) == 0 293 || strcmp(c, B_TRANSLATE("MoveMouseTo")) == 0 294 || strcmp(c, B_TRANSLATE("MouseButton")) == 0 295 || strcmp(c, B_TRANSLATE("LaunchHandler")) == 0 296 || strcmp(c, B_TRANSLATE("Multi")) == 0 297 || strcmp(c, B_TRANSLATE("MouseDown")) == 0 298 || strcmp(c, B_TRANSLATE("MouseUp")) == 0 299 || strcmp(c, B_TRANSLATE("SendMessage")) == 0 300 || strcmp(c, B_TRANSLATE("Beep")) == 0); 301 } 302 303 304 BArchivable* 305 ShortcutsSpec::Instantiate(BMessage* from) 306 { 307 bool validateOK = false; 308 if (validate_instantiation(from, "ShortcutsSpec")) 309 validateOK = true; 310 else // test the old one. 311 if (validate_instantiation(from, "SpicyKeysSpec")) 312 validateOK = true; 313 314 if (!validateOK) 315 return NULL; 316 317 return new ShortcutsSpec(from); 318 } 319 320 321 ShortcutsSpec::~ShortcutsSpec() 322 { 323 delete[] fCommand; 324 delete[] fLastBitmapName; 325 } 326 327 328 void 329 ShortcutsSpec::_CacheViewFont(BView* owner) 330 { 331 if (sFontCached == false) { 332 sFontCached = true; 333 owner->GetFont(&sViewFont); 334 font_height fh; 335 sViewFont.GetHeight(&fh); 336 sFontHeight = fh.ascent - fh.descent; 337 } 338 } 339 340 341 void 342 ShortcutsSpec::DrawItemColumn(BView* owner, BRect item_column_rect, 343 int32 column_index, bool columnSelected, bool complete) 344 { 345 const float STRING_COLUMN_LEFT_MARGIN = 25.0f; // 16 for the icon,+9 empty 346 347 rgb_color color; 348 bool selected = IsSelected(); 349 if (selected) 350 color = columnSelected ? BeBackgroundGrey : BeListSelectGrey; 351 else 352 color = BeInactiveControlGrey; 353 owner->SetLowColor(color); 354 owner->SetDrawingMode(B_OP_COPY); 355 owner->SetHighColor(color); 356 owner->FillRect(item_column_rect); 357 358 const char* text = GetCellText(column_index); 359 360 if (text == NULL) 361 return; 362 363 float textWidth = sViewFont.StringWidth(text); 364 BPoint point; 365 rgb_color lowColor = color; 366 367 if (column_index == STRING_COLUMN_INDEX) { 368 // left justified 369 point.Set(item_column_rect.left + STRING_COLUMN_LEFT_MARGIN, 370 item_column_rect.top + fTextOffset); 371 372 item_column_rect.left = point.x; 373 // keep text from drawing into icon area 374 375 // scroll if too wide 376 float rectWidth = item_column_rect.Width() - STRING_COLUMN_LEFT_MARGIN; 377 float extra = textWidth - rectWidth; 378 if (extra > 0.0f) 379 point.x -= extra; 380 } else { 381 if ((column_index < NUM_META_COLUMNS) && (text[0] == '(')) 382 return; // don't draw for this ... 383 384 if ((column_index <= NUM_META_COLUMNS) && (text[0] == '\0')) 385 return; // don't draw for this ... 386 387 // centered 388 point.Set((item_column_rect.left + item_column_rect.right) / 2.0, 389 item_column_rect.top + fTextOffset); 390 _CacheViewFont(owner); 391 point.x -= textWidth / 2.0f; 392 } 393 394 BRegion Region; 395 Region.Include(item_column_rect); 396 owner->ConstrainClippingRegion(&Region); 397 if (column_index != STRING_COLUMN_INDEX) { 398 const float KEY_MARGIN = 3.0f; 399 const float CORNER_RADIUS = 3.0f; 400 _CacheViewFont(owner); 401 402 // How about I draw a nice "key" background for this one? 403 BRect textRect(point.x - KEY_MARGIN, (point.y-sFontHeight) - KEY_MARGIN 404 , point.x + textWidth + KEY_MARGIN - 2.0f, point.y + KEY_MARGIN); 405 406 if (column_index == KEY_COLUMN_INDEX) 407 lowColor = ReallyLightPurple; 408 else 409 lowColor = LightYellow; 410 411 owner->SetHighColor(lowColor); 412 owner->FillRoundRect(textRect, CORNER_RADIUS, CORNER_RADIUS); 413 owner->SetHighColor(Black); 414 owner->StrokeRoundRect(textRect, CORNER_RADIUS, CORNER_RADIUS); 415 } 416 417 owner->SetHighColor(Black); 418 owner->SetLowColor(lowColor); 419 owner->DrawString(text, point); 420 // with a cursor at the end if highlighted 421 if (column_index == STRING_COLUMN_INDEX) { 422 // Draw cursor 423 if ((columnSelected) && (selected)) { 424 point.x += textWidth; 425 point.y += (fTextOffset / 4.0f); 426 427 BPoint pt2 = point; 428 pt2.y -= fTextOffset; 429 owner->StrokeLine(point, pt2); 430 431 fCursorPt1 = point; 432 fCursorPt2 = pt2; 433 fCursorPtsValid = true; 434 } 435 436 BRegion bitmapRegion; 437 item_column_rect.left -= (STRING_COLUMN_LEFT_MARGIN - 4.0f); 438 item_column_rect.right = item_column_rect.left + 16.0f; 439 item_column_rect.top += 3.0f; 440 item_column_rect.bottom = item_column_rect.top + 16.0f; 441 442 bitmapRegion.Include(item_column_rect); 443 owner->ConstrainClippingRegion(&bitmapRegion); 444 owner->SetDrawingMode(B_OP_OVER); 445 446 if ((fCommand != NULL) && (fCommand[0] == '*')) 447 owner->DrawBitmap(sActuatorBitmaps[fBitmapValid ? 1 : 0], 448 ICON_BITMAP_RECT, item_column_rect); 449 else 450 // Draw icon, if any 451 if (fBitmapValid) 452 owner->DrawBitmap(&fBitmap, ICON_BITMAP_RECT, 453 item_column_rect); 454 } 455 456 owner->SetDrawingMode(B_OP_COPY); 457 owner->ConstrainClippingRegion(NULL); 458 } 459 460 461 void 462 ShortcutsSpec::Update(BView* owner, const BFont* font) 463 { 464 CLVListItem::Update(owner, font); 465 font_height FontAttributes; 466 be_plain_font->GetHeight(&FontAttributes); 467 float fontHeight = ceil(FontAttributes.ascent) + 468 ceil(FontAttributes.descent); 469 fTextOffset = ceil(FontAttributes.ascent) + (Height() - fontHeight) / 2.0; 470 } 471 472 473 const char* 474 ShortcutsSpec::GetCellText(int whichColumn) const 475 { 476 const char* temp = ""; // default 477 switch(whichColumn) { 478 case KEY_COLUMN_INDEX: 479 { 480 if ((fKey > 0) && (fKey <= 0xFF)) { 481 temp = GetKeyName(fKey); 482 if (temp == NULL) 483 temp = ""; 484 } else if (fKey > 0xFF) { 485 sprintf(fScratch, "#%lx", fKey); 486 return fScratch; 487 } 488 break; 489 } 490 491 case STRING_COLUMN_INDEX: 492 temp = fCommand; 493 break; 494 495 default: 496 if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS)) 497 temp = sMetaMaps[whichColumn].GetNthStateDesc( 498 fMetaCellStateIndex[whichColumn]); 499 break; 500 } 501 return temp; 502 } 503 504 505 bool 506 ShortcutsSpec::ProcessColumnMouseClick(int whichColumn) 507 { 508 if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS)) { 509 // same as hitting space for these columns: cycle entry 510 const char temp = B_SPACE; 511 512 // 3rd arg isn't correct but it isn't read for this case anyway 513 return ProcessColumnKeyStroke(whichColumn, &temp, 0); 514 } 515 return false; 516 } 517 518 519 bool 520 ShortcutsSpec::ProcessColumnTextString(int whichColumn, const char* string) 521 { 522 switch(whichColumn) { 523 case STRING_COLUMN_INDEX: 524 SetCommand(string); 525 return true; 526 break; 527 528 case KEY_COLUMN_INDEX: 529 { 530 fKey = FindKeyCode(string); 531 return true; 532 break; 533 } 534 535 default: 536 return ProcessColumnKeyStroke(whichColumn, string, 0); 537 } 538 } 539 540 541 bool 542 ShortcutsSpec::_AttemptTabCompletion() 543 { 544 bool ret = false; 545 546 int32 argc; 547 char** argv = ParseArgvFromString(fCommand, argc); 548 if (argc > 0) { 549 // Try to complete the path partially expressed in the last argument! 550 char* arg = argv[argc - 1]; 551 char* fileFragment = strrchr(arg, '/'); 552 if (fileFragment) { 553 const char* directoryName = (fileFragment == arg) ? "/" : arg; 554 *fileFragment = '\0'; 555 fileFragment++; 556 int fragLen = strlen(fileFragment); 557 558 BDirectory dir(directoryName); 559 if (dir.InitCheck() == B_NO_ERROR) { 560 BEntry nextEnt; 561 BPath nextPath; 562 BList matchList; 563 int maxEntryLen = 0; 564 565 // Read in all the files in the directory whose names start 566 // with our fragment. 567 while (dir.GetNextEntry(&nextEnt) == B_NO_ERROR) { 568 if (nextEnt.GetPath(&nextPath) == B_NO_ERROR) { 569 char* filePath = strrchr(nextPath.Path(), '/') + 1; 570 if (strncmp(filePath, fileFragment, fragLen) == 0) { 571 int len = strlen(filePath); 572 if (len > maxEntryLen) 573 maxEntryLen = len; 574 char* newStr = new char[len + 1]; 575 strcpy(newStr, filePath); 576 matchList.AddItem(newStr); 577 } 578 } 579 } 580 581 // Now slowly extend our keyword to its full length, counting 582 // numbers of matches at each step. If the match list length 583 // is 1, we can use that whole entry. If it's greater than one 584 // , we can use just the match length. 585 int matchLen = matchList.CountItems(); 586 if (matchLen > 0) { 587 int i; 588 BString result(fileFragment); 589 for (i = fragLen; i < maxEntryLen; i++) { 590 // See if all the matching entries have the same letter 591 // in the next position... if so, we can go farther. 592 char commonLetter = '\0'; 593 for (int j = 0; j < matchLen; j++) { 594 char nextLetter = GetLetterAt( 595 (char*)matchList.ItemAt(j), i); 596 if (commonLetter == '\0') 597 commonLetter = nextLetter; 598 599 if ((commonLetter != '\0') 600 && (commonLetter != nextLetter)) { 601 commonLetter = '\0';// failed; 602 beep(); 603 break; 604 } 605 } 606 if (commonLetter == '\0') 607 break; 608 else 609 result.Append(commonLetter, 1); 610 } 611 612 // Free all the strings we allocated 613 for (int k = 0; k < matchLen; k++) 614 delete [] ((char*)matchList.ItemAt(k)); 615 616 DoStandardEscapes(result); 617 618 BString wholeLine; 619 for (int l = 0; l < argc - 1; l++) { 620 wholeLine += argv[l]; 621 wholeLine += " "; 622 } 623 624 BString file(directoryName); 625 DoStandardEscapes(file); 626 627 if (directoryName[strlen(directoryName) - 1] != '/') 628 file += "/"; 629 630 file += result; 631 632 // Remove any trailing slash... 633 const char* fileStr = file.String(); 634 if (fileStr[strlen(fileStr)-1] == '/') 635 file.RemoveLast("/"); 636 637 // And re-append it iff the file is a dir. 638 BDirectory testFileAsDir(file.String()); 639 if ((strcmp(file.String(), "/") != 0) 640 && (testFileAsDir.InitCheck() == B_NO_ERROR)) 641 file.Append("/"); 642 643 wholeLine += file; 644 645 SetCommand(wholeLine.String()); 646 ret = true; 647 } 648 } 649 *(fileFragment - 1) = '/'; 650 } 651 } 652 FreeArgv(argv); 653 return ret; 654 } 655 656 657 bool 658 ShortcutsSpec::ProcessColumnKeyStroke(int whichColumn, const char* bytes, 659 int32 key) 660 { 661 bool ret = false; 662 switch(whichColumn) { 663 case KEY_COLUMN_INDEX: 664 if (key > -1) { 665 if ((int32)fKey != key) { 666 fKey = key; 667 ret = true; 668 } 669 } 670 break; 671 672 case STRING_COLUMN_INDEX: 673 { 674 switch(bytes[0]) { 675 case B_BACKSPACE: 676 case B_DELETE: 677 if (fCommandNul > 0) { 678 // trim a char off the string 679 fCommand[fCommandNul - 1] = '\0'; 680 fCommandNul--; // note new nul position 681 ret = true; 682 _UpdateIconBitmap(); 683 } 684 break; 685 686 case B_TAB: 687 if (_AttemptTabCompletion()) { 688 _UpdateIconBitmap(); 689 ret = true; 690 } else 691 beep(); 692 break; 693 694 default: 695 { 696 uint32 newCharLen = strlen(bytes); 697 if ((newCharLen > 0) && (bytes[0] >= ' ')) { 698 bool reAllocString = false; 699 // Make sure we have enough room in our command string 700 // to add these chars... 701 while (fCommandLen - fCommandNul <= newCharLen) { 702 reAllocString = true; 703 // enough for a while... 704 fCommandLen = (fCommandLen + 10) * 2; 705 } 706 707 if (reAllocString) { 708 char* temp = new char[fCommandLen]; 709 strcpy(temp, fCommand); 710 delete [] fCommand; 711 fCommand = temp; 712 // fCommandNul is still valid since it's an offset 713 // and the string length is the same for now 714 } 715 716 // Here we should be guaranteed enough room. 717 strncat(fCommand, bytes, fCommandLen); 718 fCommandNul += newCharLen; 719 ret = true; 720 _UpdateIconBitmap(); 721 } 722 } 723 } 724 break; 725 } 726 727 default: 728 if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS)) { 729 MetaKeyStateMap * map = &sMetaMaps[whichColumn]; 730 int curState = fMetaCellStateIndex[whichColumn]; 731 int origState = curState; 732 int numStates = map->GetNumStates(); 733 734 switch(bytes[0]) 735 { 736 case B_RETURN: 737 // cycle to the previous state 738 curState = (curState + numStates - 1) % numStates; 739 break; 740 741 case B_SPACE: 742 // cycle to the next state 743 curState = (curState + 1) % numStates; 744 break; 745 746 default: 747 { 748 // Go to the state starting with the given letter, if 749 // any 750 char letter = bytes[0]; 751 if (islower(letter)) 752 letter = toupper(letter); // convert to upper case 753 754 if ((letter == B_BACKSPACE) || (letter == B_DELETE)) 755 letter = '('; 756 // so space bar will blank out an entry 757 758 for (int i = 0; i < numStates; i++) { 759 const char* desc = map->GetNthStateDesc(i); 760 761 if (desc) { 762 if (desc[0] == letter) { 763 curState = i; 764 break; 765 } 766 } else 767 printf(B_TRANSLATE("Error, NULL state description?\n")); 768 } 769 break; 770 } 771 } 772 fMetaCellStateIndex[whichColumn] = curState; 773 774 if (curState != origState) 775 ret = true; 776 } 777 break; 778 } 779 780 return ret; 781 } 782 783 784 int 785 ShortcutsSpec::MyCompare(const CLVListItem* a_Item1, const CLVListItem* a_Item2, 786 int32 KeyColumn) 787 { 788 ShortcutsSpec* left = (ShortcutsSpec*) a_Item1; 789 ShortcutsSpec* right = (ShortcutsSpec*) a_Item2; 790 791 int ret = strcmp(left->GetCellText(KeyColumn), 792 right->GetCellText(KeyColumn)); 793 return (ret > 0) ? 1 : ((ret == 0) ? 0 : -1); 794 } 795 796 797 void 798 ShortcutsSpec::Pulse(BView* owner) 799 { 800 if ((fCursorPtsValid)&&(owner->Window()->IsActive())) { 801 rgb_color prevColor = owner->HighColor(); 802 rgb_color backgroundColor = (GetSelectedColumn() == 803 STRING_COLUMN_INDEX) ? BeBackgroundGrey : BeListSelectGrey; 804 rgb_color barColor = ((GetSelectedColumn() == STRING_COLUMN_INDEX) 805 && ((system_time() % 1000000) > 500000)) ? Black : backgroundColor; 806 owner->SetHighColor(barColor); 807 owner->StrokeLine(fCursorPt1, fCursorPt2); 808 owner->SetHighColor(prevColor); 809 } 810 } 811 812 813 void 814 ShortcutsSpec::_UpdateIconBitmap() 815 { 816 BString firstWord = ParseArgvZeroFromString(fCommand); 817 818 // Only need to change if the first word has changed... 819 if (fLastBitmapName == NULL || firstWord.Length() == 0 820 || firstWord.Compare(fLastBitmapName)) { 821 if (firstWord.ByteAt(0) == '*') 822 fBitmapValid = IsValidActuatorName(&firstWord.String()[1]); 823 else { 824 fBitmapValid = false; // default till we prove otherwise! 825 826 if (firstWord.Length() > 0) { 827 delete [] fLastBitmapName; 828 fLastBitmapName = new char[firstWord.Length() + 1]; 829 strcpy(fLastBitmapName, firstWord.String()); 830 831 BEntry progEntry(fLastBitmapName, true); 832 if ((progEntry.InitCheck() == B_NO_ERROR) 833 && (progEntry.Exists())) { 834 BNode progNode(&progEntry); 835 if (progNode.InitCheck() == B_NO_ERROR) { 836 BNodeInfo progNodeInfo(&progNode); 837 if ((progNodeInfo.InitCheck() == B_NO_ERROR) 838 && (progNodeInfo.GetTrackerIcon(&fBitmap, B_MINI_ICON) 839 == B_NO_ERROR)) { 840 fBitmapValid = fBitmap.IsValid(); 841 } 842 } 843 } 844 } 845 } 846 } 847 } 848 849 850 /*static*/ void 851 ShortcutsSpec::_InitModifierNames() 852 { 853 sShiftName = B_TRANSLATE_COMMENT("Shift", 854 "Name for modifier on keyboard"); 855 sControlName = B_TRANSLATE_COMMENT("Control", 856 "Name for modifier on keyboard"); 857 // TODO: Wrapping in __INTEL__ define probably won't work to extract catkeys? 858 #if __INTEL__ 859 sOptionName = B_TRANSLATE_COMMENT("Window", 860 "Name for modifier on keyboard"); 861 sCommandName = B_TRANSLATE_COMMENT("Alt", 862 "Name for modifier on keyboard"); 863 #else 864 sOptionName = B_TRANSLATE_COMMENT("Option", 865 "Name for modifier on keyboard"); 866 sCommandName = B_TRANSLATE_COMMENT("Command", 867 "Name for modifier on keyboard"); 868 #endif 869 } 870 871