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_TRANSLATION_CONTEXT 36 #define B_TRANSLATION_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_RGBA32 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 _CacheViewFont(owner); 364 // Ensure that sViewFont is configured before using it to calculate 365 // widths. The lack of this call was causing the initial display of 366 // columns to be incorrect, with a "jump" as all the columns correct 367 // themselves upon the first column resize. 368 369 float textWidth = sViewFont.StringWidth(text); 370 BPoint point; 371 rgb_color lowColor = color; 372 373 if (column_index == STRING_COLUMN_INDEX) { 374 // left justified 375 point.Set(item_column_rect.left + STRING_COLUMN_LEFT_MARGIN, 376 item_column_rect.top + fTextOffset); 377 378 item_column_rect.left = point.x; 379 // keep text from drawing into icon area 380 381 // scroll if too wide 382 float rectWidth = item_column_rect.Width() - STRING_COLUMN_LEFT_MARGIN; 383 float extra = textWidth - rectWidth; 384 if (extra > 0.0f) 385 point.x -= extra; 386 } else { 387 if ((column_index < NUM_META_COLUMNS) && (text[0] == '(')) 388 return; // don't draw for this ... 389 390 if ((column_index <= NUM_META_COLUMNS) && (text[0] == '\0')) 391 return; // don't draw for this ... 392 393 // centered 394 point.Set((item_column_rect.left + item_column_rect.right) / 2.0, 395 item_column_rect.top + fTextOffset); 396 _CacheViewFont(owner); 397 point.x -= textWidth / 2.0f; 398 } 399 400 BRegion Region; 401 Region.Include(item_column_rect); 402 owner->ConstrainClippingRegion(&Region); 403 if (column_index != STRING_COLUMN_INDEX) { 404 const float KEY_MARGIN = 3.0f; 405 const float CORNER_RADIUS = 3.0f; 406 _CacheViewFont(owner); 407 408 // How about I draw a nice "key" background for this one? 409 BRect textRect(point.x - KEY_MARGIN, (point.y-sFontHeight) - KEY_MARGIN 410 , point.x + textWidth + KEY_MARGIN - 2.0f, point.y + KEY_MARGIN); 411 412 if (column_index == KEY_COLUMN_INDEX) 413 lowColor = ReallyLightPurple; 414 else 415 lowColor = LightYellow; 416 417 owner->SetHighColor(lowColor); 418 owner->FillRoundRect(textRect, CORNER_RADIUS, CORNER_RADIUS); 419 owner->SetHighColor(Black); 420 owner->StrokeRoundRect(textRect, CORNER_RADIUS, CORNER_RADIUS); 421 } 422 423 owner->SetHighColor(Black); 424 owner->SetLowColor(lowColor); 425 owner->DrawString(text, point); 426 // with a cursor at the end if highlighted 427 if (column_index == STRING_COLUMN_INDEX) { 428 // Draw cursor 429 if ((columnSelected) && (selected)) { 430 point.x += textWidth; 431 point.y += (fTextOffset / 4.0f); 432 433 BPoint pt2 = point; 434 pt2.y -= fTextOffset; 435 owner->StrokeLine(point, pt2); 436 437 fCursorPt1 = point; 438 fCursorPt2 = pt2; 439 fCursorPtsValid = true; 440 } 441 442 BRegion bitmapRegion; 443 item_column_rect.left -= (STRING_COLUMN_LEFT_MARGIN - 4.0f); 444 item_column_rect.right = item_column_rect.left + 16.0f; 445 item_column_rect.top += 3.0f; 446 item_column_rect.bottom = item_column_rect.top + 16.0f; 447 448 bitmapRegion.Include(item_column_rect); 449 owner->ConstrainClippingRegion(&bitmapRegion); 450 owner->SetDrawingMode(B_OP_ALPHA); 451 452 if ((fCommand != NULL) && (fCommand[0] == '*')) 453 owner->DrawBitmap(sActuatorBitmaps[fBitmapValid ? 1 : 0], 454 ICON_BITMAP_RECT, item_column_rect); 455 else 456 // Draw icon, if any 457 if (fBitmapValid) 458 owner->DrawBitmap(&fBitmap, ICON_BITMAP_RECT, 459 item_column_rect); 460 } 461 462 owner->SetDrawingMode(B_OP_COPY); 463 owner->ConstrainClippingRegion(NULL); 464 } 465 466 467 void 468 ShortcutsSpec::Update(BView* owner, const BFont* font) 469 { 470 CLVListItem::Update(owner, font); 471 font_height FontAttributes; 472 be_plain_font->GetHeight(&FontAttributes); 473 float fontHeight = ceil(FontAttributes.ascent) + 474 ceil(FontAttributes.descent); 475 fTextOffset = ceil(FontAttributes.ascent) + (Height() - fontHeight) / 2.0; 476 } 477 478 479 const char* 480 ShortcutsSpec::GetCellText(int whichColumn) const 481 { 482 const char* temp = ""; // default 483 switch(whichColumn) { 484 case KEY_COLUMN_INDEX: 485 { 486 if ((fKey > 0) && (fKey <= 0xFF)) { 487 temp = GetKeyName(fKey); 488 if (temp == NULL) 489 temp = ""; 490 } else if (fKey > 0xFF) { 491 sprintf(fScratch, "#%lx", fKey); 492 return fScratch; 493 } 494 break; 495 } 496 497 case STRING_COLUMN_INDEX: 498 temp = fCommand; 499 break; 500 501 default: 502 if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS)) 503 temp = sMetaMaps[whichColumn].GetNthStateDesc( 504 fMetaCellStateIndex[whichColumn]); 505 break; 506 } 507 return temp; 508 } 509 510 511 bool 512 ShortcutsSpec::ProcessColumnMouseClick(int whichColumn) 513 { 514 if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS)) { 515 // same as hitting space for these columns: cycle entry 516 const char temp = B_SPACE; 517 518 // 3rd arg isn't correct but it isn't read for this case anyway 519 return ProcessColumnKeyStroke(whichColumn, &temp, 0); 520 } 521 return false; 522 } 523 524 525 bool 526 ShortcutsSpec::ProcessColumnTextString(int whichColumn, const char* string) 527 { 528 switch(whichColumn) { 529 case STRING_COLUMN_INDEX: 530 SetCommand(string); 531 return true; 532 break; 533 534 case KEY_COLUMN_INDEX: 535 { 536 fKey = FindKeyCode(string); 537 return true; 538 break; 539 } 540 541 default: 542 return ProcessColumnKeyStroke(whichColumn, string, 0); 543 } 544 } 545 546 547 bool 548 ShortcutsSpec::_AttemptTabCompletion() 549 { 550 bool ret = false; 551 552 int32 argc; 553 char** argv = ParseArgvFromString(fCommand, argc); 554 if (argc > 0) { 555 // Try to complete the path partially expressed in the last argument! 556 char* arg = argv[argc - 1]; 557 char* fileFragment = strrchr(arg, '/'); 558 if (fileFragment) { 559 const char* directoryName = (fileFragment == arg) ? "/" : arg; 560 *fileFragment = '\0'; 561 fileFragment++; 562 int fragLen = strlen(fileFragment); 563 564 BDirectory dir(directoryName); 565 if (dir.InitCheck() == B_NO_ERROR) { 566 BEntry nextEnt; 567 BPath nextPath; 568 BList matchList; 569 int maxEntryLen = 0; 570 571 // Read in all the files in the directory whose names start 572 // with our fragment. 573 while (dir.GetNextEntry(&nextEnt) == B_NO_ERROR) { 574 if (nextEnt.GetPath(&nextPath) == B_NO_ERROR) { 575 char* filePath = strrchr(nextPath.Path(), '/') + 1; 576 if (strncmp(filePath, fileFragment, fragLen) == 0) { 577 int len = strlen(filePath); 578 if (len > maxEntryLen) 579 maxEntryLen = len; 580 char* newStr = new char[len + 1]; 581 strcpy(newStr, filePath); 582 matchList.AddItem(newStr); 583 } 584 } 585 } 586 587 // Now slowly extend our keyword to its full length, counting 588 // numbers of matches at each step. If the match list length 589 // is 1, we can use that whole entry. If it's greater than one 590 // , we can use just the match length. 591 int matchLen = matchList.CountItems(); 592 if (matchLen > 0) { 593 int i; 594 BString result(fileFragment); 595 for (i = fragLen; i < maxEntryLen; i++) { 596 // See if all the matching entries have the same letter 597 // in the next position... if so, we can go farther. 598 char commonLetter = '\0'; 599 for (int j = 0; j < matchLen; j++) { 600 char nextLetter = GetLetterAt( 601 (char*)matchList.ItemAt(j), i); 602 if (commonLetter == '\0') 603 commonLetter = nextLetter; 604 605 if ((commonLetter != '\0') 606 && (commonLetter != nextLetter)) { 607 commonLetter = '\0';// failed; 608 beep(); 609 break; 610 } 611 } 612 if (commonLetter == '\0') 613 break; 614 else 615 result.Append(commonLetter, 1); 616 } 617 618 // Free all the strings we allocated 619 for (int k = 0; k < matchLen; k++) 620 delete [] ((char*)matchList.ItemAt(k)); 621 622 DoStandardEscapes(result); 623 624 BString wholeLine; 625 for (int l = 0; l < argc - 1; l++) { 626 wholeLine += argv[l]; 627 wholeLine += " "; 628 } 629 630 BString file(directoryName); 631 DoStandardEscapes(file); 632 633 if (directoryName[strlen(directoryName) - 1] != '/') 634 file += "/"; 635 636 file += result; 637 638 // Remove any trailing slash... 639 const char* fileStr = file.String(); 640 if (fileStr[strlen(fileStr)-1] == '/') 641 file.RemoveLast("/"); 642 643 // And re-append it iff the file is a dir. 644 BDirectory testFileAsDir(file.String()); 645 if ((strcmp(file.String(), "/") != 0) 646 && (testFileAsDir.InitCheck() == B_NO_ERROR)) 647 file.Append("/"); 648 649 wholeLine += file; 650 651 SetCommand(wholeLine.String()); 652 ret = true; 653 } 654 } 655 *(fileFragment - 1) = '/'; 656 } 657 } 658 FreeArgv(argv); 659 return ret; 660 } 661 662 663 bool 664 ShortcutsSpec::ProcessColumnKeyStroke(int whichColumn, const char* bytes, 665 int32 key) 666 { 667 bool ret = false; 668 switch(whichColumn) { 669 case KEY_COLUMN_INDEX: 670 if (key > -1) { 671 if ((int32)fKey != key) { 672 fKey = key; 673 ret = true; 674 } 675 } 676 break; 677 678 case STRING_COLUMN_INDEX: 679 { 680 switch(bytes[0]) { 681 case B_BACKSPACE: 682 case B_DELETE: 683 if (fCommandNul > 0) { 684 // trim a char off the string 685 fCommand[fCommandNul - 1] = '\0'; 686 fCommandNul--; // note new nul position 687 ret = true; 688 _UpdateIconBitmap(); 689 } 690 break; 691 692 case B_TAB: 693 if (_AttemptTabCompletion()) { 694 _UpdateIconBitmap(); 695 ret = true; 696 } else 697 beep(); 698 break; 699 700 default: 701 { 702 uint32 newCharLen = strlen(bytes); 703 if ((newCharLen > 0) && (bytes[0] >= ' ')) { 704 bool reAllocString = false; 705 // Make sure we have enough room in our command string 706 // to add these chars... 707 while (fCommandLen - fCommandNul <= newCharLen) { 708 reAllocString = true; 709 // enough for a while... 710 fCommandLen = (fCommandLen + 10) * 2; 711 } 712 713 if (reAllocString) { 714 char* temp = new char[fCommandLen]; 715 strcpy(temp, fCommand); 716 delete [] fCommand; 717 fCommand = temp; 718 // fCommandNul is still valid since it's an offset 719 // and the string length is the same for now 720 } 721 722 // Here we should be guaranteed enough room. 723 strncat(fCommand, bytes, fCommandLen); 724 fCommandNul += newCharLen; 725 ret = true; 726 _UpdateIconBitmap(); 727 } 728 } 729 } 730 break; 731 } 732 733 default: 734 if ((whichColumn >= 0) && (whichColumn < NUM_META_COLUMNS)) { 735 MetaKeyStateMap * map = &sMetaMaps[whichColumn]; 736 int curState = fMetaCellStateIndex[whichColumn]; 737 int origState = curState; 738 int numStates = map->GetNumStates(); 739 740 switch(bytes[0]) 741 { 742 case B_RETURN: 743 // cycle to the previous state 744 curState = (curState + numStates - 1) % numStates; 745 break; 746 747 case B_SPACE: 748 // cycle to the next state 749 curState = (curState + 1) % numStates; 750 break; 751 752 default: 753 { 754 // Go to the state starting with the given letter, if 755 // any 756 char letter = bytes[0]; 757 if (islower(letter)) 758 letter = toupper(letter); // convert to upper case 759 760 if ((letter == B_BACKSPACE) || (letter == B_DELETE)) 761 letter = '('; 762 // so space bar will blank out an entry 763 764 for (int i = 0; i < numStates; i++) { 765 const char* desc = map->GetNthStateDesc(i); 766 767 if (desc) { 768 if (desc[0] == letter) { 769 curState = i; 770 break; 771 } 772 } else 773 printf(B_TRANSLATE("Error, NULL state description?\n")); 774 } 775 break; 776 } 777 } 778 fMetaCellStateIndex[whichColumn] = curState; 779 780 if (curState != origState) 781 ret = true; 782 } 783 break; 784 } 785 786 return ret; 787 } 788 789 790 int 791 ShortcutsSpec::MyCompare(const CLVListItem* a_Item1, const CLVListItem* a_Item2, 792 int32 KeyColumn) 793 { 794 ShortcutsSpec* left = (ShortcutsSpec*) a_Item1; 795 ShortcutsSpec* right = (ShortcutsSpec*) a_Item2; 796 797 int ret = strcmp(left->GetCellText(KeyColumn), 798 right->GetCellText(KeyColumn)); 799 return (ret > 0) ? 1 : ((ret == 0) ? 0 : -1); 800 } 801 802 803 void 804 ShortcutsSpec::Pulse(BView* owner) 805 { 806 if ((fCursorPtsValid)&&(owner->Window()->IsActive())) { 807 rgb_color prevColor = owner->HighColor(); 808 rgb_color backgroundColor = (GetSelectedColumn() == 809 STRING_COLUMN_INDEX) ? BeBackgroundGrey : BeListSelectGrey; 810 rgb_color barColor = ((GetSelectedColumn() == STRING_COLUMN_INDEX) 811 && ((system_time() % 1000000) > 500000)) ? Black : backgroundColor; 812 owner->SetHighColor(barColor); 813 owner->StrokeLine(fCursorPt1, fCursorPt2); 814 owner->SetHighColor(prevColor); 815 } 816 } 817 818 819 void 820 ShortcutsSpec::_UpdateIconBitmap() 821 { 822 BString firstWord = ParseArgvZeroFromString(fCommand); 823 824 // Only need to change if the first word has changed... 825 if (fLastBitmapName == NULL || firstWord.Length() == 0 826 || firstWord.Compare(fLastBitmapName)) { 827 if (firstWord.ByteAt(0) == '*') 828 fBitmapValid = IsValidActuatorName(&firstWord.String()[1]); 829 else { 830 fBitmapValid = false; // default till we prove otherwise! 831 832 if (firstWord.Length() > 0) { 833 delete [] fLastBitmapName; 834 fLastBitmapName = new char[firstWord.Length() + 1]; 835 strcpy(fLastBitmapName, firstWord.String()); 836 837 BEntry progEntry(fLastBitmapName, true); 838 if ((progEntry.InitCheck() == B_NO_ERROR) 839 && (progEntry.Exists())) { 840 BNode progNode(&progEntry); 841 if (progNode.InitCheck() == B_NO_ERROR) { 842 BNodeInfo progNodeInfo(&progNode); 843 if ((progNodeInfo.InitCheck() == B_NO_ERROR) 844 && (progNodeInfo.GetTrackerIcon(&fBitmap, B_MINI_ICON) 845 == B_NO_ERROR)) { 846 fBitmapValid = fBitmap.IsValid(); 847 } 848 } 849 } 850 } 851 } 852 } 853 } 854 855 856 /*static*/ void 857 ShortcutsSpec::_InitModifierNames() 858 { 859 sShiftName = B_TRANSLATE_COMMENT("Shift", 860 "Name for modifier on keyboard"); 861 sControlName = B_TRANSLATE_COMMENT("Control", 862 "Name for modifier on keyboard"); 863 // TODO: Wrapping in __INTEL__ define probably won't work to extract catkeys? 864 #if __INTEL__ 865 sOptionName = B_TRANSLATE_COMMENT("Window", 866 "Name for modifier on keyboard"); 867 sCommandName = B_TRANSLATE_COMMENT("Alt", 868 "Name for modifier on keyboard"); 869 #else 870 sOptionName = B_TRANSLATE_COMMENT("Option", 871 "Name for modifier on keyboard"); 872 sCommandName = B_TRANSLATE_COMMENT("Command", 873 "Name for modifier on keyboard"); 874 #endif 875 } 876 877