// @(#) 1.18 nt/src/samples/text/textplat.cpp, odtextpart, od96nt, odnt9712d 2/5/97 09:16:30 [3/21/97 17:41:58]
//====START_GENERATED_PROLOG======================================
//
//
//   COMPONENT_NAME: odtextpart
//
//   CLASSES: none
//
//   ORIGINS: 27
//
//
//   (C) COPYRIGHT International Business Machines Corp. 1995,1996
//   All Rights Reserved
//   Licensed Materials - Property of IBM
//   US Government Users Restricted Rights - Use, duplication or
//   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
//
//   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
//   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
//   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
//   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
//   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
//   OR PERFORMANCE OF THIS SOFTWARE.
//
//====END_GENERATED_PROLOG========================================
//

#ifndef SOM_ODShape_xh
#include <Shape.xh>
#endif

#ifndef SOM_ODFacet_xh
#include <Facet.xh>
#endif

#ifndef SOM_ODTransform_xh
#include <Trnsform.xh>
#endif

#include "indxcoll.hpp"

#include "iodtext.h"
#include "iodtext.xih"

//------------------------------------------------------------------------------
//                             ImportDlg
//
// - invokes the common Windows Open File dialog box to import an ASCII file.
// - returns the name of the file selected.
//------------------------------------------------------------------------------
ODBoolean PlatImportDlg(HWND hwnd, char* docName)
{
    char         szFile[kODMaxFileNameSize];
    OPENFILENAME of;

#if defined(__IBMCPP__) || defined(_IBMR2)
    nl_catd txNLSCat;       // NLS message catalog handle
    char *filterText;       // pointer for NLS message
    char *dialogText;       // pointer for NLS message
    setlocale(LC_ALL,"");

    txNLSCat=catopen(TX_NLS_CATALOG, NL_CAT_LOCALE); // Open NLS message catalog

    filterText = catgets(txNLSCat, TP_MSG_SET,
                         TP_MSG_DLG_FILTER, "All (*.*)\0*.*\0\0");
    dialogText = catgets(txNLSCat, TP_MSG_SET, TP_MSG_IMPORT_DLG, "Import");
#else
    char filterText[256] = "All (*.*)\0*.*\0\0";
    char dialogText[256] = "Import";
#endif

    // Initialize the OPENFILENAME control block to pass to the
    // File Open Dialog
    memset( &of, '\0', sizeof( of ) );
    of.lStructSize      = sizeof( OPENFILENAME );
    of.hwndOwner        = hwnd;

    // Set the initial file filter from the message catalog.  The
    // default is to display all files (*.*).
    of.lpstrFilter      = (LPSTR) filterText;
    of.nFilterIndex     = 1; // Display first filter
    of.lpstrInitialDir  = NULL;  // default to current directory

    // Indicate to the API to fill in the file name before
    // returning.
    lstrcpy( szFile, "*.*" );
    of.lpstrFile        = (LPSTR) szFile;
    of.nMaxFile         = sizeof(szFile);
    of.lpstrDefExt      = NULL;  // Don't append a default extension

    // Fill in the File Dialog title.
    of.lpstrTitle       = (LPSTR) dialogText;

    // Fill in the flags - OFN_SHOWHELP
    of.Flags            = OFN_FILEMUSTEXIST |  // File must already exist
                    OFN_EXPLORER;        // Use new-style dialog box

    // Now we're ready to ask for the filename to import

    if (GetOpenFileName(&of))
    {
      lstrcpy((LPSTR) docName, of.lpstrFile);
#ifdef ODDebug
      PRINT ("Document Name is %s\n",docName);
#endif
      return kODTrue;
    } // endif GetOpenFileName
    else
       return kODFalse;
}


