Search code examples
delphipascal

How to assign a multiline string value without quoting each line?


Is there a way to assign a multiline string value in Delphi without having to quote each line?

Edit (the specific problem): I have some SQL queries which I want to test outside Delphi. When copying the queries it is a bit of overhead to add and replace quotes every time.


Solution

  • Here's code for an application you can add to the IDE's Tools menu that might help. It was posted a while back to one of the CodeGear newsgroups by TeamB member Peter Below:

    program ClipToStringConst;
    
    // Remove the dot from the line below for a console app, 
    // per Rob Kennedy's comment. It works fine without being
    // a console app.
    {.$APPTYPE CONSOLE}
    uses
      Windows,
      Classes,
      Sysutils,
      APIClipboard;
    
    const
      cIndent = '  '; // 2 spaces
      cSingleQuote = '''';
      EndChar : array [Boolean] of Char = ('+',';');
    
    procedure Process;
    var
      SL: TStringlist;
      i, max: Integer;
    begin
      if ClipboardHasFormat( CF_TEXT ) then
      begin
        SL := TStringlist.Create;
        try
          SL.Text := ClipboardAsString;
          max := SL.count-1;
          for i:= 0 to max do
            SL[i] := cIndent +
                     AnsiQuotedStr( TrimRight(SL[i])+#32, cSingleQuote ) +
                     EndChar[i = max];
          StringToClipboard( SL.Text );
        finally
          SL.Free;
        end; { Finally }
      end;
    end;
    
    begin
      try
        Process;
      except
        on E: Exception do
          ShowException( E, ExceptAddr );
      end;
    end.
    

    Just select the text in the SQL management tool after you've tested it and copy it to the clipboard. Switch to the Delphi Code Editor, place the insertion point where you want the constant text to appear, choose 'Clipboard To Const' or whatever you called it from the Tools menu, and then Ctrl+V to paste it into the editor.

    It's a pretty handy little tool. You can also modify it to work the opposite way (ConstantToClipboard) to remove the source formatting and revert back to raw SQL, although I haven't bothered to do so yet.

    EDIT: Missed a unit (APIClipboard). This needs to be a separate unit, obviously. Again, thanks to Peter Below:

    {== Unit APIClipboard =================================================}
    {: Clipboard access routines using only API functions
    @author Dr. Peter Below
    @desc Version 1.0 created 5 Juli 2000<BR>
            Current revision 1.0<BR>
            Last modified 5 Juli 2000<P>
    
    This unit provides simply clipboard access routines that do not rely on
    the VCL Clipbrd unit. That unit drags in Dialogs and Forms and a major
    part of the VCL as a consequence, not appropriate for simple console
    or non-form programs. This unit uses only API routines, the only VCL
    units used are Classes (for exceptions and streams) and SysUtils.
    }
    {=====================================================================}
    
    unit APIClipboard;
    
    interface
    
    uses
      Windows, Classes;
    
      procedure StringToClipboard( const S: String );
      function ClipboardAsString: String;
      procedure CopyDataToClipboard( fmt: DWORD; const data; datasize: Integer;
                                     emptyClipboardFirst: Boolean = true );
      procedure CopyDataFromClipboard( fmt: DWORD; S: TStream );
      function ClipboardHasFormat( fmt: DWORD ): Boolean;
    
    implementation
    
    uses
      Sysutils;
    
    type
      {: This is an internal exception class used by the <see unit=APIClipboard> }
      EClipboardError = class( Exception )
      public
        constructor Create( const msg: String );
      end;
    
    resourcestring
      eSystemOutOfMemory =
        'could not allocate memory for clipboard data.';
      eLockfailed =
        'could not lock global memory handle.';
      eSetDataFailed =
        'could not copy data block to clipboard.';
      eCannotOpenClipboard =
        'could not open the clipboard.';
      eErrorTemplate =
        'APIClipboard: %s'#13#10+
        'System error code: %d'#13#10+
        'System error message: %s';
    
    {-- EClipboardError.Create --------------------------------------------}
    {: Creates a new EclipboardError object
    @Param msg is the string to embed into the error message
    @Precondition none
    @Postcondition none
    @desc Composes an error message that contains the passed message and the
      API error code and matching error message. The CreateFmt constructor
      inherited from the basic Exception class is used to do the work.
    Created 5.7.2000 by P. Below
    }{---------------------------------------------------------------------}
    
    constructor EClipboardError.Create( const msg: String );
    begin { Create }
      CreateFmt( eErrorTemplate,
                   [msg, GetLastError, SysErrorMessage(GetLastError)] );
    end; { EClipboardError.Create }
    
    {-- DataToClipboard ---------------------------------------------------}
    {: Copies a block of memory to the clipboard in a given format
    @Param fmt is the clipboard format to use
    @Param data is an untyped const parameter that addresses the data to copy
    @Param datasize is the size of the data, in bytes
    @Precondition The clipboard is already open. If not an EClipboardError
      will result. This precondition cannot be asserted, unfortunately.
    @Postcondition Any previously exisiting data of this format will have
      been replaced by the new data, unless datasize was 0 or we run into an
      exception. In this case the clipboard will be unchanged.
    @desc Uses API methods to allocate and lock a global memory block of
      approproate size, copies the data to it and submits the block to the
      clipboard. Any error on the way will raise an EClipboardError
      exception.<BR>
    Created 5.7.2000 by P. Below
    @Raises EClipboardError
    }{---------------------------------------------------------------------}
    
    procedure DataToClipboard( fmt: DWORD; Const data; datasize: Integer );
    var
      hMem: THandle;
      pMem: Pointer;
    begin { DataToClipboard }
      if datasize <= 0 then
        Exit;
    
      hMem := GlobalAlloc( GMEM_MOVEABLE or GMEM_SHARE or GMEM_ZEROINIT, datasize );
      if hmem = 0 then
        raise EClipboardError.Create( eSystemOutOfMemory );
    
      pMem := GlobalLock( hMem );
      if pMem = nil then
      begin
        GlobalFree( hMem );
        raise EClipboardError.Create( eLockFailed );
      end;
    
      Move( data, pMem^, datasize );
      GlobalUnlock( hMem );
      if SetClipboardData( fmt, hMem ) = 0 then
        raise EClipboardError( eSetDataFailed );
    
      // Note: API docs are unclear as to whether the memory block has
      // to be freed in case of failure. Since failure is unlikely here
      // lets blithly ignore this issue for now.
    end; { DataToClipboard }
    
    {-- DataFromClipboard -------------------------------------------------}
    {: Copies data from the clipboard into a stream
    @Param fmt is the clipboard format to look for
    @Param S is the stream to copy to
    @precondition S <> nil
    @postcondition If data was copied the streams position will have moved
    @desc Tries to get a memory block for the requested clipboard format.
    Nothing
      further is done if this fails (because the format is not available or
      the clipboard is not open, we treat neither as error here), otherwise
      the memory handle is locked and the data copied into the stream. <P>
      Note that we cannot determine the actual size of the data originally
      copied to the clipboard, only the allocated size of the memory block!
      Since GlobalAlloc works with a granularity of 32 bytes the block may be
      larger than required for the data and thus the stream may contain some
      spurious bytes at the end. There is no guarantee that these bytes will
      be 0. <P>
      If the memory handle obtained from the clipboard cannot be locked we
      raise an <see class=EClipboardError> exception.
    Created 5.7.2000 by P. Below
    @Raises EClipboardError
    }{---------------------------------------------------------------------}
    
    procedure DataFromClipboard( fmt: DWORD; S: TStream );
    var
      hMem: THandle;
      pMem: Pointer;
      datasize: DWORD;
    begin { DataFromClipboard }
      Assert( Assigned( S ));
      hMem := GetClipboardData( fmt );
      if hMem <> 0 then
      begin
        datasize := GlobalSize( hMem );
        if datasize > 0 then
        begin
          pMem := GlobalLock( hMem );
          if pMem = nil then
            raise EclipboardError.Create( eLockFailed );
          try
            S.WriteBuffer( pMem^, datasize );
          finally
            GlobalUnlock( hMem );
          end;
        end;
      end;
    end; { DatafromClipboard }
    
    {-- CopyDataToClipboard -----------------------------------------------}
    {: Copies a block of memory to the clipboard in a given format
    @Param fmt is the clipboard format to use
    @Param data is an untyped const parameter that addresses the data to copy
    @Param datasize is the size of the data, in bytes
    @Param emptyClipboardFirst determines if the clipboard should be emptied,
      true by default
    @Precondition The clipboard must not be open already
    @Postcondition If emptyClipboardFirst is true all prior data will be
      cleared from the clipboard, even if datasize is <= 0. The clipboard
      is closed again.
    @desc Tries to open the clipboard, empties it if required and then tries to
      copy the passed data to the clipboard. This operation is a NOP if
      datasize <= 0. If the clipboard cannot be opened a <see
    class=EClipboardError>
      is raised.
    Created 5.7.2000 by P. Below
    @Raises EClipboardError
    }{---------------------------------------------------------------------}
    
    procedure CopyDataToClipboard( fmt: DWORD; const data; datasize: Integer;
                                   emptyClipboardFirst: Boolean = true );
    begin { CopyDataToClipboard }
      if OpenClipboard( 0 ) then
        try
          if emptyClipboardFirst then
            EmptyClipboard;
          DataToClipboard( fmt, data, datasize );
        finally
          CloseClipboard;
        end
      else
        raise EclipboardError.Create( eCannotOpenClipboard );
    end; { CopyDataToClipboard }
    
    {-- StringToClipboard -------------------------------------------------}
    {: Copies a string to clipboard in CF_TEXT clipboard format
    @Param S is the string to copy, it may be empty.
    @Precondition The clipboard must not be open already.
    @Postcondition Any prior clipboard content will be cleared, but only
      if S was not empty. The clipboard is closed again.
    @desc Hands the brunt of the work off to <See routine=CopyDataToClipboard>,
      but only if S was not empty. Otherwise nothing is done at all.<BR>
    Created 5.7.2000 by P. Below
    @Raises EClipboardError
    }{---------------------------------------------------------------------}
    
    procedure StringToClipboard( const S: String );
    begin
      if Length(S) > 0 Then
        CopyDataToClipboard( CF_TEXT, S[1], Length(S)+1);
    end; { StringToClipboard }
    
    {-- CopyDataFromClipboard ---------------------------------------------}
    {: Copies data from the clipboard into a stream
    @Param fmt is the clipboard format to look for
    @Param S is the stream to copy to
    @Precondition S <> nil<P>
      The clipboard must not be open already.
    @Postcondition If data was copied the streams position will have moved.
      The clipboard is closed again.
    @desc Tries to open the clipboard, and then tries to
      copy the data to the passed stream. This operation is a NOP if
      the clipboard does not contain data in the requested format.
      If the clipboard cannot be opened a <see class=EClipboardError>
      is raised.
    Created 5.7.2000 by P. Below
    @Raises EClipboardError
    }{---------------------------------------------------------------------}
    
    procedure CopyDataFromClipboard( fmt: DWORD; S: TStream );
    begin { CopyDataFromClipboard }
      Assert( Assigned( S ));
      if OpenClipboard( 0 ) then
        try
          DataFromClipboard( fmt , S );
        finally
          CloseClipboard;
        end
      else
        raise EclipboardError.Create( eCannotOpenClipboard );
    end; { CopyDataFromClipboard }
    
    {-- ClipboardAsString -------------------------------------------------}
    {: Returns any text contained on the clipboard
    @Returns the clipboards content if it contained something in CF_TEXT
      format, or an empty string.
    @Precondition The clipboard must not be already open
    @Postcondition The clipboard is closed.
    @desc If the clipboard contains data in CF_TEXT format it is copied to a
      temp memory stream, zero-terminated for good measure and copied into
      the result string.
    Created 5.7.2000 by P. Below
    @Raises EClipboardError
    }{---------------------------------------------------------------------}
    
    function ClipboardAsString: String;
    const
      nullchar: Char = #0;
    var
      ms: TMemoryStream;
    begin { ClipboardAsString }
      if not IsClipboardFormatAvailable( CF_TEXT ) then
        Result := EmptyStr
      else
      begin
        ms:= TMemoryStream.Create;
        try
          CopyDataFromClipboard( CF_TEXT , ms );
          ms.Seek( 0, soFromEnd );
          ms.WriteBuffer( nullChar, Sizeof( nullchar ));
          Result := PChar( ms.Memory );
        finally
          ms.Free;
        end;
      end;
    end; { ClipboardAsString }
    
    {-- ClipboardHasFormat ------------------------------------------------}
    {: Checks if the clipboard contains data in the specified format
    @Param fmt is the format to check for
    @Returns true if the clipboard contains data in this format, false
      otherwise
    @Precondition none
    @Postcondition none
    @desc This is a simple wrapper around an API function.
    Created 5.7.2000 by P. Below
    }{---------------------------------------------------------------------}
    
    function ClipboardHasFormat( fmt: DWORD ): Boolean;
    begin { ClipboardHasFormat }
      Result := IsClipboardFormatAvailable( fmt );
    end; { ClipboardHasFormat }
    
    end.
    

    Sample use:

    Prepare the text in your SQL editor, text editor, or whatever:

    SELECT 
      lname,
      fname,
      dob
    FROM
      employees
    

    Select all of the text, and copy to the clipboard using Ctrl+C.

    Switch to the IDE's Code Editor, run the ClipboardToStringConst application (using the Tools menu item you added, or whatever other means you want). Place the editor's cursor (insertion point) where you want the constant text to appear, and press Ctrl+V to paste in the text.

    const
      MySQLText = |            // The pipe indicates the insertion point.
    

    The result:

    const
      MySQLText =   'SELECT '+
      '  lname, '+
      '  fname, '+
      '  dob '+
      'FROM '+
      '  employees ';