/* -*- 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(); } } }