//------------------------------------------------------------------------------
//                           ExportDlg
// - invokes the common Windows Save File dialog box to obtain a file
//   name to export TextPart's data.
// - returns the name of the file specified.
//------------------------------------------------------------------------------
ODBoolean PlatExportDlg(HWND hwnd, char* docName)
{

    char         szFile[kODMaxFileNameSize];
    OPENFILENAME of;
    ODBoolean    result = kODFalse;

#if defined(__IBMCPP__) || defined(_IBMR2)
    nl_catd txNLSCat;       // NLS message catalog handle
    char *filterText;       // pointer for NLS message
    char *dialogText;       // pointer for NLS message
    setlocale(LC_ALL,"");

    txNLSCat=catopen(TX_NLS_CATALOG, NL_CAT_LOCALE); // Open NLS message catalog

    filterText = catgets(txNLSCat, TP_MSG_SET,
                         TP_MSG_DLG_FILTER, "All (*.*)\0*.*\0\0");
    dialogText = catgets(txNLSCat, TP_MSG_SET, TP_MSG_EXPORT_DLG, "Export");
#else
    char filterText[256] = "All (*.*)\0*.*\0\0";
    char dialogText[256] = "Export";
#endif

    // Initialize the OPENFILENAME control block to pass to the
    // File Open Dialog
    memset( &of, '\0', sizeof( of ) );
    of.lStructSize      = sizeof( OPENFILENAME );
    of.hwndOwner        = hwnd;

    // Set the initial file filter from the message catalog.  The
    // default is to display all files (*.*).
    of.lpstrFilter      = (LPSTR) filterText;
    of.nFilterIndex     = 1; // Display first filter
    of.lpstrInitialDir  = NULL;  // default to current directory

    // Indicate to the API to fill in the file name before
    // returning.
    lstrcpy( szFile, "*.*" );
    of.lpstrFile        = (LPSTR) szFile;
    of.nMaxFile         = sizeof(szFile);
    of.lpstrDefExt      = NULL;  // Don't append a default extension

    // Fill in the File Dialog title.
    of.lpstrTitle       = (LPSTR) dialogText;

    // Fill in the flags - OFN_SHOWHELP
    of.Flags            = OFN_PATHMUSTEXIST |  // Path must already exist
                    OFN_OVERWRITEPROMPT |  // Ask before overlaying a file
                    OFN_EXPLORER;        // Use new-style dialog box

    // Now we're ready to ask for the filename to export
    if (GetSaveFileName(&of))
    {
      lstrcpy((LPSTR) docName, of.lpstrFile);
#ifdef ODDebug
      PRINT ("Document Name is %s\n",docName);
#endif
      result = kODTrue;
    } // endif GetSaveFileName

    return result;
}


//------------------------------------------------------------------------------
//                             ReadFile
//
// - allocates a buffer to read in the requested file.
// - reads the requested file into the specified buffer.
// - returns the buffer with the file contents.
//------------------------------------------------------------------------------
ODBoolean PlatReadFile(char* docName, char* &buffer, int &bytesRead)
{
    HANDLE hFile;
    DWORD dwFileSize;
    DWORD dwBytesRead;

    // If no file is read in, buffer will be empty
    buffer = NULL;
    dwBytesRead = 0;

    // Open the file.
    if ((hFile = CreateFile((LPCTSTR) docName,
          GENERIC_READ,
          FILE_SHARE_READ,
          NULL,
          OPEN_EXISTING,
          FILE_ATTRIBUTE_READONLY,
          (HANDLE)NULL))  // Win95 requires this be null.
       == kODNoFileRefNum)
    {
#ifdef ODDebug
      PRINT ("File open failed for %s\n",docName);
#endif
      return kODFalse;
    }

    // Get the size of the file.
    dwFileSize = GetFileSize(hFile, NULL);
    if (dwFileSize == 0xFFFFFFFF)
    {
#ifdef ODDebug
      PRINT ("GetFileSize failed for %s\n",docName);
#endif
      return kODFalse;
    }

    // Allocate a buffer for the file to be read into.
    buffer = (char *)SOMMalloc(dwFileSize);
    if (buffer == NULL)
    {
#ifdef ODDebug
      PRINT ("SOMMalloc failed!\n");
#endif
      CloseHandle( hFile );
      return kODFalse;
    }

    // Read the file contents into a buffer.
    ReadFile(hFile,(LPVOID)buffer, dwFileSize, &dwBytesRead, NULL);
    bytesRead = dwBytesRead;

    if (dwBytesRead == 0)
    {
#ifdef ODDebug
      PRINT ("Zero bytes read.\n");
#endif
      CloseHandle( hFile );
      SOMFree(buffer);
      return kODFalse;
    }

    // Close the file
    CloseHandle( hFile );
#ifdef ODDebug
    PRINT ("File was successfully read.\n");
#endif
    return kODTrue;
}


