Example: The DWClipboard JSExtension


Example: The DWClipboard JSExtension

In this section, we will create a working JSExtension that can place text onto and receive text from the system Clipboard. This allows JavaScript developers direct access to the Clipboard to see what kind of information is available.

Our extension will have three functions that JavaScript developers can call: setClipboardContent , getClipboardContent , and getClipboardContentSize .

  • setClipboardContent()

    Inserts a string of text into the Clipboard

  • getClipboardContent()

    Retrieves the contents of the Clipboard

  • getClipboardContentSize()

    Returns the number of characters on the Clipboard

NOTE

The following example uses the Windows and Macintosh API functions to access each system's Clipboard. For more information on these functions, you should consult the documentation for your particular development environment.


In addition, both the Mac and Windows source code will be in the same file. We will make sure that the correct code is compiled for the correct platform by using standard C conditional compilation directives.

Setting Up the Project

Create a new extension project for your particular platform by following the directions at the start of the chapter. Name your extension project DWClipboard. Create a new text file named DWClipboard.cpp and add it to your extension project.

NOTE

To view the completed code, see Listing 10-2 at the end of this section.


After this is done, you can begin writing the extension code.

Including the Header Files and Definitions

First, we need to include the necessary header files and define the function prototypes that will be used in the extension:

 #ifdef _WIN32   // Windows  #include "windows.h"  #else           // Mac  #define _MAC 1  #define HANDLE Handle  #include <Memory.h>  #include <Scrap.h>  #endif  #ifdef __cplusplus  extern "C" {  #endif  #include "mm_jsapi.h"  #ifdef __cplusplus  }  #endif  // Define the forward function prototypes  JSBool  setClipboardContent( JSContext* cx, JSObject* obj, unsigned int  argc, jsval* jsArgv, jsval* rval );  JSBool  getClipboardContent( JSContext* cx, JSObject* obj, unsigned int  argc, jsval* jsArgv, jsval* rval );  JSBool  getClipboardContentSize( JSContext* cx, JSObject* obj, unsigned  int  argc, jsval* jsArgv, jsval* rval );  void MM_Init( void ); 

The first few lines include any necessary platform-specific header files needed by the extension to call the OS API functions to access the Clipboard. In addition, for coding convenience, we define a HANDLE macro on the Macintosh to represent the Mac-specific Handle type. This simplifies things later on in the code for OS functions that accept handles to memory blocks; we don't need to write specific code for each platform just to define two different memory handle variables .

We also define forward function prototypes for the functions that the extension will use. These are not absolutely required by most compilers, but it is usually a good idea to include them.

Writing the Initialization Code

The next step is to write the initialization code for the extension. Here is where the MM_STATE declaration can be found, as well as the code for the MM_Init() function that will define the three functions that are available in the JavaScript environment:

 MM_STATE  void  MM_Init( void )  {     JS_DefineFunction("setClipboardContent", setClipboardContent, 1 );     JS_DefineFunction("getClipboardContent", getClipboardContent, 0 );     JS_DefineFunction("getClipboardContentSize",  getClipboardContentSize, 0);  } 

Setting the Clipboard Content

