It seams as though I can't post via email anymore - so this is from
the website.


Here it is, small and simple - thanks to Nafis Sadyko

Just add to a package and install it.

Doug

----------------------------------------------------------------------------------------------------------------------------------------------------
unit JBComment;
 {
   Keybinding for Delphi (versions 5, 6, 7) and
   C++Builder (versions 5 and 6) editors.
   Introduces 'Ctrl+/' shortcut for fast multi-line (JBuilder-style)
commenting.
   How it works: select text block (with a mouse) and press 'Ctrl+/'
   to comment/uncomment the lines contained.
   Installation: just install as you'd install a new component
   (by clicking "Component-> Install Component" IDE menu), then
compile and
   install the resulting package.
   Note: for C++Builder - don't forget to add the path of your Builder's
   Source\ToolsAPI  folder to your package's library path in Project
Options.
  
----------------------------------------------------------------------------
   Copyright Nafis Sadykov - No restrictions on use.
   e-mail: [EMAIL PROTECTED]
 }
interface
uses
//  SysUtils, {if using inttostr() or format() functions for debug
messages}
 Classes,
 Menus,
 ToolsAPI,
 Windows;
procedure Register;
implementation
const
 VK_SLASH = 191; // not defined in 'windows.pas'
type
 TJBCommentBinding = class(TNotifierObject, IOTAKeyboardBinding)
 private
 public
   {required IOTAKeyboardBinding methods:}
   function GetBindingType: TBindingType;
   function GetDisplayName: string;
   function GetName: string;
   procedure BindKeyboard(const BindingServices:
     IOTAKeyBindingServices);
   {callback for the introduced shortcut - though not called automatically
   for our specific "Ctrl+/" shortcut}
   procedure JBComment(const Context: IOTAKeyContext;
     KeyCode: TShortcut;
     var BindingResult: TKeyBindingResult);
   {callback for all the "unrecognized" shortcuts - this is where we
actually
    catch 'Ctrl+/' shortcut using API call, and call the method above
    ourselves}
   procedure DefaultKeyProc(const Context: IOTAKeyContext;
     KeyCode: TShortcut;
     var BindingResult: TKeyBindingResult);
   end;
procedure Register;
 begin
 (BorlandIDEServices as IOTAKeyboardServices).
   AddKeyboardBinding(TJBCommentBinding.Create);
 end;
function TJBCommentBinding.GetBindingType: TBindingType;
 begin
 {means we don't want to re-define the entire keyboard, but to introduce
 just one or more shortcuts}
 Result := btPartial;
 end;
function TJBCommentBinding.GetDisplayName: string;
 begin
 {name to be displayed on the "Key Mappings" tab
 when you click "Tools->Editor Options" menu in IDE}
 Result := 'Multiline Commenting (JBuilder-style)';
 end;
function TJBCommentBinding.GetName: string;
 begin
 Result := 'ns.jbcomment'; // any unique string will do here
 end;
procedure TJBCommentBinding.BindKeyboard(
 const BindingServices: IOTAKeyBindingServices);
 begin
 {doesn't work for this specific shortcut, though works OK for "Ctrl+\"
 for example.}
 //BindingServices.AddKeyBinding([TextToShortCut('Ctrl+/')],
JBComment, nil);
 {So, instead we define default callback for all unrecognized shortcuts}
 BindingServices.SetDefaultKeyProc(DefaultKeyProc, nil, '');
 end;
procedure TJBCommentBinding.JBComment(const Context: IOTAKeyContext;
 KeyCode: TShortcut; var BindingResult: TKeyBindingResult);
 var
   iEditPos: IOTAEditPosition;
   iEditBlock: IOTAEditBlock;
   startCol, endCol,
   startRow, endRow: Integer;
   topViewRow, topViewColumn: integer;
   i: integer;
   cursorAtStart: boolean;// is our cursor at the beginning of the