//------------------------------------------------------------------------------
//                             WriteFile
//
// - creates the requested file.
// - writes data from the specified buffer into the file.
// - does not deallocate the buffer.
//------------------------------------------------------------------------------
ODBoolean PlatWriteFile(char* docName, char* buffer, int bufferSize)
{
    HANDLE hFile;
    DWORD dwBytesWritten = 0;

    // Open the file.
    if ((hFile = CreateFile((LPCTSTR) docName,
          GENERIC_WRITE,
          FILE_SHARE_WRITE,
          NULL,
          CREATE_ALWAYS,
          FILE_ATTRIBUTE_NORMAL,
          (HANDLE)NULL))  // Win95 requires this be null.
       == kODNoFileRefNum)
    {
#ifdef ODDebug
      PRINT ("File create failed for %s\n",docName);
#endif
      return kODFalse;
    }

    // Write the file contents from the buffer.
    WriteFile(hFile,           // file handle
              (LPCVOID)buffer, // data to write
              bufferSize,      // number bytes to write
              &dwBytesWritten, // bytes successfully written
              NULL);

    if (dwBytesWritten == 0)
    {
#ifdef ODDebug
      PRINT ("Zero bytes written.\n");
#endif
      CloseHandle( hFile );
      return kODFalse;
    }

    // Close the file
    CloseHandle( hFile );
#ifdef ODDebug
    PRINT ("File was successfully written.\n");
#endif
    return kODTrue;
}

//------------------------------------------------------------------------------
//                        TextMgr::DrawText
//
// - sets up the drawing environment
// - draws all text for the specified area
//------------------------------------------------------------------------------
void TextMgr::DrawText(HDC hdc, ODRect clipRect)
{
  RECT frameRect;
  HFONT hfnt, hOldFont;
  int numPara = paragraphs->Count();
  int firstParI;
  int lastParI;

  // if there is no text to draw, no sense in continuing
  if (!HasText())
     return;

  SetRect(&frameRect, FixedToInt(clipRect.left),
                      FixedToInt(clipRect.top),
                      FixedToInt(clipRect.right),
                      FixedToInt(clipRect.bottom));

   // Figure out which paragraphs to draw, based on the clip region
   firstParI = frameRect.top / myFont.height;
   lastParI = min(numPara-1, frameRect.bottom / myFont.height);

   // Write the text on the screen using a transparent background and
   // black text.  Use a fixed font to determine cursor position easier.
   SetBkMode(hdc, TRANSPARENT);
   SetTextColor(hdc, 0x00000000);
   hfnt = GetStockObject(ANSI_FIXED_FONT);
   hOldFont = SelectObject(hdc, hfnt);

   // Adjust the clip region to correspond to one line of text
   frameRect.bottom = frameRect.top + myFont.height;

   // Iterate through each paragraph to draw its text
   for (int i=firstParI; i <= lastParI; i++)
   {
       Paragraph* para = (Paragraph*) paragraphs->FromIndex(i);
       para->DrawText(hdc, frameRect, myFont.width, myFont.ascender);

       // Set up the clip region for the line of text below this one.
       frameRect.top  = frameRect.bottom + myFont.externalLeading;
       frameRect.bottom = frameRect.top + myFont.height;
   } /* endfor */

   // Restore the old font.
   SelectObject(hdc, hOldFont);
}

//------------------------------------------------------------------------------
//                        TextMgr::DisplayCursor
//
// - Displays the cursor in the given window
//------------------------------------------------------------------------------
void TextMgr::DisplayCursor(ODFacet* facet, ODRect clipRect)
{
   RECT frameRect;
   POINT origin;
   ODPoint odOrigin;
   int numPara = paragraphs->Count();
   int firstParI;
   int lastParI;

   // Hide the cursor first, in case the caret position has changed.
   // This prevents residual cursor lines from being left on screen.
   HWND hwnd = facet->GetFacetHWND(myEv);
//   HideCaret(hwnd);

   // Figure out which paragraphs are drawn, based on the clip region
   SetRect(&frameRect, FixedToInt(clipRect.left),
                       FixedToInt(clipRect.top),
                       FixedToInt(clipRect.right),
                       FixedToInt(clipRect.bottom));
   firstParI = frameRect.top / myFont.height;
   lastParI = min(numPara-1, frameRect.bottom / myFont.height);

   // Use the paragraph range to determine if we need to display the cursor
   if (currentPos.row >= firstParI && currentPos.row <= lastParI)
   {
     // Determine the cursor position relative to the frame
     origin.x = frameRect.left + currentPos.column * myFont.width;
     origin.y = currentPos.row * myFont.height;

     // Convert the cursor position to ODPoint coordinates and transform it
     // to our window coordinates.
     odOrigin.x = IntToFixed(origin.x);
     odOrigin.y = IntToFixed(origin.y);
     TempODTransform xform = facet->AcquireWindowContentTransform(myEv, kODNULL);
     xform->TransformPoint (myEv, &odOrigin);

     // Convert the position back to integer coordinates, and set the
     // cursor position.
     origin.x = FixedToInt(odOrigin.x);
     origin.y = FixedToInt(odOrigin.y);
     SetCaretPos(origin.x, origin.y);
   }

   ShowCaret(hwnd);  // Execute this to balance the HideCaret.
}

