gecko-dev/cmd/macfe/gui/macgui.cp
pinkerton%netscape.com 28723768dd use LTextEditView
1998-09-29 17:31:30 +00:00

855 lines
21 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.
*/
// StPrepareForDialog
// macfe
#include "macgui.h"
#include "uprefd.h"
#include "resgui.h"
#include "mimages.h"
#include "uerrmgr.h"
#include "macutil.h"
// Netscape
#include "client.h"
#include "ntypes.h"
#include "shist.h"
#include "glhist.h"
#include "lo_ele.h"
#include "mkutils.h"
#include "prefapi.h"
// PowerPlant
#include <UEnvironment.h>
// system
#include <LowMem.h>
#ifdef PROFILE
#pragma profile on
#endif
void DrawArc( const RGBColor& initColor, const Rect& box, Boolean inset );
// ¥¥ StPrepareForDialog
StPrepareForDialog::StPrepareForDialog()
{
ErrorManager::PrepareToInteract();
UDesktop::Deactivate();
}
StPrepareForDialog::~StPrepareForDialog()
{
UDesktop::Activate();
}
// class StTempClipRgn
RgnHandle StTempClipRgn::sOldClip = NULL;
RgnHandle StTempClipRgn::sMergedNewClip = NULL;
StTempClipRgn::StTempClipRgn (RgnHandle newClip)
{
if (sOldClip == NULL) sOldClip = ::NewRgn();
if (sMergedNewClip == NULL) sMergedNewClip = ::NewRgn();
::GetClip(sOldClip);
::SectRgn(sOldClip, newClip, sMergedNewClip);
::SetClip(sMergedNewClip);
}
StTempClipRgn::~StTempClipRgn()
{
::SetClip(sOldClip);
}
// class HyperStyle
extern Boolean gIsPrinting;
/*
HyperStyle::HyperStyle( HyperStyle* style )
{
memcpy( this, style, sizeof( HyperStyle ) ); // Just copy the fields
}
*/
HyperStyle::HyperStyle( MWContext *context, const CCharSet* charSet, LO_TextAttr* attr, Boolean onlyMeasuring,
LO_TextStruct* element )
{
fElement = element;
fAttr = *attr;
fAttr.size *= context->fontScalingPercentage;
// GetFontReference() stashes font info into fAttr->FE_Data so we need to make sure that
// we propogate that info to the attribute struct that is going back to layout.
fFontReference = CFontReference::GetFontReference(charSet, &fAttr, context, sUnderlineLinks);
attr->FE_Data = fAttr.FE_Data;
strike = ( attr->attrmask & LO_ATTR_STRIKEOUT );
fInlineInput = ( attr->attrmask & ( LO_ATTR_INLINEINPUT | LO_ATTR_INLINEINPUTTHICK | LO_ATTR_INLINEINPUTDOTTED ) );
fOnlyMeasuring = onlyMeasuring;
if ( !fOnlyMeasuring )
{
rgbFgColor = UGraphics::MakeRGBColor( attr->fg.red, attr->fg.green, attr->fg.blue );
rgbBkColor = UGraphics::MakeRGBColor( attr->bg.red, attr->bg.green, attr->bg.blue );
}
}
// ¥ calculate a rectangular area of the page based on "where" and the
// length of "text", using this style's font information; note that if
// we have a reference element, we use the line height given by layout,
// otherwise we figure out a line height using the text metrics
Rect HyperStyle::InvalBackground( Point where, char* text, int start, int end, Boolean blinks )
{
int usedText;
int unusedText;
Rect hiliteArea;
int length;
Apply();
GetFontInfo();
length = end - start + 1;
usedText = fFontReference->TextWidth(text, start, length);
unusedText = start ? fFontReference->TextWidth(text, 0, start) : 0;
if ( !fElement || !fElement->line_height )
{
hiliteArea.top = where.v;
hiliteArea.left = where.h + unusedText;
hiliteArea.bottom = hiliteArea.top + fFontInfo.ascent + fFontInfo.descent;
hiliteArea.right = hiliteArea.left + usedText;
}
else
{
hiliteArea.top = where.v - fElement->y_offset;
hiliteArea.left = where.h + unusedText;
hiliteArea.bottom = hiliteArea.top + fElement->line_height;
hiliteArea.right = hiliteArea.left + usedText;
if ( blinks ||
( fAttr.fontmask & LO_FONT_ITALIC ) )
{
hiliteArea.right += ( fFontInfo.widMax / 2 );
hiliteArea.left -= MIN( 2, ( fFontInfo.widMax / 8 ) );
}
}
return hiliteArea;
}
void HyperStyle::DrawText( Point where,
char* text,
int start,
int end )
{
Boolean selected;
Boolean swapped = FALSE;
int length = end - start + 1;
Assert_( fElement );
selected = ( ( fElement->ele_attrmask & LO_ELE_SELECTED ) != 0 &&
fElement->sel_start <= start &&
end <= fElement->sel_end );
Boolean paint_background = selected || !fAttr.no_background;
fFontReference->SetMode( paint_background ? srcCopy : srcOr );
// ¥ stupid layout reverses bk / fg colors when it tries to draw selected
// put the actual fg color back in fg, and put the user's hilite color
// into bk
// ¥ selection takes precedence over blink
if ( selected )
{
// ¥ put the colors back and check to make sure that highlighting
// will actually show up
rgbFgColor = rgbBkColor;
LMGetHiliteRGB( &rgbBkColor );
if ( UGraphics::EqualColor( rgbFgColor, rgbBkColor ) )
{
swapped = TRUE;
rgbFgColor.red = ~rgbBkColor.red;
rgbFgColor.green = ~rgbBkColor.green;
rgbFgColor.blue = ~rgbBkColor.blue;
}
}
Apply();
GetFontInfo();
long unusedText = start ? fFontReference->TextWidth(text, 0, start) : 0;
int x = where.h + unusedText;
int y = where.v + fFontInfo.ascent;
fFontReference->DrawText(x, y, text, start, end );
if ( paint_background )
{
short fontHeight = fFontInfo.ascent + fFontInfo.descent + fFontInfo.leading;
if ( !fElement )
return;
// ¥Êif the font height is less than the element's line height,
// there will be rectangular areas above and below the text
// that are not drawn with the selection highlighting
// we draw them here
if ( fontHeight < fElement->line_height )
{
long usedText = fFontReference->TextWidth(text, start, length);
Rect hiliteArea;
hiliteArea.top = where.v - fElement->y_offset;
hiliteArea.left = where.h + unusedText;
hiliteArea.right = hiliteArea.left + usedText;
hiliteArea.bottom = where.v;
::EraseRect( &hiliteArea );
hiliteArea.bottom = hiliteArea.top + fElement->line_height;
hiliteArea.top += ( fFontInfo.ascent + fFontInfo.descent + fElement->y_offset );
::EraseRect( &hiliteArea );
}
}
/*
// ¥Êspecial case for selected italics
// FIX ME! Don't forget about this when we redo printing
// if ( !gIsPrinting && ( (fStyle & italic) != 0 ) )
if ( (fAttr.fontmask & LO_FONT_ITALIC) != 0 )
{
if ( swapped )
::RGBForeColor( &rgbBkColor );
// **** should this use fFontReference->SetMode?
::TextMode( srcOr );
fFontReference->DrawText(where.h, where.v + fFontInfo.ascent, text, 0, fElement->text_len );
}
*/
Boolean isMisspelled = (fAttr.attrmask & LO_ATTR_SPELL) != 0;
if ( strike || isMisspelled )
{
// I have consciously decided that if a word is misspelled and
// striked that it is preferable to not strike and just mark
// for misspelling; this may not be the correct decision. [brade]
long usedText = fFontReference->TextWidth(text, start, length);
short vstrike;
if ( isMisspelled )
{
// for misspelled words we want a dashed underline
PenPat( &qd.gray );
vstrike = -( fFontInfo.descent - 1 );
}
else
vstrike = ( fFontInfo.ascent / 2 ) - 1;
::Move( -usedText, -vstrike );
::Line( usedText, 0 );
// restore things...
::Move( 0, vstrike );
if ( isMisspelled )
PenPat( &qd.black );
}
if ( fInlineInput )
{
long usedText = fFontReference->TextWidth( text, start, length );
short vInline = fFontInfo.descent;
if ( fInlineInput & LO_ATTR_INLINEINPUTTHICK ) {
PenSize( 1, 2);
vInline -= 2;
} else {
PenSize( 1, 1);
vInline -= 1;
}
if ( fInlineInput & LO_ATTR_INLINEINPUTDOTTED )
PenPat( &UQDGlobals::GetQDGlobals()->gray);
else
PenPat( &UQDGlobals::GetQDGlobals()->black);
::Move( -usedText, vInline );
::Line( usedText, 0 );
// restore things...
::Move( 0, -vInline );
PenSize( 1, 1);
PenPat( &UQDGlobals::GetQDGlobals()->black);
}
}
const char* sUnderlineLinksPref = "browser.underline_anchors";
Boolean HyperStyle::sUnderlineLinks = true;
XP_HashTable HyperStyle::sFontHash = NULL;
void HyperStyle::InitHyperStyle()
{
#if 0
if ( !sFontHash )
{
// the table size be: 2 fonts * 7 styles (bold, italic underline combos) *
// 10 for safety (multiple languages)
sFontHash = XP_HashTableNew( 140, StyleHash, StyleCompFunction );
}
#endif
PREF_RegisterCallback(sUnderlineLinksPref, SetUnderlineLinks, nil);
SetUnderlineLinks(sUnderlineLinksPref, nil);
CWebFontReference::Init();
}
void HyperStyle::FinishHyperStyle()
{
CWebFontReference::Finish();
}
int HyperStyle::SetUnderlineLinks(const char * /*newpref*/, void * /*stuff*/)
{
XP_Bool value;
PREF_GetBoolPref(sUnderlineLinksPref, &value);
sUnderlineLinks = value;
return 0;
}
/*
// Hash it.
// Time critical
uint32 HyperStyle::StyleHash( const void* style )
{
return (( ((HyperStyle*)style)->fFont << 1 ) +
((HyperStyle*)style)->fSize );
}
// Compare two hyperstyles
// time critical routine
// should function like strcmp
int HyperStyle::StyleCompFunction( const void* style1, const void* style2 )
{
Boolean equal = ( style1 &&
style2 &&
(((HyperStyle*)style1)->fFont == ((HyperStyle*)style2)->fFont) &&
(((HyperStyle*)style1)->fSize == ((HyperStyle*)style2)->fSize) );
if ( equal )
return 0;
else if ( !style1 )
return -1;
else if ( !style2 )
return 1;
else if (
(((HyperStyle*)style1)->fFont + ((HyperStyle*)style1)->fSize) >
(((HyperStyle*)style2)->fFont + ((HyperStyle*)style2)->fSize) )
return -1;
else
return 1;
}
*/
void HyperStyle::GetFontInfo()
{
/*
HyperStyle* hashStyle = (HyperStyle*)XP_Gethash( sFontHash, this, NULL );
if ( !hashStyle )
{
fFontReference->GetFontInfo(&fFontInfo);
hashStyle = new HyperStyle(this);
XP_Puthash(sFontHash, hashStyle, hashStyle);
}
else
fFontInfo = hashStyle->fFontInfo;
*/
fFontReference->GetFontInfo(&fFontInfo);
}
// TextWidth Dispatch routine
short HyperStyle::TextWidth(char* text, int firstByte, int byteCount )
{
return fFontReference->TextWidth(text, firstByte, byteCount);
}
/*-----------------------------------------------------------------------------
UGraphics is full of little graphics utilities
Original version by atotic
mark messed it up
-----------------------------------------------------------------------------*/
void UGraphics::Initialize()
{
}
void UGraphics::SetFore( CPrefs::PrefEnum r )
{
UGraphics::SetIfColor( CPrefs::GetColor( r ) );
}
void UGraphics::SetBack( CPrefs::PrefEnum r )
{
UGraphics::SetIfBkColor( CPrefs::GetColor( r ) );
}
void UGraphics::VertLine( int x, int top, int height, CPrefs::PrefEnum r )
{
UGraphics::SetFore( r );
MoveTo( x, top );
LineTo( x, top + height - 1 );
}
void UGraphics::HorizLineAtWidth (int vertical, int left, int width, CPrefs::PrefEnum r)
{
UGraphics::SetFore (r);
MoveTo (left, vertical);
LineTo (left + width - 1, vertical);
}
// Draws a diagonal to the rectangle
void UGraphics::DrawLine( int16 top, int16 left, int16 bottom, int16 right, CPrefs::PrefEnum color )
{
UGraphics::SetFore( color );
MoveTo( left, top );
LineTo( right, bottom );
}
CGrafPtr UGraphics::IsColorPort( GrafPtr port )
{
Assert_( port );
return ((((CGrafPtr)port)->portVersion) & 0xC000) == 0xC000? (CGrafPtr) port: nil;
}
//
// Important Note!
// If you are going to muck with the colors of a window, be sure to give it
// a custom wctb resource! Otherwise the window will be created with a pointer
// to the SYSTEMWIDE global AuxRec and you'll change EVERYTHING.
//
void
UGraphics::SetWindowColor( GrafPort* window, short field, const RGBColor& color )
{
if ( !UEnvironment::HasFeature( env_SupportsColor ) )
return;
// Get window's auxilary record. Skip on failure
AuxWinHandle aux = NULL;
AuxWinHandle def = NULL;
CTabHandle awCTable = NULL;
Boolean foundFieldInTable = FALSE;
Boolean hasAuxWinRec = FALSE;
Boolean isDefault = FALSE;
Assert_( window );
if ( !window )
return;
hasAuxWinRec = ::GetAuxWin( window, &aux );
::GetAuxWin( NULL, &def );
isDefault = (*aux)->awCTable == (*def)->awCTable;
if ( isDefault || !hasAuxWinRec || !aux )
{
//XP_TRACE(("UGraphics::SetWindowColor (%p) -> nil", window));
return;
}
// Get the color table
// Find the fore/background colors, and set them to the unified scheme
awCTable = (*aux)->awCTable;
for ( unsigned long i = 0; i <= (*awCTable)->ctSize; i++ )
{
if ( (*awCTable)->ctTable[ i ].value == field )
{
foundFieldInTable = true;
if ( UGraphics::EqualColor( (*awCTable)->ctTable[ i ].rgb, color ) )
return;
(*awCTable)->ctTable[ i ].rgb = color;
CTabChanged( awCTable );
}
}
Assert_( foundFieldInTable );
}
void UGraphics::SetWindowColors (LWindow *window)
{
SetWindowColor (window->GetMacPort(), wContentColor, CPrefs::GetColor(CPrefs::WindowBkgnd));
}
void UGraphics::FrameRectMotif (const Rect& box, Boolean inset)
{
RGBColor lighter = {65535,65535,65535}, darker = {0,0,0};
FrameRectBevel(box, inset, lighter, darker);
}
void UGraphics::FrameRectSubtle (const Rect& box, Boolean inset)
{
RGBColor lighter = {60000,60000,60000}, darker = {20000,20000,20000};
FrameRectBevel(box, inset, lighter, darker);
}
void UGraphics::FrameRectBevel (const Rect& box, Boolean inset, const RGBColor &lighter,
const RGBColor &darker )
{
::PenSize(1,1);
if (inset) UGraphics::SetIfColor (darker);
else UGraphics::SetIfColor (lighter);
MoveTo (box.left, box.top);
LineTo (box.right-1, box.top);
MoveTo (box.left, box.top);
LineTo (box.left, box.bottom-1);
if (inset) UGraphics::SetIfColor (lighter);
else UGraphics::SetIfColor (darker);
MoveTo (box.right-1, box.bottom-1);
LineTo (box.right-1, box.top+1);
MoveTo (box.right-1, box.bottom-1);
LineTo (box.left+1, box.bottom-1);
}
void UGraphics::FrameRectShaded (const Rect& box, Boolean inset)
{
RGBColor addOn = {20000, 20000, 20000};
RGBColor lighter = {60000,60000,60000}, darker = {0,0,0};
// subPin substracts value, makes color darker
// addPin adds value, makes color lighter
SetIfColor(addOn);
if (inset)
{
OpColor(&darker);
PenMode(subPin);
}
else
{
OpColor(&lighter);
PenMode(addPin);
}
MoveTo (box.left, box.top);
LineTo (box.right-1, box.top);
MoveTo (box.left, box.top);
LineTo (box.left, box.bottom-1);
if (inset)
{
OpColor(&lighter);
PenMode(addPin);
}
else
{
OpColor(&darker);
PenMode(subPin);
}
MoveTo (box.right-1, box.bottom-1);
LineTo (box.right-1, box.top+1);
MoveTo (box.right-2, box.bottom-1);
LineTo (box.left+1, box.bottom-1);
PenMode(srcCopy);
}
void DrawArc( const RGBColor& initColor, const Rect& box, Boolean inset )
{
RGBColor lighter = {60000,60000,60000}, darker = {0,0,0};
UGraphics::SetIfColor( initColor );
if ( inset )
{
OpColor( &darker );
PenMode( subPin );
}
else
{
OpColor( &lighter );
PenMode( addPin );
}
FrameArc( &box, 225, 180 );
if ( inset )
{
OpColor( &lighter );
PenMode( addPin );
}
else
{
OpColor( &darker );
PenMode( subPin );
}
FrameArc( &box, 45, 180 );
}
void UGraphics::FrameCircleShaded( const Rect& box, Boolean inset )
{
RGBColor first = { 20000, 20000, 20000 };
RGBColor second = { 30000, 30000, 30000 };
// subPin substracts value, makes color darker
// addPin adds value, makes color lighter
DrawArc( first, box, inset );
Rect tempRect = box; // box is CONST!
InsetRect( &tempRect, -1, -1 );
DrawArc( second, tempRect, inset );
PenMode( srcCopy );
}
void ArithHighlight( const Rect& box, const RGBColor& curr );
void ArithHighlight( const Rect& box, const RGBColor& curr )
{
RGBColor tmp = curr;
tmp.red = ~tmp.red;
tmp.green = ~tmp.green;
tmp.blue = ~tmp.blue;
RGBColor darker = { 0, 0, 0};
RGBColor addOn = { 30000, 30000, 30000 };
UGraphics::SetIfColor( addOn );
OpColor( &tmp );
PenMode( addPin );
PenPat( &UQDGlobals::GetQDGlobals()->black );
::PaintRect( &box );
PenMode( patCopy );
}
void UGraphics::FrameTopLeftShaded( const Rect& box, short width, const RGBColor& curr )
{
Rect l;
l.top = box.top;
l.left = box.left;
l.right = box.right;
l.bottom = box.top + width;
ArithHighlight( l, curr );
l.top += width;
l.right = l.left + width;
l.bottom = box.bottom;
ArithHighlight( l, curr );
}
void UGraphics::FrameEraseRect( const Rect& box, short width, Boolean focussed )
{
Rect lineRect;
// top left -> top right
lineRect.top = box.top;
lineRect.left = box.left;
lineRect.bottom = lineRect.top + width;
lineRect.right = box.right;
::EraseRect( &lineRect );
if ( focussed )
DarkenRect( lineRect );
// bottom left -> bottom right
lineRect.top = box.bottom - width;
lineRect.bottom = box.bottom;
::EraseRect( &lineRect );
if ( focussed )
DarkenRect( lineRect );
// top left -> bottom left
lineRect.top = box.top;
lineRect.left = box.left;
lineRect.bottom = box.bottom;
lineRect.right = lineRect.left + width;
::EraseRect( &lineRect );
if ( focussed )
DarkenRect( lineRect );
// top right -> bottom right
lineRect.left = box.right - width;
lineRect.right = box.right;
::EraseRect( &lineRect );
if ( focussed )
DarkenRect( lineRect );
}
void UGraphics::DarkenRect( const Rect& box )
{
RGBColor darker = { 0, 0, 0};
RGBColor addOn = { 10000, 10000, 10000 };
SetIfColor( addOn );
OpColor( &darker );
PenMode( subPin );
PenPat( &UQDGlobals::GetQDGlobals()->black );
::PaintRect( &box );
PenMode( patCopy );
}
Boolean UGraphics::PointInBigRect(SPoint32 rectLoc, SDimension16 rectSize, SPoint32 loc)
{
return ((loc.v > rectLoc.v) &&
(loc.v < (rectLoc.v + rectSize.height)) &&
(loc.h > rectLoc.h) &&
(loc.h < (rectLoc.h + rectSize.width)));
}
RgnHandle UGraphics::sTempRegions[] = { NULL, NULL, NULL, NULL, NULL };
Boolean UGraphics::sRegionsUsed[] = { false, false, false, false, false };
//
// Get a temporary region from our cache (array, actually) of
// allocated regions, if possible. A parallel array of booleans
// indicates which regions are in use; if all are unavailable,
// just allocate a new one. Call ReleaseTempRegion() when youÕre
// done using the region.
//
RgnHandle UGraphics::GetTempRegion()
{
for (short i = 0; i < kRegionCacheSize; i++)
{
if (sRegionsUsed[i] == false)
{
sRegionsUsed[i] = true;
if (sTempRegions[i] == NULL)
sTempRegions[i] = ::NewRgn();
return sTempRegions[i];
}
}
return (::NewRgn());
}
//
// Release a temporary region returned from GetTempRegion().
// We call SetEmptyRgn() here to save some memory, and because
// when the region is reused in GetTempRegion the caller will
// expect the ÒnewÓ region to be empty. If the region wasnÕt
// found in the cache array, then it must have been freshly
// allocated by GetTempRegion because all cached regions were
// in use at the time, so just dispose of the region.
//
void UGraphics::ReleaseTempRegion(RgnHandle rgn)
{
for (short i = 0; i < kRegionCacheSize; i++)
{
if (sTempRegions[i] == rgn)
{
sRegionsUsed[i] = false;
SetEmptyRgn(rgn);
return;
}
}
::DisposeRgn(rgn);
}
/*****************************************************************************
* class CEditBroadcaster
* LEditField that notifies listeners about its changes
*****************************************************************************/
CEditBroadcaster::CEditBroadcaster(LStream *inStream)
: CTSMEditField(inStream)
{
}
void CEditBroadcaster::UserChangedText()
{
BroadcastMessage(msg_EditField, this);
}
/*****************************************************************************
* class CGAEditBroadcaster
* LGAEditField that notifies listeners about its changes
*****************************************************************************/
CGAEditBroadcaster::CGAEditBroadcaster(LStream *inStream)
: LGAEditField(inStream)
{
}
void CGAEditBroadcaster::UserChangedText()
{
BroadcastMessage(msg_EditField, this);
}
void
CGAEditBroadcaster::BroadcastValueMessage()
{
BroadcastMessage(msg_EditField, this);
}
/*****************************************************************************
* class CTextEdit
* LTextEdit that keeps track if it has been modified
*****************************************************************************/
CTextEdit::CTextEdit(LStream *inStream) : LTextEditView(inStream)
{
fModified = FALSE;
}
void CTextEdit::UserChangedText()
{
fModified = TRUE;
LTextEditView::UserChangedText();
}
/*****************************************************************************
* class CResPicture
* LPicture with an associated resfile ID
*****************************************************************************/
CResPicture::CResPicture(LStream *inStream)
: LPicture(inStream)
{
mResFileID = -1;
}
void CResPicture::DrawSelf()
{
Int16 resfile = ::CurResFile();
if (mResFileID != -1)
::UseResFile(mResFileID);
LPicture::DrawSelf();
::UseResFile(resfile);
}
#ifdef PROFILE
#pragma profile off
#endif