The JavaScript developer calls the setClipboardContent() function to place a string of text onto the Clipboard.

 JSBool setClipboardContent( JSContext* cx, JSObject* obj, unsigned  int  argc, jsval* jsArgv, jsval* rval )  {     char *clipText;     unsigned int clipTextLen;     HANDLE hMem;     // Get the first argument     clipText = JS_ValueToString(cx, jsArgv[0], &clipTextLen);  if ( clipText == NULL) {        *rval = JS_IntegerToValue( 0 );        return JS_TRUE;     }  #ifdef _WIN32     LPVOID pText;     if (::OpenClipboard(NULL))     {        ::EmptyClipboard();        // Create the memory handle        hMem = ::GlobalAlloc( GMEM_MOVEABLE + GMEM_DDESHARE +        GMEM_ZEROINIT, clipTextLen);        pText = ::GlobalLock(hMem);        memcpy(pText,clipText,(size_t)clipTextLen);        ::GlobalUnlock(hMem);        // Set the clipboard contents        ::SetClipboardData(CF_TEXT, hMem);        ::CloseClipboard();        *rval = JS_IntegerToValue( 1 );     }     *rval = JS_IntegerToValue( 0 );  #else     ::ZeroScrap();     ::PutScrap(clipTextLen,'TEXT',clipText);     *rval = JS_IntegerToValue( 1 );  #endif     return JS_TRUE;  } 

The function first attempts to convert the first argument to a string. If it fails and clipText is equal to NULL , the function returns a result of to the JavaScript environment but does not report an error.

The next line, #ifdef _WIN32 , uses a "conditional compiler" flag to compile code only if the symbol _WIN32 is defined. Most compilers define these kinds of symbols automatically for you so that your code can compile differently under different platforms and compilers. Here, the Microsoft Visual C++ compiler defines this symbol if compiling on Windows. If this symbol is not defined, the code that is contained in the #else section of the conditional block is compiled instead. This is how you can compile code for more than one platform in your extensions.

The Windows code calls the OpenClipboard() function to gain access to the Clipboard. If the function returns TRUE , then the EmptyClipboard() function is called to clear the Clipboard of its current contents.

Next, a Windows construct called a "memory handle" is allocated from the system to hold the text that will be placed on the Clipboard. (The Windows Clipboard API expects the contents to be stored in a memory handle .)

The extension places the text on the Clipboard by calling SetClipboardData() and then cleans up by calling CloseClipboard() . The function then returns a non-zero value to the JavaScript environment to indicate that the function completed successfully.

The Mac code is much simpler in this case. First, the function ZeroScrap() is called to clear the current Clipboard contents, and then PutScrap() is called to set the Clipboard to the supplied text string. (The Mac uses the term "Scrap" because the original name for the Clipboard was the ScrapBook.)

Getting the Clipboard Content

Getting the Clipboard content is essentially the reverse of setting the Clipboard content.

 JSBool getClipboardContent( JSContext* cx, JSObject* obj, unsigned  int  argc, jsval* jsArgv, jsval* rval )  {     HANDLE hMem;     char *pText = NULL;     unsigned int iTextSize = 0;  #ifdef _WIN32     // get the clipboard text if there is any     if (::OpenClipboard(NULL))     {        if (::IsClipboardFormatAvailable(CF_TEXT))        {           // get the string data           hMem = ::GetClipboardData(CF_TEXT);           pText = (char *)::GlobalLock(hMem);           iTextSize = strlen((const char *)pText);           JS_StringToValue(cx,pText,iTextSize,rval);           ::GlobalUnlock(hMem);        }        else           *rval = (jsval)NULL;        ::CloseClipboard();     }     else        *rval = (jsval)NULL;  #else     long offset;     iTextSize = ::GetScrap(NULL, 'TEXT', &offset);     hMem = NewHandleClear(iTextSize);     ::GetScrap(hMem, 'TEXT', &offset);     HLock(hMem);     JS_StringToValue( cx, *hMem, iTextSize, rval );     HUnlock(hMem);     DisposeHandle(hMem);  #endif     return JS_TRUE;  } 

The Windows part of the code starts by calling OpenClipboard() , and then checks to see if the Clipboard contains any text by calling IsClipboardFormatAvailable() . This is an important check because the system Clipboard can contain text, images, or other custom data that applications store there.

If the Clipboard contains text, then it is retrieved by a call to GetClipboardData() . This returns a Windows memory handle , which stores the string. The string is then converted to a JavaScript value by calling JS_StringToValue() .

The Mac part of the code is more complex than the previous function, although simpler than the Windows code. The steps of determining whether text data is available and what its length is are combined in the call to GetScrap() .

The first call to GetScrap() passes a NULL value for the first argument, which essentially means "Just tell me the length of whatever is there, but don't retrieve it yet." After the length has been retrieved, a Mac memory handle is created to hold the returned text. This time, the call to GetScrap() passes this memory handle as the first argument, which causes the Mac to store the string that represents the Clipboard contents in the handle.

The resulting string is then converted to a JavaScript value via JS_StringToValue() and returned to the caller.

Getting the Clipboard Content Length

In our last function, we retrieve the length of the text on the Clipboard.

 JSBool getClipboardContentSize( JSContext* cx, JSObject* obj,  unsigned int  argc, jsval* jsArgv, jsval* rval )  {     HANDLE hMem;     char *pText;     unsigned int iTextSize = -1;  #ifdef _WIN32     if (::OpenClipboard(NULL))     {        if (::IsClipboardFormatAvailable(CF_TEXT))        {           // get the string data           hMem = ::GetClipboardData(CF_TEXT);           pText = (char *)::GlobalLock(hMem);           iTextSize = strlen((const char *)pText);           ::GlobalUnlock(hMem);        }        ::CloseClipboard();     }  #else     long offset;     iTextSize = ::GetScrap(NULL, 'TEXT', &offset);  #endif     *rval = JS_IntegerToValue(iTextSize);     return JS_TRUE;  } 

The Windows code opens the Clipboard by calling OpenClipboard() and then checks to see if text data is available. This check is necessary because the system Clipboard can contain text, pictures, and other custom data types that applications store there.

If textual data is on the Clipboard, it is retrieved by calling GetClipboardData() , which returns a memory handle that references the text string. The length of the string is calculated using the standard C function strlen() , and the result is converted to a JavaScript value before being returned to the caller.

Again, the Mac code is much simpler because the steps of determining whether text data is available and, if so, its length are combined by the call to GetScrap() .

Testing the Extension

The following is a simple command that can be used to test the DWClipboard extension. The command exercises the three API functions that have been exposed to the JavaScript environment by setting and retrieving the contents of the Clipboard. To use this command, save it in the Commands folder and restart Dreamweaver.

The command first retrieves the current contents of the Clipboard and displays it in an alert box. Next, the command copies a new string to the Clipboard, retrieves the size of the new string, and displays it in an alert box. Finally, the command retrieves the new string from the Clipboard and displays it.

 <html>  <head>  <title>Test DWClipboard</title>  <script>  function test()  {     alert("Current Clip Content");     alert(DWClipboard.getClipboardContent());     alert("Copying 'Hello World' to clipboard...");     DWClipboard.setClipboardContent("Hello World");     alert("Getting the clip size...");     alert(DWClipboard.getClipboardContentSize());     alert("Getting the clip content...");     alert(DWClipboard.getClipboardContent());  }  </script>  </head>  <body onLoad="test()">  </body>  </html> 
Listing 10-2 DWClipboard.cpp (10_dwclipboard.cpp)
 #ifdef _WIN32   // Windows  #include "windows.h"  #else           // Mac  #define _MAC 1  #define HANDLE Handle  #include <Memory.h>  #include <Scrap.h>  #endif  #ifdef __cplusplus  extern "C" {  #endif  #include "mm_jsapi.h"  #ifdef __cplusplus  }  #endif  // Define the forward function prototypes  JSBool  setClipboardContent( JSContext* cx, JSObject* obj, unsigned int  argc, jsval* jsArgv, jsval* rval );  JSBool  getClipboardContent( JSContext* cx, JSObject* obj, unsigned int  argc, jsval* jsArgv, jsval* rval );  JSBool  getClipboardContentSize( JSContext* cx, JSObject* obj, unsigned int  argc, jsval* jsArgv, jsval* rval );  void MM_Init( void );  MM_STATE  void  MM_Init( void )  {     JS_DefineFunction("setClipboardContent", setClipboardContent, 1 );     JS_DefineFunction("getClipboardContent", getClipboardContent, 0 );     JS_DefineFunction("getClipboardContentSize", getClipboardContentSize, 0);  }  JSBool setClipboardContent( JSContext* cx, JSObject* obj, unsigned  int  argc, jsval* jsArgv, jsval* rval )  {     char *clipText;     unsigned int clipTextLen;     HANDLE hMem;     // Get the first argument     clipText = JS_ValueToString(cx, jsArgv[0], &clipTextLen);  if ( clipText == NULL) {        *rval = JS_IntegerToValue( 0 );        return JS_TRUE;     }   #ifdef _WIN32     LPVOID pText;     if (::OpenClipboard(NULL))     {        ::EmptyClipboard();        // Create the memory handle        hMem = ::GlobalAlloc( GMEM_MOVEABLE + GMEM_DDESHARE +        GMEM_ZEROINIT, clipTextLen);        pText = ::GlobalLock(hMem);        memcpy(pText,clipText,(size_t)clipTextLen);        ::GlobalUnlock(hMem);        // Set the clipboard contents        ::SetClipboardData(CF_TEXT, hMem);        ::CloseClipboard();        *rval = JS_IntegerToValue( 1 );     }     *rval = JS_IntegerToValue( 0 );  #else     ::ZeroScrap();     ::PutScrap(clipTextLen,'TEXT',clipText);     *rval = JS_IntegerToValue( 1 );  #endif     return JS_TRUE;  }  JSBool getClipboardContent( JSContext* cx, JSObject* obj, unsigned  int  argc, jsval* jsArgv, jsval* rval )  {     HANDLE hMem;     char *pText = NULL;     unsigned int iTextSize = 0;  #ifdef _WIN32     // get the clipboard text if there is any     if (::OpenClipboard(NULL))     {        if (::IsClipboardFormatAvailable(CF_TEXT))        {           // get the string data           hMem = ::GetClipboardData(CF_TEXT);           pText = (char *)::GlobalLock(hMem);           iTextSize = strlen((const char *)pText);           JS_StringToValue(cx,pText,iTextSize,rval);           ::GlobalUnlock(hMem);        }        else           *rval = (jsval)NULL;        ::CloseClipboard();     }     else        *rval = (jsval)NULL;  #else     long offset;         iTextSize = ::GetScrap(NULL, 'TEXT', &offset);     hMem = NewHandleClear(iTextSize);     ::GetScrap(hMem, 'TEXT', &offset);     HLock(hMem);     JS_StringToValue( cx, *hMem, iTextSize, rval );     HUnlock(hMem);     DisposeHandle(hMem);  #endif     return JS_TRUE;  }  JSBool getClipboardContentSize( JSContext* cx, JSObject* obj,  unsigned int  argc, jsval* jsArgv, jsval* rval )   {     HANDLE hMem;     char *pText;     unsigned int iTextSize = -1;  #ifdef _WIN32     if (::OpenClipboard(NULL))     {        if (::IsClipboardFormatAvailable(CF_TEXT))        {           // get the string data           hMem = ::GetClipboardData(CF_TEXT);           pText = (char *)::GlobalLock(hMem);           iTextSize = strlen((const char *)pText);           ::GlobalUnlock(hMem);        }        ::CloseClipboard();     }  #else     long offset;     iTextSize = ::GetScrap(NULL, 'TEXT', &offset);  #endif     *rval = JS_IntegerToValue(iTextSize);     return JS_TRUE;  } 


Joseph Lowery's Beyond Dreamweaver
Joseph Lowerys Beyond Dreamweaver
ISBN: B000H2MWYS
EAN: N/A
Year: 2001
Pages: 87
Authors: Joseph Lowery

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net