//------------------------------------------------------------------------------
//                        TextMgr::SetCursor
//
// - Sets the cursor to the input position
//------------------------------------------------------------------------------
void TextMgr::SetCursor(HWND hwnd, ODPoint cursor, ODRect cliprect)
{
// POINT origin;
   // Ensure that the cursor does not go beyond the last row of text.
   currentPos.row = min(FixedToInt(cursor.y) / myFont.height,
                        paragraphs->Count()-1);
   // Set the current paragraph pointer so we can ensure the cursor does
   // not go beyond the last column of the paragraph.
   currentParagraph = (Paragraph*) paragraphs->FromIndex(currentPos.row);
   currentPos.column = min(FixedToInt(cursor.x) / myFont.width,
                           currentParagraph->GetTextSize());
//   HideCaret(hwnd);  // Hide first in case caret position has changed
//   SetCaretPos(origin.x, origin.y);
//   ShowCaret(hwnd);
}

//------------------------------------------------------------------------------
//                        TextMgr::SetFont
//
// - chooses a font for the drawing environment
// - stores the necessary font metrics
//------------------------------------------------------------------------------
void TextMgr::SetFont(HDC hdc, int printing)
{
   HFONT hfnt, hOldFont;
   TEXTMETRIC tm;

   // Store a copy of the metrics for the default font
   hfnt = GetStockObject(ANSI_FIXED_FONT);
   hOldFont = SelectObject(hdc, hfnt);
   GetTextMetrics(hdc, &tm);
   // Restore the old font.
   SelectObject(hdc, hOldFont);

   lstrcpy(myFont.name,"");
   myFont.id = 0;
   myFont.width = tm.tmMaxCharWidth;
   myFont.height = tm.tmHeight + tm.tmExternalLeading;
   myFont.externalLeading = tm.tmExternalLeading;
   myFont.descender = tm.tmDescent;
   myFont.ascender = tm.tmAscent;
}

//------------------------------------------------------------------------------
//                        TextMgr::CreateCursor
//
// - creates a system cursor for the given window
//------------------------------------------------------------------------------
void TextMgr::CreateCursor(HWND hwnd)
{
#ifdef ODDebug
     PRINT("TextMgr::CreateCursor invoked.\n");
#endif
     // Create the cursor.  Specify (HBITMAP) NULL to get a solid cursor.
     // Specify the width as 0 to use the default
     CreateCaret(hwnd, (HBITMAP) NULL, 0, myFont.height - myFont.externalLeading);
     SetCaretPos(currentPos.column, currentPos.row * myFont.height);
     // Default is to HideCaret
     ShowCaret(hwnd);
}

//------------------------------------------------------------------------------
//                        TextMgr::DeleteCursor
//
// - deletes the system cursor for the given window
//------------------------------------------------------------------------------
void TextMgr::DeleteCursor(HWND hwnd)
{
#ifdef ODDebug
     PRINT("TextMgr::DeleteCursor invoked.\n");
#endif
     // Hide and destroy the caret when the window loses the keyboard focus.
     HideCaret(hwnd);  // Remove residual cursor
     DestroyCaret();
}

//------------------------------------------------------------------------------
//                        Paragraph::DrawText
//
// - draws this paragraph's text for the specified area
//------------------------------------------------------------------------------
void Paragraph::DrawText(HDC hdc, RECT frameRect, int fontWidth, int fontAsc)
{
   int startChar = frameRect.left / fontWidth;
   int numChars = textBufferSize - startChar;

   // Make sure we have characters to output.
   if (numChars > 0) {
      TextOut(hdc,
          frameRect.left,              // x origin
          frameRect.top,               // y origin
          textBuffer + startChar,      // starting address
          numChars);
   }
}
