pjs/cmd/winfe/edlayout.cpp

698 строки
24 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// edlayout.cpp : implementation of Editor-only FE layout functions
//
// Created 9/16/96 by CLM
//
#include "stdafx.h"
#include "dialog.h"
#include "mainfrm.h"
#include "netsvw.h"
#include "edview.h"
// Utility function to check if it is OK to show the caret. It is used in the FE functions that
// are called by the back end to show caret.
static BOOL IsOkToShowCaret(CNetscapeEditView *pView, char *FE_FuncName)
{
// Prevent setting caret when we have a modal (e.g. property) dialog over us
CFrameWnd *pFrame = pView->GetFrame()->GetFrameWnd();
if(pFrame && pFrame->GetLastActivePopup() != pFrame){
return FALSE;
}
// This will allow us to use the regular caret for drop-point feedback
// even if window does not have focus
// Don't display caret if the editor view does not have focus.
// unless we are dragging over our view
if( !pView->m_bDragOver && pView->GetFocus() != pView ){
return FALSE;
}
return TRUE; // OK to show caret
}
// Caret used during Drag&Drop - more visible than regular cursor
extern CBitmap ed_DragCaretBitmap;
#define ED_DRAGCARET_HEIGHT 32
static void AdjustForDragCaret(CCaret * pCaret)
{
// NOTE: This assumes IDB_ED_DRAG bitmap is 3 by ED_DRAGCARET_HEIGHT
// and regular caret is 2 pixels wide
// Back off one on X
pCaret->x--;
// Only one size for drop caret, so reposition vertically
pCaret->y -= ED_DRAGCARET_HEIGHT - pCaret->height;
pCaret->width = 3;
pCaret->height = ED_DRAGCARET_HEIGHT;
}
//
// The text has been drawn. This code will position the text caret in the
// proper position and height.
//
PUBLIC void
FE_DisplayTextCaret(MWContext * context, int loc, LO_TextStruct * text_data,
int char_offset)
{
if(!context || !EDT_IS_EDITOR(context) || !text_data || !text_data->text_attr)
return;
int32 xVal, yVal;
CWinCX *pWinCX = VOID2CX(context->fe.cx, CWinCX);
CNetscapeEditView * pView = (CNetscapeEditView *)pWinCX->GetView();
HDC hDC = pView->GetContextDC();
int32 xOrigin = WINCX(context)->GetOriginX();
// Figure out X and Y coords of the text in the View's coordinate system
xVal = text_data->x + text_data->x_offset - xOrigin;
yVal = text_data->y + text_data->y_offset - WINCX(context)->GetOriginY();
CPoint cDocPoint(CASTINT(text_data->x), CASTINT(text_data->y));
if( pWinCX->PtInSelectedRegion(cDocPoint) && pView->m_caret.bEnabled ){
DestroyCaret();
pView->m_caret.cShown = 0;
pView->m_caret.bEnabled = FALSE;
}
// Only draw text that falls within the curently viewed part of the frame
if((yVal < pWinCX->GetHeight()) && (yVal < 32000)) {
// Get the font so we can get its height
CyaFont *pMyFont;
pWinCX->SelectNetscapeFontWithCache( hDC, text_data->text_attr, pMyFont );
// Calculate offset within text
CSize cSize(0,0);
const char *pText = (const char*)text_data->text;
// Win16 barfs on this if null string
if( char_offset > 0 && pText && *pText != '\0' ){
pWinCX->ResolveTextExtent(hDC, pText, char_offset, &cSize, pMyFont );
}
pWinCX->ReleaseNetscapeFontWithCache( hDC, pMyFont );
//
// The caret has changed position and size. Buffer the size and position
// information
//
// Place caret half way between characters,
pView->m_caret.x = CASTINT(xVal + cSize.cx);
pView->m_caret.y = CASTINT(yVal);
pView->m_caret.x = CASTINT(xVal + cSize.cx);
pView->m_caret.width = 2;
pView->m_caret.height = CASTINT(text_data->height);
if(pView->m_bDragOver)
AdjustForDragCaret(&(pView->m_caret));
// Don't show the caret if it is not ok to do so.
if (!IsOkToShowCaret(pView, "FE_DisplayTextCaret"))
return;
if(pView->m_bDragOver)
pView->CreateCaret(&ed_DragCaretBitmap);
else
pView->CreateSolidCaret(pView->m_caret.width, pView->m_caret.height);
pView->SetCaretPos(CPoint(pView->m_caret.x, pView->m_caret.y));
pView->m_caret.cShown = 1;
pView->m_caret.bEnabled = TRUE;
//TRACE0( "FE_DisplayTextCaret: Caret created and enabled\n");
pView->ShowCaret();
pView->SetEditChanged();
} // end of if-then for when text was visible
}
//
// The text has been drawn. This code will position the text carret in the
// proper position and height.
//
PUBLIC void
FE_DisplayImageCaret(MWContext * context, LO_ImageStruct *pLoImage,
ED_CaretObjectPosition pos)
{
int32 xVal, yVal;
if(!context || !pLoImage )
return;
CWinCX *pWinCX = VOID2CX(context->fe.cx, CWinCX);
CNetscapeEditView * pView = (CNetscapeEditView *)pWinCX->GetView();
xVal = pLoImage->x + pLoImage->x_offset - WINCX(context)->GetOriginX();
yVal = pLoImage->y - WINCX(context)->GetOriginY();
CPoint cDocPoint(CASTINT(pLoImage->x), CASTINT(pLoImage->y));
if( pWinCX->PtInSelectedRegion(cDocPoint) && pView->m_caret.bEnabled ){
DestroyCaret();
pView->m_caret.cShown = 0;
pView->m_caret.bEnabled = FALSE;
}
// Only draw text that falls within the curently viewed part of the frame
if((yVal < pWinCX->GetHeight()) && (yVal < 32000)) {
pView->m_caret.width = 2;
// Constrain caret size to between 10 and 40 else it looks too weird
if( pLoImage->line_height > 40 ){
pView->m_caret.height = 40;
yVal += (pLoImage->line_height - 45);
} else if( pLoImage->line_height < 10 ){
pView->m_caret.height = 10;
yVal -= (10 - pLoImage->line_height);
} else {
pView->m_caret.height = CASTINT(pLoImage->line_height);
}
pView->m_caret.y = CASTINT(yVal);
pView->m_caret.x = CASTINT(xVal);
if( pos == ED_CARET_BEFORE ){
pView->m_caret.x -= 1;
}
else if( pos == ED_CARET_AFTER ){
pView->m_caret.x += CASTINT(pLoImage->width + 2 * pLoImage->border_width);
}
else {
pView->m_caret.width = CASTINT(pLoImage->width+ 2 * pLoImage->border_width);
}
if(pView->m_bDragOver)
AdjustForDragCaret(&(pView->m_caret));
// Don't show the caret if it is not ok to do so.
if (!IsOkToShowCaret(pView, "FE_DisplayImageCaret"))
return;
if(pView->m_bDragOver)
pView->CreateCaret(&ed_DragCaretBitmap);
else
pView->CreateSolidCaret(pView->m_caret.width, pView->m_caret.height);
pView->SetCaretPos(CPoint(pView->m_caret.x, pView->m_caret.y));
pView->m_caret.cShown = 1;
pView->m_caret.bEnabled = TRUE;
pView->ShowCaret();
((CNetscapeEditView*)pView)->SetEditChanged();
}
}
PUBLIC void
FE_DisplayGenericCaret(MWContext * context, LO_Any *pLoAny,
ED_CaretObjectPosition pos)
{
int32 xVal, yVal;
if(!context || !pLoAny )
return;
CWinCX *pWinCX = VOID2CX(context->fe.cx, CWinCX);
CNetscapeEditView * pView = (CNetscapeEditView *)pWinCX->GetView();
// Figure out X and Y coords of the text
xVal = pLoAny->x + pLoAny->x_offset - WINCX(context)->GetOriginX();
yVal = pLoAny->y - WINCX(context)->GetOriginY();
CPoint cDocPoint(CASTINT(pLoAny->x), CASTINT(pLoAny->y));
if( pWinCX->PtInSelectedRegion(cDocPoint) && pView->m_caret.bEnabled ){
DestroyCaret();
pView->m_caret.cShown = 0;
pView->m_caret.bEnabled = FALSE;
}
// Only draw text that falls within the curently viewed part of the frame
if((yVal < pWinCX->GetHeight()) && (yVal < 32000)) {
pView->m_caret.width = 2;
// Constrain caret size to between 10 and 40 else it looks too weird
if( pLoAny->line_height > 40 ){
pView->m_caret.height = 40;
yVal += (pLoAny->line_height - 45);
} else if( pLoAny->line_height < 10 ){
pView->m_caret.height = 10;
yVal -= (10 - pLoAny->line_height);
} else {
pView->m_caret.height = CASTINT(pLoAny->line_height);
}
pView->m_caret.y = CASTINT(yVal);
pView->m_caret.x = CASTINT(xVal);
if( pos == ED_CARET_BEFORE ){
pView->m_caret.x -= 1;
}
else if( pos == ED_CARET_AFTER ){
pView->m_caret.x += CASTINT(pLoAny->width);
}
else {
pView->m_caret.width = CASTINT(pLoAny->width);
}
if(pView->m_bDragOver)
AdjustForDragCaret(&(pView->m_caret));
// Don't show the caret if it is not ok to do so.
if (!IsOkToShowCaret(pView, "FE_DisplayGenericCaret"))
return;
if(pView->m_bDragOver)
pView->CreateCaret(&ed_DragCaretBitmap);
else
pView->CreateSolidCaret(pView->m_caret.width, pView->m_caret.height);
pView->SetCaretPos(CPoint(pView->m_caret.x, pView->m_caret.y));
pView->m_caret.cShown = 1;
pView->m_caret.bEnabled = TRUE;
pView->ShowCaret();
((CNetscapeEditView*)pView)->SetEditChanged();
}
}
// Note: The returned coordinates do NOT account for the current window
// scroll. So you can't use them to set the windows caret until after
// you subtract the current window WINCX(context)->GetOriginXXX
PUBLIC Bool
FE_GetCaretPosition(MWContext *context, LO_Position* where,
int32* caretX, int32* caretYLow, int32* caretYHigh)
{
if(!context || !EDT_IS_EDITOR(context) || !where->element )
return FALSE;
HDC hDC;
CWinCX *pWinCX = VOID2CX(context->fe.cx, CWinCX);
CNetscapeEditView * pView = (CNetscapeEditView *)pWinCX->GetView();
int32 xVal = where->element->lo_any.x + where->element->lo_any.x_offset;
int32 yVal = where->element->lo_any.y;
int32 yValHigh = yVal + where->element->lo_any.height;
switch ( where->element->type )
{
case LO_TEXT:
{
LO_TextStruct* text_data = & where->element->lo_text;
if ( ! text_data->text_attr ) return FALSE;
hDC = pView->GetContextDC();
CyaFont *pMyFont;
pWinCX->SelectNetscapeFontWithCache( hDC, text_data->text_attr, pMyFont );
SIZE cSize;
pWinCX->ResolveTextExtent(hDC, (const char*)text_data->text,
CASTINT(where->position), &cSize, pMyFont );
xVal += cSize.cx - 1;
// ??? Do we need to restore the old font ???
pWinCX->ReleaseNetscapeFontWithCache( hDC, pMyFont );
}
break;
case LO_IMAGE:
{
LO_ImageStruct *pLoImage = & where->element->lo_image;
if( where->position == 0 ){
xVal -= 1;
}
else {
xVal += pLoImage->width + 2 * pLoImage->border_width;
}
}
break;
default:
{
LO_Any *any = & where->element->lo_any;
if( where->position == 0 ){
xVal -= 1;
}
else {
xVal += any->width;
}
}
}
*caretX = xVal;
*caretYLow = yVal;
*caretYHigh = yValHigh;
return TRUE;
}
PUBLIC void
FE_DestroyCaret(MWContext * context)
{
CNetscapeEditView * pView = (CNetscapeEditView *)WINCX(context)->GetView();
if(!context || pView->GetFocus() != pView ){
// TRACE0( "FE_DestroyCaret called, but CNetscapeEditView does not have focus\n");
return;
}
DestroyCaret();
pView->m_caret.cShown = 0;
pView->m_caret.bEnabled = FALSE;
}
PUBLIC void
FE_ShowCaret(MWContext * context)
{
CNetscapeEditView * pView = NULL;
if(!context)
return;
pView = (CNetscapeEditView *)WINCX(context)->GetView();
pView->m_caret.cShown = 1;
pView->m_caret.bEnabled = TRUE;
//TRACE0( "FE_ShowCaret: Caret created and enabled\n");
pView->ShowCaret();
}
//
// FE_DocumentChanged.
// The editor is telling the front end that the document has changed. This
// occurs in the normal process of editing. At any time, changeHeight can
// be -1 which means grey to the end of the window. This routine has to
// map document coordinates into window coordinates.
//
// There are 4 possible cases here:
//
/*****
case 0:
0,0---------------------------------
| |
| |
| |
|===============================| windowStart
| | |
changeStartY |-------------------------------| |
| |///////////////////////////////| |
changeHeight | |///////////////////////////////| | windowHeight
| |///////////////////////////////| |
V |-------------------------------| |
| | |
|===============================| V
| |
| |
| |
--------------------------------- N,N
case 1:
0,0---------------------------------
| |
| |
|===============================| windowStart
| | |
changeStartY |-------------------------------| |
| |///////////////////////////////| |
changeHeight | |///////////////////////////////| | windowHeight
| |///////////////////////////////| |
| |///////////////////////////////| |
| |///////////////////////////////| |
| |===============================| V
| | |
V |-------------------------------|
| |
| |
--------------------------------- N,N
case 2:
0,0---------------------------------
| |
| |
changeStartY |-------------------------------|
| | |
| | |
| |===============================| windowStart
| |///////////////////////////////| |
| |///////////////////////////////| |
| |///////////////////////////////| |
changeHeight | |///////////////////////////////| | windowHeight
| |///////////////////////////////| |
| |///////////////////////////////| |
| |///////////////////////////////| |
| |===============================| V
| | |
V |-------------------------------|
| |
| |
--------------------------------- N,N
case 3:
0,0---------------------------------
| |
| |
changeStartY |-------------------------------|
| | |
| | |
| |===============================| windowStart
| |///////////////////////////////| |
| |///////////////////////////////| |
changeHeight | |///////////////////////////////| | windowHeight
| |///////////////////////////////| |
V |-------------------------------| |
| | |
|===============================| V
| |
| |
| |
--------------------------------- N,N
****/
PUBLIC void
FE_DocumentChanged(MWContext * context, int32 changeStartY, int32 changeHeight )
{
CNetscapeEditView * pView = NULL;
int32 left,right , windowStartY, windowHeight, windowBottom, changeBottom;
int32 top, bottom;
CWinCX *pWinCX = VOID2CX(context->fe.cx, CWinCX);
pView = (CNetscapeEditView *)pWinCX->GetView();
HDC hDC = pView->GetContextDC();
// Figure out X and Y coords of the text
// Figure out X and Y coords of the text
left = 0;
right = pWinCX->GetWidth();
windowStartY = WINCX(context)->GetOriginY();
windowHeight = pWinCX->GetHeight();
windowBottom = windowStartY + pWinCX->GetHeight();
// if the window is before the start of any change, then we don't need to
// redisplay anything.
if( windowBottom < changeStartY ){
return;
}
// make sure there is an overlap
if( changeHeight == -1 ){
changeBottom = windowBottom;
}
else {
changeBottom = changeStartY + changeHeight;
}
if( changeBottom < windowStartY ){
return;
}
// If window width is expanded past document width,
// the horizontal scroll bar disappears.
// If X origin is not 0, then we have a bad view and
// you can't get to doc region to the left of window edge.
// Detect this and reset X origin to 0 to reposition the view
// Note: SetDocPosition, Scroll, etc. all ignore this case
// (Browser works by always doing NET_GetURL, which resets origins)
if( pWinCX->GetDocumentWidth() < pWinCX->GetWidth() ){
pWinCX->m_lOrgX = 0;
}
//TODO: Is this the correct way to handle problem of not
// refreshing area below the doc?
// (Happens when doc shrinks (e.g., delete stuff) to less than window height)
if( pWinCX->GetDocumentHeight() < pWinCX->GetHeight() ){
bottom = windowBottom;
}
top = max( windowStartY, changeStartY ) - windowStartY;
bottom = min( windowBottom, changeBottom ) - windowStartY;
RECT rect;
::SetRect(&rect, CASTINT(left), CASTINT(top), CASTINT(right), CASTINT(bottom));
::LPtoDP(hDC, (POINT*)&rect, 2);
pView->InvalidateRect( &rect, TRUE );
}
void WFE_HideEditCaret(MWContext * pMWContext)
{
if( EDT_IS_EDITOR(pMWContext) ){
CWinCX *pWinCX = VOID2CX(pMWContext->fe.cx, CWinCX);
CNetscapeEditView * pView = (CNetscapeEditView *)pWinCX->GetView();
if( !pView ) return;
if( pView && pView->m_caret.bEnabled && pView->m_caret.cShown ) {
pView->HideCaret();
pView->m_caret.cShown = 0;
}
}
}
void WFE_ShowEditCaret(MWContext * pMWContext)
{
if( EDT_IS_EDITOR(pMWContext) ){
CWinCX *pWinCX = VOID2CX(pMWContext->fe.cx, CWinCX);
CNetscapeEditView * pView = (CNetscapeEditView *)pWinCX->GetView();
if( !pView ) return;
if( pView->m_caret.bEnabled ) {
pView->ShowCaret();
pView->m_caret.cShown = 1;
}
}
}
void FE_DisplayAddRowOrColBorder(MWContext * pMWContext, XP_Rect *pRect, XP_Bool bErase)
{
CWinCX *pCX = VOID2CX(pMWContext->fe.cx, CWinCX);
if( !pCX )
return;
if( bErase )
{
RECT rect = {pRect->left, pRect->top, pRect->right, pRect->bottom};
// Adjust for line thickness
if( pRect->left == pRect->right)
{
rect.right++;
} else {
rect.bottom++;
}
::InvalidateRect(pCX->GetPane(), &rect, TRUE);
::UpdateWindow(pCX->GetPane());
} else {
HDC hdc = pCX->GetContextDC();
COLORREF rgbColor = RGB(0,0,0);
// Maybe draw in solid color?
// wfe_GetSelectionColors(pDC->m_rgbBackgroundColor, NULL, &rgbColor);
HPEN pPen = ::CreatePen(PS_DOT, 1, rgbColor);
HPEN pOldPen = (HPEN)::SelectObject(hdc, pPen);
// Use reverse effect
int OldRop = ::SetROP2( hdc, R2_NOT );
::MoveToEx(hdc, CASTINT(pRect->left), CASTINT(pRect->top), NULL);
::LineTo(hdc, CASTINT(pRect->right), CASTINT(pRect->bottom));
::SelectObject(hdc, pOldPen);
SetROP2(hdc, OldRop);
VERIFY(::DeleteObject(pPen));
}
}
void FE_DisplayEntireTableOrCell(MWContext * pMWContext, LO_Element * pElement)
{
if( pMWContext && pElement )
{
int32 iWidth, iHeight;
if( pElement->type == LO_TABLE )
{
iWidth = pElement->lo_table.width;
iHeight = pElement->lo_table.height;
}
else if ( pElement->type == LO_CELL )
{
iWidth = pElement->lo_cell.width;
iHeight = pElement->lo_cell.height;
}
else
return; // Only Table and Cell types allowed
CWinCX *pCX = VOID2CX(pMWContext->fe.cx, CWinCX);
RECT rect;
rect.left = pElement->lo_any.x - pCX->GetOriginX();
rect.top = pElement->lo_any.y - pCX->GetOriginY();
rect.right = rect.left + iWidth;
rect.bottom = rect.top + iHeight;
if( pElement->type == LO_TABLE &&
pElement->lo_table.border_left_width == 0 &&
pElement->lo_table.border_right_width == 0 &&
pElement->lo_table.border_top_width == 0 &&
pElement->lo_table.border_bottom_width == 0 )
{
// We are displaying a "zero-width" table,
// increase rect by 1 pixel 'cause thats
// where we drew the table's dotted-line border
::InflateRect(&rect, 1, 1);
}
// Include the inter-cell spacing area also used for highlighting a cell
int32 iExtraSpace;
if( pElement->type == LO_CELL && pElement->lo_cell.border_width < ED_SELECTION_BORDER &&
0 < (iExtraSpace = pElement->lo_cell.inter_cell_space / 2) )
{
::InflateRect(&rect, iExtraSpace, iExtraSpace);
}
InvalidateRect(pCX->GetPane(), &rect, TRUE);
}
}
void FE_DisplayDropTableFeedback(MWContext * pMWContext, EDT_DragTableData *pDragData)
{
if(!pMWContext || !EDT_IS_EDITOR(pMWContext) || !pDragData)
return;
CWinCX *pWinCX = VOID2CX(pMWContext->fe.cx, CWinCX);
CNetscapeEditView * pView = (CNetscapeEditView *)pWinCX->GetView();
if( pView )
{
if( pDragData->iDropType == ED_DROP_REPLACE_CELL )
{
//TODO: FEEDBACK WHEN REPLACING
// THIS MAY BE DONE IN XP CODE BY SETTING LO_ELE_SELECTED_SPECIAL
// THEN JUST REDRAWING THE TABLE
} else {
// Use caret to show inserting between cells
pView->CreateSolidCaret(pDragData->iWidth, pDragData->iHeight);
pView->SetCaretPos(CPoint(pDragData->X, pDragData->Y));
pView->m_caret.cShown = 1;
pView->m_caret.bEnabled = TRUE;
pView->ShowCaret();
}
}
}