Back to index

lightning-sunbird  0.9+nobinonly
JSConsole.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 //is case this is defined from the outside... MMP
00039 #ifdef WIN32_LEAN_AND_MEAN
00040 #undef WIN32_LEAN_AND_MEAN
00041 #endif
00042 
00043 #include "JSConsole.h"
00044 #include "jsconsres.h"
00045 #include "nsIScriptContext.h"
00046 #include <stdio.h>
00047 #include "jsapi.h"
00048 #include "nsReadableUtils.h"
00049 
00050 HINSTANCE JSConsole::sAppInstance = 0;
00051 HACCEL JSConsole::sAccelTable = 0;
00052 CHAR JSConsole::sDefaultCaption[] = "JavaScript Console";
00053 BOOL JSConsole::mRegistered = FALSE;
00054 
00055 
00056 // display an error string along with the error returned from
00057 // the GetLastError functions
00058 #define MESSAGE_LENGTH 256
00059 void DisplayError(LPSTR lpMessage)
00060 {
00061     CHAR lpMsgBuf[MESSAGE_LENGTH * 2];
00062     CHAR lpLastError[MESSAGE_LENGTH];
00063 
00064     ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
00065                     NULL,
00066                     GetLastError(),
00067                     0,
00068                     (LPTSTR)&lpLastError,
00069                     MESSAGE_LENGTH,
00070                     NULL);
00071     
00072     strcpy(lpMsgBuf, lpMessage);
00073     strcat(lpMsgBuf, "\nThe WWS (Worthless Windows System) reports:\n");
00074     strcat(lpMsgBuf, lpLastError);
00075 
00076     // Display the string.
00077     ::MessageBox(NULL, lpMsgBuf, "JSConsole Error", MB_OK | MB_ICONSTOP);
00078 }
00079 
00080 #if defined(_DEBUG)
00081   #define VERIFY(value, errorCondition, message)   \
00082                   if((value) == (errorCondition)) DisplayError(message);
00083 #else // !_DEBUG
00084   #define VERIFY(value, errorCondition, message)   (value)
00085 #endif // _DEBUG
00086 
00087 //
00088 // Register the window class
00089 //
00090 BOOL JSConsole::RegisterWidget()
00091 {
00092     WNDCLASS wc;
00093    
00094     wc.style = 0;
00095     wc.lpfnWndProc = JSConsole::WindowProc;
00096     wc.cbClsExtra = 0;
00097     wc.cbWndExtra = 0;
00098     wc.hInstance = JSConsole::sAppInstance;
00099     wc.hIcon = NULL;
00100     wc.hCursor = NULL;
00101     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
00102     wc.lpszMenuName = MAKEINTRESOURCE(JSCONSOLE_MENU);
00103     wc.lpszClassName = "JavaScript Console";
00104 
00105     return (BOOL)::RegisterClass(&wc);
00106 }
00107 
00108 //
00109 // Create the main application window
00110 //
00111 JSConsole* JSConsole::CreateConsole()
00112 {
00113     if (!JSConsole::mRegistered){
00114         JSConsole::mRegistered = RegisterWidget();
00115     }
00116 
00117     HWND hWnd = ::CreateWindowEx(WS_EX_ACCEPTFILES | 
00118                                             WS_EX_CLIENTEDGE | 
00119                                             WS_EX_CONTROLPARENT,
00120                                 "JavaScript Console",
00121                                 JSConsole::sDefaultCaption,
00122                                 WS_OVERLAPPEDWINDOW,
00123                                 CW_USEDEFAULT,
00124                                 CW_USEDEFAULT,
00125                                 450,
00126                                 500,
00127                                 NULL,
00128                                 NULL,
00129                                 JSConsole::sAppInstance,
00130                                 NULL);
00131     if (hWnd) {
00132         ::ShowWindow(hWnd, SW_SHOW);
00133         ::UpdateWindow(hWnd);
00134 
00135         JSConsole *console = (JSConsole*)::GetWindowLong(hWnd, GWL_USERDATA);
00136         return console;
00137     }
00138 
00139     return NULL;
00140 }
00141 
00142 //
00143 // Window Procedure
00144 //
00145 LRESULT CALLBACK JSConsole::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
00146 {
00147     JSConsole *console = (JSConsole*)::GetWindowLong(hWnd, GWL_USERDATA);
00148     
00149     // just ignore the message unless is WM_NCCREATE
00150     if (!console) {
00151         if (uMsg == WM_NCCREATE) {
00152             HWND hWndEdit = ::CreateWindow("EDIT",
00153                                             NULL,
00154                                             WS_CHILDWINDOW | WS_VISIBLE | ES_MULTILINE |
00155                                                         ES_AUTOHSCROLL | ES_AUTOVSCROLL |
00156                                                         WS_HSCROLL | WS_VSCROLL,
00157                                             0, 0, 0, 0,
00158                                             hWnd,
00159                                             NULL,
00160                                             JSConsole::sAppInstance,
00161                                             NULL);
00162             if (!hWndEdit) {
00163                 ::DisplayError("Cannot Create Edit Window");
00164                 return FALSE;
00165             }
00166             ::SendMessage(hWndEdit, EM_SETLIMITTEXT, (WPARAM)0, (LPARAM)0);
00167 
00168             console = new JSConsole(hWnd, hWndEdit);
00169             ::SetWindowLong(hWnd, GWL_USERDATA, (DWORD)console);
00170 
00171             ::SetFocus(hWndEdit);
00172         }
00173 
00174 #if defined(STRICT)
00175         return ::CallWindowProc((WNDPROC)::DefWindowProc, hWnd, uMsg, 
00176                                 wParam, lParam);
00177 #else
00178         return ::CallWindowProc((FARPROC)::DefWindowProc, hWnd, uMsg, 
00179                                 wParam, lParam);
00180 #endif /* STRICT */
00181     }
00182 
00183     switch(uMsg) {
00184 
00185         // make sure the edit window covers the whole client area
00186         case WM_SIZE:
00187             return console->OnSize(wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam));
00188 
00189         // exit the application
00190         case WM_DESTROY:
00191             console->OnDestroy();
00192 
00193 #if defined(STRICT)
00194             return ::CallWindowProc((WNDPROC)::DefWindowProc, hWnd, uMsg, 
00195                                     wParam, lParam);
00196 #else
00197             return ::CallWindowProc((FARPROC)::DefWindowProc, hWnd, uMsg, 
00198                                     wParam, lParam);
00199 #endif /* STRICT */
00200 
00201         // enable/disable menu items
00202         case WM_INITMENUPOPUP:
00203             return console->OnInitMenu((HMENU)wParam, (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam));
00204 
00205         case WM_COMMAND:
00206             // menu or accelerator
00207             if (HIWORD(wParam) == 0 || HIWORD(wParam) == 1) {
00208                 switch(LOWORD(wParam)) {
00209 
00210                     case ID_FILENEW:
00211                         console->OnFileNew();
00212                         break;
00213 
00214                     case ID_FILEOPEN:
00215                         if (console->OpenFileDialog(OPEN_DIALOG)) {
00216                             console->LoadFile();
00217                         }
00218 
00219                         break;
00220 
00221                     case ID_FILESAVE:
00222                         if (console->CanSave()) {
00223                             console->SaveFile();
00224                             break;
00225                         }
00226                         // fall through so it can "Save As..."
00227 
00228                     case ID_FILESAVEAS:
00229                         if (console->OpenFileDialog(SAVE_DIALOG)) {
00230                             console->SaveFile();
00231                         }
00232                         break;
00233 
00234                     case ID_FILEEXIT:
00235                         ::DestroyWindow(hWnd);
00236                         break;
00237 
00238                     case ID_EDITUNDO:
00239                         console->OnEditUndo();
00240                         break;
00241 
00242                     case ID_EDITCUT:
00243                         console->OnEditCut();
00244                         break;
00245 
00246                     case ID_EDITCOPY:
00247                         console->OnEditCopy();
00248                         break;
00249 
00250                     case ID_EDITPASTE:
00251                         console->OnEditPaste();
00252                         break;
00253 
00254                     case ID_EDITDELETE:
00255                         console->OnEditDelete();
00256                         break;
00257 
00258                     case ID_EDITSELECTALL:
00259                         console->OnEditSelectAll();
00260                         break;
00261 
00262                     case ID_COMMANDSEVALALL:
00263                         console->OnCommandEvaluateAll();
00264                         break;
00265 
00266                     case ID_COMMANDSEVALSEL:
00267                         console->OnCommandEvaluateSelection();
00268                         break;
00269 
00270                     case ID_COMMANDSINSPECTOR:
00271                         console->OnCommandInspector();
00272                         break;
00273 
00274                 }
00275             }
00276 
00277             break;
00278 
00279         case WM_DROPFILES:
00280         {
00281             HDROP hDropInfo = (HDROP)wParam;
00282             if (::DragQueryFile(hDropInfo, (UINT)-1L, NULL, 0) != 1) {
00283                 ::MessageBox(hWnd, "Just One File Please...", "JSConsole Error", MB_OK | MB_ICONINFORMATION);
00284             }
00285             else {
00286                 CHAR fileName[MAX_PATH];
00287                 ::DragQueryFile(hDropInfo, 0, fileName, MAX_PATH);
00288                 console->SetFileName(fileName);
00289                 console->LoadFile();
00290             }
00291             break;
00292         }
00293 
00294         case WM_SETFOCUS:
00295             return console->OnSetFocus((HWND)wParam);
00296 
00297         default:
00298 #if defined(STRICT)
00299             return ::CallWindowProc((WNDPROC)::DefWindowProc, hWnd, uMsg, 
00300                                     wParam, lParam);
00301 #else
00302             return ::CallWindowProc((FARPROC)::DefWindowProc, hWnd, uMsg, 
00303                                     wParam, lParam);
00304 #endif /* STRICT */
00305     }
00306 
00307     return 0;
00308 }
00309 
00310 //
00311 // Constructor
00312 // The main window and the edit control must have been created already
00313 //
00314 JSConsole::JSConsole(HWND aMainWindow, HWND aEditControl) : 
00315                                     mMainWindow(aMainWindow), 
00316                                     mEditWindow(aEditControl),
00317                                     mContext(NULL)
00318 {
00319     mFileInfo.Init();
00320 }
00321 
00322 //
00323 // Destructor
00324 //
00325 JSConsole::~JSConsole()
00326 {
00327     NS_IF_RELEASE(mContext);
00328 }
00329 
00330 //
00331 // Load a file into the edit field
00332 //
00333 BOOL JSConsole::LoadFile()
00334 {
00335     BOOL result = FALSE;
00336 
00337     if (mMainWindow) {
00338         // open the file
00339         HANDLE file = ::CreateFile(mFileInfo.mCurrentFileName,
00340                                     GENERIC_READ,
00341                                     FILE_SHARE_READ,
00342                                     NULL,
00343                                     OPEN_EXISTING,
00344                                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
00345                                     NULL);
00346         if (file != INVALID_HANDLE_VALUE) {
00347             // check the file size. Max is 64k
00348             DWORD sizeHiWord;
00349             DWORD sizeLoWord = ::GetFileSize(file, &sizeHiWord);
00350             if (sizeLoWord < 0x10000 && sizeHiWord == 0) {
00351                 // alloc a buffer big enough to contain the file (account for '\0'
00352                 CHAR *buffer = new CHAR[sizeLoWord + 1]; 
00353                 if (buffer) {
00354                     // read the file in memory
00355                     if (::ReadFile(file,
00356                                     buffer,
00357                                     sizeLoWord,
00358                                     &sizeHiWord,
00359                                     NULL)) {
00360                         NS_ASSERTION(sizeLoWord == sizeHiWord, "ReadFile inconsistency");
00361                         buffer[sizeLoWord] = '\0'; // terminate the buffer 
00362                         // write the file to the edit field
00363                         ::SendMessage(mEditWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)buffer);
00364 
00365                         // update the caption
00366                         CHAR caption[80];
00367                         ::wsprintf(caption, 
00368                                     "%s - %s", 
00369                                     mFileInfo.mCurrentFileName + mFileInfo.mFileOffset,
00370                                     sDefaultCaption);
00371                         ::SendMessage(mMainWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)caption);
00372 
00373                         result = TRUE;
00374                     }
00375                     else {
00376                         ::DisplayError("Error Reading the File");
00377                     }
00378 
00379                     // free the allocated buffer
00380                     delete[] buffer;
00381                 }
00382                 else {
00383                     ::MessageBox(mMainWindow, 
00384                                 "Cannot Allocate Enough Memory to Copy the File in Memory", 
00385                                 "JSConsole Error", 
00386                                 MB_OK | MB_ICONSTOP);
00387                 }
00388             }
00389             else {
00390                 ::MessageBox(mMainWindow, 
00391                             "File too big. Max is 64k", 
00392                             "JSConsole Error", 
00393                             MB_OK | MB_ICONSTOP);
00394             }
00395 
00396             // close the file handle
00397             ::CloseHandle(file);
00398         }
00399 #ifdef _DEBUG
00400         else {
00401             CHAR message[MAX_PATH + 20];
00402             wsprintf(message, "Cannot Open File: %s", mFileInfo.mCurrentFileName); 
00403             ::DisplayError(message);
00404         }
00405 #endif
00406     }
00407 
00408     return result;
00409 }
00410 
00411 //
00412 // Save the current text into a file
00413 //
00414 BOOL JSConsole::SaveFile()
00415 {
00416     BOOL result = FALSE;
00417 
00418     if (mMainWindow && mFileInfo.mCurrentFileName[0] != '\0') {
00419         // create the new file
00420         HANDLE file = ::CreateFile(mFileInfo.mCurrentFileName,
00421                                     GENERIC_WRITE,
00422                                     FILE_SHARE_READ,
00423                                     NULL,
00424                                     CREATE_ALWAYS,
00425                                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
00426                                     NULL);
00427         if (file != INVALID_HANDLE_VALUE) {
00428             DWORD size;
00429             // get the text size
00430             size = ::SendMessage(mEditWindow, WM_GETTEXTLENGTH, (WPARAM)0, (LPARAM)0);
00431 
00432             // alloc a buffer big enough to contain the file
00433             CHAR *buffer = new CHAR[++size];
00434             if (buffer) {
00435                 DWORD byteRead;
00436                 // read the text area content
00437                 ::SendMessage(mEditWindow, WM_GETTEXT, (WPARAM)size, (LPARAM)buffer);
00438 
00439                 // write the buffer to disk
00440                 if (::WriteFile(file,
00441                                 buffer,
00442                                 size,
00443                                 &byteRead,
00444                                 NULL)) {
00445                     NS_ASSERTION(byteRead == size, "WriteFile inconsistency");
00446                     // update the caption
00447                     CHAR caption[80];
00448                     ::wsprintf(caption, 
00449                                 "%s - %s", 
00450                                 mFileInfo.mCurrentFileName + mFileInfo.mFileOffset,
00451                                 sDefaultCaption);
00452                     ::SendMessage(mMainWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)caption);
00453 
00454                     result = TRUE;
00455                 }
00456                 else {
00457                     ::DisplayError("Error Writing the File");
00458                 }
00459 
00460                 // free the allocated buffer
00461                 delete[] buffer;
00462             }
00463             else {
00464                 ::MessageBox(mMainWindow, 
00465                             "Cannot Allocate Enough Memory to Copy the Edit Text in Memory", 
00466                             "JSConsole Error", 
00467                             MB_OK | MB_ICONSTOP);
00468             }
00469 
00470             // close the file handle
00471             ::CloseHandle(file);
00472         }
00473 #ifdef _DEBUG
00474         else {
00475             CHAR message[MAX_PATH + 20];
00476             wsprintf(message, "Cannot Open File: %s", mFileInfo.mCurrentFileName); 
00477             ::DisplayError(message);
00478         }
00479 #endif
00480     }
00481 
00482     return result;
00483 }
00484 
00485 //
00486 // Open a FileOpen or FileSave dialog
00487 //
00488 BOOL JSConsole::OpenFileDialog(UINT aWhichDialog)
00489 {
00490     BOOL result = FALSE;
00491     OPENFILENAME ofn;
00492 
00493     if (mMainWindow) {
00494 
00495         // *.js is the standard File Name on the Save/Open Dialog
00496         if (mFileInfo.mCurrentFileName[0] == '\0') 
00497             ::strcpy(mFileInfo.mCurrentFileName, "*.js");
00498 
00499         // fill the OPENFILENAME sruct
00500         ofn.lStructSize = sizeof(OPENFILENAME);
00501         ofn.hwndOwner = mMainWindow;
00502         ofn.hInstance = JSConsole::sAppInstance;
00503         ofn.lpstrFilter = "JavaScript Files (*.js)\0*.js\0Text Files (*.txt)\0*.txt\0All Files\0*.*\0\0";
00504         ofn.lpstrCustomFilter = NULL; 
00505         ofn.nMaxCustFilter = 0;
00506         ofn.nFilterIndex = 1; // the first one in lpstrFilter
00507         ofn.lpstrFile = mFileInfo.mCurrentFileName; // contains the file path name on return
00508         ofn.nMaxFile = sizeof(mFileInfo.mCurrentFileName);
00509         ofn.lpstrFileTitle = NULL;
00510         ofn.nMaxFileTitle = 0;
00511         ofn.lpstrInitialDir = NULL; // use default
00512         ofn.lpstrTitle = NULL; // use default
00513         ofn.Flags = OFN_CREATEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
00514         ofn.nFileOffset = 0; 
00515         ofn.nFileExtension = 0;
00516         ofn.lpstrDefExt = "js"; // default extension is .js
00517         ofn.lCustData = NULL; 
00518         ofn.lpfnHook = NULL;
00519         ofn.lpTemplateName = NULL;
00520 
00521         // call the open file dialog or the save file dialog according to aIsOpenDialog
00522         if (aWhichDialog == OPEN_DIALOG) {
00523             result = ::GetOpenFileName(&ofn);
00524         }
00525         else if (aWhichDialog == SAVE_DIALOG) {
00526             result = ::GetSaveFileName(&ofn);
00527         }
00528     
00529         if (!result) {
00530             mFileInfo.mCurrentFileName[0] = '\0';
00531             ::CommDlgExtendedError();
00532         }
00533         else {
00534             mFileInfo.mFileOffset = ofn.nFileOffset;
00535             mFileInfo.mFileExtension = ofn.nFileExtension;
00536         }
00537     }
00538 
00539     return result;
00540 }
00541 
00542 //
00543 // set the mFileInfo structure with the proper value given a generic path
00544 //
00545 void JSConsole::SetFileName(LPSTR aFileName)
00546 {
00547     strcpy(mFileInfo.mCurrentFileName, aFileName);
00548     for (int i = strlen(aFileName); i >= 0; i--) {
00549         if (mFileInfo.mCurrentFileName[i] == '.') {
00550             mFileInfo.mFileExtension = i;
00551         }
00552 
00553         if (mFileInfo.mCurrentFileName[i] == '\\') {
00554             mFileInfo.mFileOffset = i + 1;
00555             break;
00556         }
00557     }
00558 }
00559 
00560 //
00561 // Move the edit window to cover the all client area
00562 //
00563 LRESULT JSConsole::OnSize(DWORD aResizeFlags, UINT aWidth, UINT aHeight)
00564 {
00565     ::MoveWindow(mEditWindow, 0, 0, aWidth, aHeight, TRUE);
00566     RECT textArea;
00567     textArea.left = 3;
00568     textArea.top = 1;
00569     textArea.right = aWidth - 20;
00570     textArea.bottom = aHeight - 17;
00571     ::SendMessage(mEditWindow, EM_SETRECTNP, (WPARAM)0, (LPARAM)&textArea);
00572     return 0L;
00573 }
00574 
00575 //
00576 // Initialize properly menu items
00577 //
00578 LRESULT JSConsole::OnInitMenu(HMENU aMenu, UINT aPos, BOOL aIsSystem)
00579 {
00580     if (!aIsSystem) {
00581         if (aPos == EDITMENUPOS) {
00582             InitEditMenu(aMenu);
00583         }
00584         else if (aPos == COMMANDSMENUPOS) {
00585             InitCommandMenu(aMenu);
00586         }
00587     }
00588 
00589     return 0L;
00590 }
00591 
00592 //
00593 // Pass the focus to the edit window
00594 //
00595 LRESULT JSConsole::OnSetFocus(HWND aWnd)
00596 {
00597     ::SetFocus(mEditWindow);
00598     return 0L;
00599 }
00600 
00601 //
00602 // Destroy message
00603 //
00604 void JSConsole::OnDestroy()
00605 {
00606     if (mDestroyNotification)
00607         (*mDestroyNotification)();
00608 }
00609 
00610 //
00611 // File/New. Reset caption, text area and file info
00612 //
00613 void JSConsole::OnFileNew()
00614 {
00615     SendMessage(mEditWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)0);
00616     SendMessage(mMainWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)JSConsole::sDefaultCaption);
00617     mFileInfo.Init();
00618 }
00619 
00620 //
00621 // Edit/Undo. Undo the last operation on the edit field
00622 //
00623 void JSConsole::OnEditUndo()
00624 {
00625     SendMessage(mEditWindow, WM_UNDO, (WPARAM)0, (LPARAM)0);
00626 }
00627 
00628 //
00629 // Edit/Cut. Cut the current selection
00630 //
00631 void JSConsole::OnEditCut()
00632 {
00633     SendMessage(mEditWindow, WM_CUT, (WPARAM)0, (LPARAM)0);
00634 }
00635 
00636 //
00637 // Edit/Copy. Copy the current selection
00638 //
00639 void JSConsole::OnEditCopy()
00640 {
00641     SendMessage(mEditWindow, WM_COPY, (WPARAM)0, (LPARAM)0);
00642 }
00643 
00644 //
00645 // Edit/Paste. Paste from the clipboard
00646 //
00647 void JSConsole::OnEditPaste()
00648 {
00649     SendMessage(mEditWindow, WM_PASTE, (WPARAM)0, (LPARAM)0);
00650 }
00651 
00652 //
00653 // Edit/Delete. Delete the current selection
00654 //
00655 void JSConsole::OnEditDelete()
00656 {
00657     SendMessage(mEditWindow, WM_CLEAR, (WPARAM)0, (LPARAM)0);
00658 }
00659 
00660 //
00661 // Edit/Select All. Select the whole text in the text area
00662 //
00663 void JSConsole::OnEditSelectAll()
00664 {
00665     //SendMessage(mEditWindow, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
00666 }
00667 
00668 //
00669 // Command/Evaluate All. Take the text area content and evaluate in the js context
00670 //
00671 void JSConsole::OnCommandEvaluateAll()
00672 {
00673     EvaluateText(0, (UINT)-1);
00674 }
00675 
00676 //
00677 // Command/Evaluate Selection. Take the current text area selection and evaluate in the js context
00678 //
00679 void JSConsole::OnCommandEvaluateSelection()
00680 {
00681     //
00682     // get the selection and evaluate it
00683     //
00684     DWORD startSel, endSel;
00685 
00686     // get selection range
00687     ::SendMessage(mEditWindow, EM_GETSEL, (WPARAM)&startSel, (LPARAM)&endSel);
00688 
00689     EvaluateText(startSel, endSel);
00690 }
00691 
00692 //
00693 // Command/Inspector. Run the js inspector on the global object
00694 //
00695 void JSConsole::OnCommandInspector()
00696 {
00697     ::MessageBox(mMainWindow, "Inspector not yet available", "JSConsole Error", MB_OK | MB_ICONINFORMATION);
00698 }
00699 
00700 //
00701 // Help
00702 //
00703 void JSConsole::OnHelp()
00704 {
00705 }
00706 
00707 //
00708 // private method. Deal with the "Edit" menu
00709 //
00710 void JSConsole::InitEditMenu(HMENU aMenu)
00711 {
00712     CHAR someText[2] = {'\0', '\0'}; // some buffer
00713 
00714     // set flags to "disable"
00715     UINT undoFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
00716     UINT cutFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
00717     UINT copyFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
00718     UINT pasteFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
00719     UINT deleteFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
00720     UINT selectAllFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
00721 
00722     // check if the edit area has any text
00723     SendMessage(mEditWindow, WM_GETTEXT, (WPARAM)2, (LPARAM)someText);
00724     if (someText[0] != '\0') {
00725         // enable the "Select All"
00726         selectAllFlags = MF_BYPOSITION | MF_ENABLED;
00727 
00728         // enable "Copy/Cut/Paste" if there is any selection
00729         UINT startPos, endPos;
00730         SendMessage(mEditWindow, EM_GETSEL, (WPARAM)&startPos, (LPARAM)&endPos);
00731         if (startPos != endPos) {
00732             cutFlags = MF_BYPOSITION | MF_ENABLED;
00733             copyFlags = MF_BYPOSITION | MF_ENABLED;
00734             deleteFlags = MF_BYPOSITION | MF_ENABLED;
00735         }
00736     }
00737 
00738     // undo is available if the edit control says so
00739     if (SendMessage(mEditWindow, EM_CANUNDO, (WPARAM)0, (LPARAM)0)) {
00740         undoFlags = MF_BYPOSITION | MF_ENABLED;
00741     }
00742 
00743     // check whether or not the clipboard contains text data
00744     if (IsClipboardFormatAvailable(CF_TEXT)) {
00745         pasteFlags = MF_BYPOSITION | MF_ENABLED;
00746     }
00747 
00748     // do enable/disable
00749     VERIFY(EnableMenuItem(aMenu, 
00750                             ID_EDITUNDO - ID_EDITMENU - 1, 
00751                             undoFlags),
00752             -1L,
00753             "Disable/Enable \"Undo\" Failed");
00754     VERIFY(EnableMenuItem(aMenu, 
00755                             ID_EDITCUT - ID_EDITMENU - 1, 
00756                             cutFlags),
00757             -1L,
00758             "Disable/Enable \"Cut\" Failed");
00759     VERIFY(EnableMenuItem(aMenu, 
00760                             ID_EDITCOPY - ID_EDITMENU - 1, 
00761                             copyFlags),
00762             -1L,
00763             "Disable/Enable \"Copy\" Failed");
00764     VERIFY(EnableMenuItem(aMenu, 
00765                             ID_EDITPASTE - ID_EDITMENU - 1, 
00766                             pasteFlags),
00767             -1L,
00768             "Disable/Enable \"Paste\" Failed");
00769     VERIFY(EnableMenuItem(aMenu, 
00770                             ID_EDITDELETE - ID_EDITMENU - 1, 
00771                             deleteFlags),
00772             -1L,
00773             "Disable/Enable \"Delete\" Failed");
00774     VERIFY(EnableMenuItem(aMenu, 
00775                             ID_EDITSELECTALL - ID_EDITMENU - 1, 
00776                             selectAllFlags),
00777             -1L,
00778             "Disable/Enable \"Select All\" Failed");
00779 }
00780 
00781 //
00782 // private method. Deal with the "Command" menu
00783 //
00784 void JSConsole::InitCommandMenu(HMENU aMenu)
00785 {
00786     CHAR someText[2] = {'\0', '\0'}; // some buffer
00787 
00788     // set flags to "disable"
00789     UINT evaluateAllFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
00790     UINT evaluateSelectionFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
00791 
00792     // check if the edit area has any text
00793     SendMessage(mEditWindow, WM_GETTEXT, (WPARAM)2, (LPARAM)someText);
00794     if (someText[0] != 0) {
00795         // if there is some text enable "Evaluate All"
00796         evaluateAllFlags = MF_BYPOSITION | MF_ENABLED;
00797 
00798         // enable "Evaluate Selection" if there is any selection
00799         UINT startPos, endPos;
00800         SendMessage(mEditWindow, EM_GETSEL, (WPARAM)&startPos, (LPARAM)&endPos);
00801         if (startPos != endPos) {
00802             evaluateSelectionFlags = MF_BYPOSITION | MF_ENABLED;
00803         }
00804     }
00805 
00806     // disable/enable commands
00807     VERIFY(EnableMenuItem(aMenu, 
00808                             ID_COMMANDSEVALALL - ID_COMMANDSMENU - 1, 
00809                             evaluateAllFlags),
00810             -1L,
00811             "Disable/Enable \"Evaluate All\" Failed");
00812     VERIFY(EnableMenuItem(aMenu, 
00813                             ID_COMMANDSEVALSEL - ID_COMMANDSMENU - 1, 
00814                             evaluateSelectionFlags),
00815             -1L,
00816             "Disable/Enable \"Evaluate Selection\" Failed");
00817 }
00818 
00819 //
00820 // normailize a buffer of char coming from a text area. 
00821 // Basically get rid of the 0x0D char
00822 //
00823 LPSTR NormalizeBuffer(LPSTR aBuffer)
00824 {
00825     // trim all the 0x0D at the beginning (should be 1 at most, but hey...)
00826     while (*aBuffer == 0x0D) {
00827         aBuffer++;
00828     }
00829 
00830     LPSTR readPointer = aBuffer;
00831     LPSTR writePointer = aBuffer;
00832     
00833     do {
00834         // compact the buffer if needed
00835         *writePointer = *readPointer;
00836 
00837         // skip the 0x0D
00838         if (*readPointer != 0x0D) {
00839             writePointer++;
00840         }
00841 
00842     } while (*readPointer++ != '\0');
00843 
00844     return aBuffer;
00845 }
00846 
00847 LPSTR PrepareForTextArea(LPSTR aBuffer, PRInt32 aSize)
00848 {
00849   PRInt32 count = 0;
00850   LPSTR newBuffer = aBuffer;
00851   LPSTR readPointer = aBuffer;
00852 
00853   // count the '\n'
00854   while (*readPointer != '\0' && (*readPointer++ != '\n' || ++count));
00855 
00856   if (0 != count) {
00857     readPointer = aBuffer;
00858     newBuffer = new CHAR[aSize + count + 1];
00859     LPSTR writePointer = newBuffer;
00860     while (*readPointer != '\0') {
00861       if (*readPointer == '\n') {
00862         *writePointer++ = 0x0D;
00863       }
00864       *writePointer++ = *readPointer++;
00865     }
00866     *writePointer = '\0';
00867   }
00868 
00869   return newBuffer;
00870 }
00871 
00872 //
00873 // Evaluate the text enclosed between startSel and endSel
00874 //
00875 void JSConsole::EvaluateText(UINT aStartSel, UINT aEndSel)
00876 {
00877     if (mContext) {
00878         // get the text size
00879         UINT size = ::SendMessage(mEditWindow, WM_GETTEXTLENGTH, (WPARAM)0, (LPARAM)0);
00880 
00881         // alloc a buffer big enough to contain the file
00882         CHAR *buffer = new CHAR[++size];
00883         if (buffer) {
00884 
00885             // get the whole text
00886             ::SendMessage(mEditWindow, WM_GETTEXT, (WPARAM)size, (LPARAM)buffer);
00887 
00888             // get the portion of the text to be evaluated
00889             if (aEndSel != (UINT)-1) {
00890                 size = aEndSel - aStartSel;
00891                 strncpy(buffer, buffer + aStartSel, size);
00892                 buffer[size] = '\0';
00893             }
00894             else {
00895                 aEndSel = size;
00896             }
00897         
00898 
00899             // change the 0x0D0x0A couple in 0x0A ('\n')
00900             // no new buffer allocation, the pointer may be in the middle 
00901             // of the old buffer though, so keep buffer to call delete
00902             LPSTR cleanBuffer = ::NormalizeBuffer(buffer);
00903 
00904             // evaluate the string
00905             nsAutoString returnValue;
00906             PRBool isUndefined;
00907             if (NS_SUCCEEDED(mContext->EvaluateString(NS_ConvertASCIItoUCS2(cleanBuffer), 
00908                                                       nsnull,
00909                                                       nsnull,
00910                                                       nsnull,
00911                                                       0,
00912                                                       nsnull,
00913                                                       &returnValue,
00914                                                       &isUndefined))) {
00915                 // output the result in the edit area
00916                 CHAR result[128];
00917                 LPSTR res = result;
00918                 int bDelete = 0;
00919 
00920                 JSContext *cx = (JSContext *)mContext->GetNativeContext();
00921                 char *str = ToNewCString(returnValue);
00922 
00923                 // make a string with 0xA changed to 0xD0xA
00924                 res = PrepareForTextArea(str, returnValue.Length());
00925                 if (res != str) {
00926                   bDelete = 1; // if the buffer was new'ed
00927                 }
00928 
00929                 // set the position at the end of the selection
00930                 ::SendMessage(mEditWindow, EM_SETSEL, (WPARAM)aEndSel, (LPARAM)aEndSel);
00931                 // write the result
00932                 ::SendMessage(mEditWindow, EM_REPLACESEL, (WPARAM)TRUE, (LPARAM)res);
00933                 // highlight the result
00934                 ::SendMessage(mEditWindow, EM_SETSEL, (WPARAM)aEndSel - 1, (LPARAM)(aEndSel + strlen(res)));
00935 
00936                 // deal with the "big string" case
00937                 if (bDelete > 0) {
00938                   delete[] res;
00939                 }
00940                 delete[] str;
00941 
00942                 // clean up a bit
00943                 JS_GC((JSContext *)mContext->GetNativeContext());
00944             }
00945             else {
00946                 ::MessageBox(mMainWindow, 
00947                                 "Error evaluating the Script", 
00948                                 "JSConsole Error", 
00949                                 MB_OK | MB_ICONERROR);
00950             }
00951 
00952             delete[] buffer;
00953         }
00954         else {
00955             ::MessageBox(mMainWindow, 
00956                         "Not Enough Memory to Allocate a Buffer to Evaluate the Script",
00957                         "JSConsole Error",
00958                         MB_OK | MB_ICONSTOP);
00959         }
00960     }
00961     else {
00962         ::MessageBox(mMainWindow, 
00963                     "Java Script Context not initialized",
00964                     "JSConsole Error",
00965                     MB_OK | MB_ICONSTOP);
00966     }
00967 }
00968 
00969