selection
 procedure myCommentUncomment(Row: integer);
   begin
   iEditPos.Move(Row, 1);
   if iEditPos.Read(2) = '//' then // if commented then uncomment the line
     begin
     {somehow, independent on the parameter, the .Delete() method only
deletes
     a single character, so we need to call it twice here. (Another
IOTA bug ?)}
     iEditPos.Delete(1);
     iEditPos.Delete(1);
     {now move the selection to the left}
     if (Row = startRow) and  (startCol <> 1) then
       dec(startCol, 2);
     if (Row = endRow) and  (endCol <> 1) then
       dec(endCol, 2);
     end
   else
     begin                      // otherwise comment the line
     iEditPos.InsertText('//');
     {now move the selection to the right}
     if (Row = startRow) and  (startCol <> 1) then
       inc(startCol, 2);
     if (Row = endRow) and  (endCol <> 1) then
       inc(endCol, 2);
     end;
   end;
 begin
 BindingResult := krHandled;
 iEditBlock := Context.EditBuffer.EditBlock;
 if not iEditBlock.IsValid then // do nothing if there is no selection
   exit;
 {store the EditBlock's parameters:}
 startCol:= iEditBlock.StartingColumn;
 endCol:= iEditBlock.EndingColumn;
 startRow:= iEditBlock.StartingRow;
 endRow:= iEditBlock.EndingRow;
 iEditPos:= Context.EditBuffer.EditPosition;
 {remember which side of the selection the cursor is at}
 if (iEditPos.Row = startRow) and (iEditPos.Column = startCol) then
   cursorAtStart:= true
 else
   cursorAtStart:= false;
 {and remember the top line viewed in the editor}
 topViewRow:= Context.EditBuffer.TopView.TopRow;
 topViewColumn:= Context.EditBuffer.TopView.LeftColumn;
 for i:= startRow to endRow -1 do
   myCommentUncomment(i);
 {process only if there is non-empty selection in the last row}
 if (endRow <> startRow) and (endCol > 1) then
   myCommentUncomment(endRow)
 {process anyway if the selection doesn't span more than a single row}
 else if (endRow = startRow) then
   myCommentUncomment(endRow);
 {now restore the selection and the cursor position}
 if cursorAtStart then
   begin
   iEditPos.Move(endRow, endCol);
   iEditBlock.EndBlock;
   iEditPos.Move(startRow, startCol);
   iEditBlock.BeginBlock;
   end
 else
   begin
   iEditPos.Move(startRow, startCol);
   iEditBlock.BeginBlock;
   iEditPos.Move(endRow, endCol);
   iEditBlock.EndBlock;
   end;
 {restore the top row and left column - necessary for cases when you have
 selections spanning more than a single page}
 Context.EditBuffer.TopView.SetTopLeft(topViewRow, topViewColumn);
 end;
var
 ArrayKeysState: TKeyboardState; // type is defined in 'windows.pas'
function AreCorrectKeysPressed: boolean;
 var
   i: integer;
   ctrlPressed,
   slashPressed,
   otherKeysPressed: boolean;
 begin
 result:= false;
 ctrlPressed:= false;
 slashPressed:= false;
 otherKeysPressed:= false;
 if GetKeyboardState(ArrayKeysState) then   // make WinAPI call
   begin
   for i:= Low(ArrayKeysState) to High(ArrayKeysState)  do
     begin
     {if the highest bit is set - see 'win32.hlp' for details on
GetKeyboardState()}
     if (ArrayKeysState[i] and $80) > 0 then
       begin
       case i of
         VK_CONTROL, VK_LCONTROL, VK_RCONTROL:
             ctrlPressed:= true;
         VK_SLASH:
             slashPressed:= true;
         else
           begin
           otherKeysPressed:= true;
           break;
           end;
         end; //end case
       end;
     end; //end for
   if (not otherKeysPressed)and ctrlPressed and slashPressed then
     result:= true;
   end;
 end;
procedure TJBCommentBinding.DefaultKeyProc(const Context: IOTAKeyContext;
 KeyCode: TShortcut; var BindingResult: TKeyBindingResult);
 begin
 {
   Debugging message : if switched on, it'll show us that ALL the
shortcuts
   come with the same(!?) KeyCode parameter of $4040.
   (You need the Messages window - same where you get the Compiler
messages -
   visible to get this debug message.)

   Note: setting Breakpoints or trying to use Message Dialogs is of no use
   when debugging Keybindings, so using
IOTAMessageServices).AddTitleMessage()
   seems to be the only way to get the debugging info needed.
 }
//  (BorlandIDEServices as IOTAMessageServices).AddTitleMessage(
//           format('DefaultKeyProc: ShortCut KeyCode= $%.4x',[KeyCode]));
if AreCorrectKeysPressed then
   begin
   self.JBComment(Context, KeyCode, BindingResult);
   BindingResult:= krHandled;
   end
 else
   BindingResult:= krUnhandled;
 end;
end.
----------------------------------------------------------------------------------------------------------------------------------------------------


Reply via email to