diff options
Diffstat (limited to 'macosx/PlatMacOSX.cxx')
-rw-r--r-- | macosx/PlatMacOSX.cxx | 1861 |
1 files changed, 0 insertions, 1861 deletions
diff --git a/macosx/PlatMacOSX.cxx b/macosx/PlatMacOSX.cxx deleted file mode 100644 index 381be992d..000000000 --- a/macosx/PlatMacOSX.cxx +++ /dev/null @@ -1,1861 +0,0 @@ -// Scintilla source code edit control -// PlatMacOSX.cxx - implementation of platform facilities on MacOS X/Carbon -// Based on work by Evan Jones (c) 2002 <ejones@uwaterloo.ca> -// Based on PlatGTK.cxx Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org> -// The License.txt file describes the conditions under which this software may be distributed. - -#include <cstring> -#include <cstdio> -#include <cstdlib> - -#include <assert.h> - -#include <sys/time.h> - -#include <vector> -#include <map> - -#include <Carbon/Carbon.h> -#include "QuartzTextLayout.h" -#include "TCarbonEvent.h" - -#include "Platform.h" -#include "Scintilla.h" -#include "PlatMacOSX.h" -#include "XPM.h" - -using namespace Scintilla; - -#include "ScintillaWidget.h" - - -extern sptr_t scintilla_send_message(void* sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam); - -inline CGRect PRectangleToCGRect( PRectangle& rc ) { - return CGRectMake( rc.left, rc.top, rc.Width(), rc.Height() ); -} - -inline PRectangle CGRectToPRectangle( const CGRect& rect ) { - PRectangle rc; - rc.left = (int)( rect.origin.x + 0.5 ); - rc.top = (int)( rect.origin.y + 0.5 ); - rc.right = (int)( rect.origin.x + rect.size.width + 0.5 ); - rc.bottom = (int)( rect.origin.y + rect.size.height + 0.5 ); - return rc; -} - -Scintilla::Point Scintilla::Point::FromLong(long lpoint) { - return Scintilla::Point( - Platform::LowShortFromLong(lpoint), - Platform::HighShortFromLong(lpoint)); -} - -// The Palette is just ignored on Mac OS X. OS X runs "Millions" or "Thousands" of colours. -Scintilla::Palette::Palette() { -} - -Scintilla::Palette::~Palette() { -} - -void Scintilla::Palette::Release() { -} - -// Do nothing if it "wants" a colour. Copy the colour from desired to allocated if it is "finding" a colour. -void Scintilla::Palette::WantFind(ColourPair &cp, bool want) { - if (want) { - } else { - cp.allocated.Set(cp.desired.AsLong()); - } -} - -void Scintilla::Palette::Allocate(Window &/*w*/) { - // OS X always runs in thousands or millions of colours -} - -Font::Font() : fid(0) {} - -Font::~Font() { Release(); } - - -void Font::Create(const char *faceName, int /*characterSet*/, - int size, bool bold, bool italic, int /*extraFontFlag*/) { - // TODO: How should I handle the characterSet request? - Release(); - - fid = new QuartzTextStyle(); - - // Find the font - QuartzFont font( faceName, strlen( faceName ) ); - - // We set Font, Size, Bold, Italic - QuartzTextSize textSize( size ); - QuartzTextBold isBold( bold ); - QuartzTextItalic isItalic( italic ); - - // Actually set the attributes - QuartzTextStyleAttribute* attributes[] = { &font, &textSize, &isBold, &isItalic }; - reinterpret_cast<QuartzTextStyle*>( fid )->setAttributes( attributes, sizeof( attributes ) / sizeof( *attributes ) ); - - //ATSStyleRenderingOptions rendering = kATSStyleNoAntiAliasing; - //reinterpret_cast<QuartzTextStyle*>( fid )->setAttribute( kATSUStyleRenderingOptionsTag, sizeof( rendering ), &rendering ); - - // TODO: Why do I have to manually set this? - reinterpret_cast<QuartzTextStyle*>( fid )->setFontFeature( kLigaturesType, kCommonLigaturesOffSelector ); -} - -void Font::Release() { - if (fid) - delete reinterpret_cast<QuartzTextStyle*>( fid ); - - fid = 0; -} - -SurfaceImpl::SurfaceImpl() { - bitmapData = NULL; // Release will try and delete bitmapData if != NULL - gc = NULL; - textLayout = new QuartzTextLayout(NULL); - Release(); -} - -SurfaceImpl::~SurfaceImpl() { - Release(); - delete textLayout; -} - -void SurfaceImpl::Release() { - textLayout->setContext (NULL); - if ( bitmapData != NULL ) - { - delete[] bitmapData; - // We only "own" the graphics context if we are a bitmap context - if ( gc != NULL ) CGContextRelease( gc ); - } - bitmapData = NULL; - gc = NULL; - - bitmapWidth = 0; - bitmapHeight = 0; - x = 0; - y = 0; - //inited = false; -} - -bool SurfaceImpl::Initialised() { - // We are initalised if the graphics context is not null - return gc != NULL;// || port != NULL; -} - -void SurfaceImpl::Init(WindowID /*wid*/) { - // To be able to draw, the surface must get a CGContext handle. We save the graphics port, - // then aquire/release the context on an as-need basis (see above). - // XXX Docs on QDBeginCGContext are light, a better way to do this would be good. - // AFAIK we should not hold onto a context retrieved this way, thus the need for - // aquire/release of the context. - - Release(); -} - -void SurfaceImpl::Init(SurfaceID sid, WindowID /*wid*/) { - Release(); - gc = reinterpret_cast<CGContextRef>( sid ); - CGContextSetLineWidth( gc, 1.0 ); - textLayout->setContext (gc); -} - -void SurfaceImpl::InitPixMap(int width, int height, Surface* /*surface_*/, WindowID /*wid*/) { - Release(); - // Create a new bitmap context, along with the RAM for the bitmap itself - bitmapWidth = width; - bitmapHeight = height; - - const int bitmapBytesPerRow = (width * BYTES_PER_PIXEL); - const int bitmapByteCount = (bitmapBytesPerRow * height); - - // create an RGB color space - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - if( colorSpace == NULL ) - return; - - // create the bitmap - bitmapData = new uint8_t[ bitmapByteCount ]; - if( bitmapData != NULL ) { - // create the context - gc = CGBitmapContextCreate( bitmapData, - width, - height, - BITS_PER_COMPONENT, - bitmapBytesPerRow, - colorSpace, - kCGImageAlphaPremultipliedLast); - - if( gc == NULL ) { - // the context couldn't be created for some reason, - // and we have no use for the bitmap without the context - delete[] bitmapData; - bitmapData = NULL; - } - textLayout->setContext (gc); - } - - // the context retains the color space, so we can release it - CGColorSpaceRelease( colorSpace ); - - if ( gc != NULL && bitmapData != NULL ) - { - // "Erase" to white - CGContextClearRect( gc, CGRectMake( 0, 0, width, height ) ); - CGContextSetRGBFillColor( gc, 1.0, 1.0, 1.0, 1.0 ); - CGContextFillRect( gc, CGRectMake( 0, 0, width, height ) ); - } -} - -void SurfaceImpl::PenColour(ColourAllocated fore) { - if (gc) { - ColourDesired colour( fore.AsLong() ); - - // Set the Stroke color to match - CGContextSetRGBStrokeColor( gc, colour.GetRed() / 255.0, colour.GetGreen() / 255.0, colour.GetBlue() / 255.0, 1.0 ); - } -} - -void SurfaceImpl::FillColour(const ColourAllocated& back) { - if (gc) { - ColourDesired colour( back.AsLong() ); - - // Set the Fill color to match - CGContextSetRGBFillColor( gc, colour.GetRed() / 255.0, colour.GetGreen() / 255.0, colour.GetBlue() / 255.0, 1.0 ); - } -} - -CGImageRef SurfaceImpl::GetImage() { - // For now, assume that GetImage can only be called on PixMap surfaces - if ( bitmapData == NULL ) return NULL; - - CGContextFlush( gc ); - - // create an RGB color space - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - if( colorSpace == NULL ) - return NULL; - - const int bitmapBytesPerRow = ((int) bitmapWidth * BYTES_PER_PIXEL); - const int bitmapByteCount = (bitmapBytesPerRow * (int) bitmapHeight); - - // Create a data provider - CGDataProviderRef dataProvider = CGDataProviderCreateWithData( NULL, bitmapData, bitmapByteCount, NULL ); - CGImageRef image = NULL; - if ( dataProvider != NULL ) - { - // create the CGImage - image = CGImageCreate( bitmapWidth, - bitmapHeight, - BITS_PER_COMPONENT, - BITS_PER_PIXEL, - bitmapBytesPerRow, - colorSpace, - kCGImageAlphaPremultipliedLast, - dataProvider, - NULL, - 0, - kCGRenderingIntentDefault ); - } - - // the image retains the color space, so we can release it - CGColorSpaceRelease( colorSpace ); - colorSpace = NULL; - - // Done with the data provider - CGDataProviderRelease( dataProvider ); - dataProvider = NULL; - - return image; -} - -int SurfaceImpl::LogPixelsY() { - return 72; -} - -int SurfaceImpl::DeviceHeightFont(int points) { - int logPix = LogPixelsY(); - return (points * logPix + logPix / 2) / 72; -} - -void SurfaceImpl::MoveTo(int x_, int y_) { - x = x_; - y = y_; -} - -void SurfaceImpl::LineTo(int x_, int y_) { - CGContextBeginPath( gc ); - // Because Quartz is based on floating point, lines are drawn with half their colour - // on each side of the line. Integer coordinates specify the INTERSECTION of the pixel - // divison lines. If you specify exact pixel values, you get a line that - // is twice as thick but half as intense. To get pixel aligned rendering, - // we render the "middle" of the pixels by adding 0.5 to the coordinates. - CGContextMoveToPoint( gc, x + 0.5, y + 0.5 ); - CGContextAddLineToPoint( gc, x_ + 0.5, y_ + 0.5 ); - CGContextStrokePath( gc ); - x = x_; - y = y_; -} - -void SurfaceImpl::Polygon(Scintilla::Point *pts, int npts, ColourAllocated fore, - ColourAllocated back) { - // Allocate memory for the array of points - CGPoint *points = new CGPoint[ npts ]; - - for (int i = 0;i < npts;i++) { - // Quartz floating point issues: plot the MIDDLE of the pixels - points[i].x = pts[i].x + 0.5; - points[i].y = pts[i].y + 0.5; - } - - CGContextBeginPath( gc ); - - // Set colours - FillColour(back); - PenColour(fore); - - // Draw the polygon - CGContextAddLines( gc, points, npts ); - // Explicitly close the path, so it is closed for stroking AND filling (implicit close = filling only) - CGContextClosePath( gc ); - CGContextDrawPath( gc, kCGPathFillStroke ); - - // Deallocate memory - delete [] points; - points = NULL; -} - -void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) { - if ( gc ) { - CGContextBeginPath( gc ); - FillColour(back); - PenColour(fore); - - // Quartz integer -> float point conversion fun (see comment in SurfaceImpl::LineTo) - // We subtract 1 from the Width() and Height() so that all our drawing is within the area defined - // by the PRectangle. Otherwise, we draw one pixel too far to the right and bottom. - CGContextAddRect( gc, CGRectMake( rc.left + 0.5, rc.top + 0.5, rc.Width() - 1, rc.Height() - 1 ) ); - CGContextDrawPath( gc, kCGPathFillStroke ); - } -} - -void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) { - if ( gc ) { - //CGContextBeginPath( gc ); - FillColour(back); - - CGRect rect = PRectangleToCGRect( rc ); - - CGContextFillRect( gc, rect ); - //CGContextDrawPath( gc, kCGPathFill ); - } -} - -void drawImageRefCallback( CGImageRef pattern, CGContextRef gc ) -{ - CGContextDrawImage( gc, CGRectMake( 0, 0, CGImageGetWidth( pattern ), CGImageGetHeight( pattern ) ), pattern ); -} - -void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) { - SurfaceImpl& patternSurface = static_cast<SurfaceImpl &>(surfacePattern); - - // For now, assume that copy can only be called on PixMap surfaces - // Shows up black - CGImageRef image = patternSurface.GetImage(); - if ( image == NULL ) - { - FillRectangle(rc, ColourAllocated(0)); - return; - } - - const CGPatternCallbacks drawImageCallbacks = { 0, reinterpret_cast<CGPatternDrawPatternCallback>( drawImageRefCallback ), NULL }; - - CGPatternRef pattern = CGPatternCreate( image, - CGRectMake( 0, 0, patternSurface.bitmapWidth, patternSurface.bitmapHeight ), - CGAffineTransformIdentity, - patternSurface.bitmapWidth, - patternSurface.bitmapHeight, - kCGPatternTilingNoDistortion, - true, - &drawImageCallbacks - ); - if( pattern != NULL ) { - - // Create a pattern color space - CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern( NULL ); - if( colorSpace != NULL ) { - - CGContextSaveGState( gc ); - CGContextSetFillColorSpace( gc, colorSpace ); - - // Unlike the documentation, you MUST pass in a "components" parameter: - // For coloured patterns it is the alpha value. - const float alpha = 1.0; - CGContextSetFillPattern( gc, pattern, &alpha ); - CGContextFillRect( gc, PRectangleToCGRect( rc ) ); - CGContextRestoreGState( gc ); - // Free the color space, the pattern and image - CGColorSpaceRelease( colorSpace ); - } /* colorSpace != NULL */ - colorSpace = NULL; - CGPatternRelease( pattern ); - pattern = NULL; - CGImageRelease( image ); - image = NULL; - } /* pattern != NULL */ -} - -void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) { - // This is only called from the margin marker drawing code for SC_MARK_ROUNDRECT - // The Win32 version does - // ::RoundRect(hdc, rc.left + 1, rc.top, rc.right - 1, rc.bottom, 8, 8 ); - // which is a rectangle with rounded corners each having a radius of 4 pixels. - // It would be almost as good just cutting off the corners with lines at - // 45 degrees as is done on GTK+. - - // Create a rectangle with semicircles at the corners - const int MAX_RADIUS = 4; - int radius = Platform::Minimum( MAX_RADIUS, rc.Height()/2 ); - radius = Platform::Minimum( radius, rc.Width()/2 ); - - // Points go clockwise, starting from just below the top left - // Corners are kept together, so we can easily create arcs to connect them - CGPoint corners[4][3] = - { - { - { rc.left, rc.top + radius }, - { rc.left, rc.top }, - { rc.left + radius, rc.top }, - }, - { - { rc.right - radius - 1, rc.top }, - { rc.right - 1, rc.top }, - { rc.right - 1, rc.top + radius }, - }, - { - { rc.right - 1, rc.bottom - radius - 1 }, - { rc.right - 1, rc.bottom - 1 }, - { rc.right - radius - 1, rc.bottom - 1 }, - }, - { - { rc.left + radius, rc.bottom - 1 }, - { rc.left, rc.bottom - 1 }, - { rc.left, rc.bottom - radius - 1 }, - }, - }; - - // Align the points in the middle of the pixels - for( int i = 0; i < 4*3; ++ i ) - { - CGPoint* c = (CGPoint*) corners; - c[i].x += 0.5; - c[i].y += 0.5; - } - - PenColour( fore ); - FillColour( back ); - - // Move to the last point to begin the path - CGContextBeginPath( gc ); - CGContextMoveToPoint( gc, corners[3][2].x, corners[3][2].y ); - - for ( int i = 0; i < 4; ++ i ) - { - CGContextAddLineToPoint( gc, corners[i][0].x, corners[i][0].y ); - CGContextAddArcToPoint( gc, corners[i][1].x, corners[i][1].y, corners[i][2].x, corners[i][2].y, radius ); - } - - // Close the path to enclose it for stroking and for filling, then draw it - CGContextClosePath( gc ); - CGContextDrawPath( gc, kCGPathFillStroke ); -} - -void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, int /*cornerSize*/, ColourAllocated fill, int alphaFill, - ColourAllocated /*outline*/, int /*alphaOutline*/, int /*flags*/) -{ - if ( gc ) { - ColourDesired colour( fill.AsLong() ); - - // Set the Fill color to match - CGContextSetRGBFillColor( gc, colour.GetRed() / 255.0, colour.GetGreen() / 255.0, colour.GetBlue() / 255.0, alphaFill / 255.0 ); - CGRect rect = PRectangleToCGRect( rc ); - CGContextFillRect( gc, rect ); - } -} - -void SurfaceImpl::DrawRGBAImage(PRectangle /* rc */, int /* width */, int /* height */, const unsigned char * /* pixelsImage*/) { - // Not supported for Carbon -} - -void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) { - // Drawing an ellipse with bezier curves. Code modified from: - // http://www.codeguru.com/gdi/ellipse.shtml - // MAGICAL CONSTANT to map ellipse to beziers 2/3*(sqrt(2)-1) - const double EToBConst = 0.2761423749154; - - CGSize offset = CGSizeMake((int)(rc.Width() * EToBConst), (int)(rc.Height() * EToBConst)); - CGPoint centre = CGPointMake((rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2); - - // The control point array - CGPoint cCtlPt[13]; - - // Assign values to all the control points - cCtlPt[0].x = - cCtlPt[1].x = - cCtlPt[11].x = - cCtlPt[12].x = rc.left + 0.5; - cCtlPt[5].x = - cCtlPt[6].x = - cCtlPt[7].x = rc.right - 0.5; - cCtlPt[2].x = - cCtlPt[10].x = centre.x - offset.width + 0.5; - cCtlPt[4].x = - cCtlPt[8].x = centre.x + offset.width + 0.5; - cCtlPt[3].x = - cCtlPt[9].x = centre.x + 0.5; - - cCtlPt[2].y = - cCtlPt[3].y = - cCtlPt[4].y = rc.top + 0.5; - cCtlPt[8].y = - cCtlPt[9].y = - cCtlPt[10].y = rc.bottom - 0.5; - cCtlPt[7].y = - cCtlPt[11].y = centre.y + offset.height + 0.5; - cCtlPt[1].y = - cCtlPt[5].y = centre.y - offset.height + 0.5; - cCtlPt[0].y = - cCtlPt[12].y = - cCtlPt[6].y = centre.y + 0.5; - - FillColour(back); - PenColour(fore); - - CGContextBeginPath( gc ); - CGContextMoveToPoint( gc, cCtlPt[0].x, cCtlPt[0].y ); - - for ( int i = 1; i < 13; i += 3 ) - { - CGContextAddCurveToPoint( gc, cCtlPt[i].x, cCtlPt[i].y, cCtlPt[i+1].x, cCtlPt[i+1].y, cCtlPt[i+2].x, cCtlPt[i+2].y ); - } - - // Close the path to enclose it for stroking and for filling, then draw it - CGContextClosePath( gc ); - CGContextDrawPath( gc, kCGPathFillStroke ); -} - -void SurfaceImpl::CopyImageRectangle(Surface &surfaceSource, PRectangle srcRect, PRectangle dstRect) -{ - SurfaceImpl& source = static_cast<SurfaceImpl &>(surfaceSource); - CGImageRef image = source.GetImage(); - - CGRect src = PRectangleToCGRect(srcRect); - CGRect dst = PRectangleToCGRect(dstRect); - - /* source from QuickDrawToQuartz2D.pdf on developer.apple.com */ - float w = (float) CGImageGetWidth(image); - float h = (float) CGImageGetHeight(image); - CGRect drawRect = CGRectMake (0, 0, w, h); - if (!CGRectEqualToRect (src, dst)) - { - float sx = CGRectGetWidth(dst) / CGRectGetWidth(src); - float sy = CGRectGetHeight(dst) / CGRectGetHeight(src); - float dx = CGRectGetMinX(dst) - (CGRectGetMinX(src) * sx); - float dy = CGRectGetMinY(dst) - (CGRectGetMinY(src) * sy); - drawRect = CGRectMake (dx, dy, w*sx, h*sy); - } - CGContextSaveGState (gc); - CGContextClipToRect (gc, dst); - CGContextDrawImage (gc, drawRect, image); - CGContextRestoreGState (gc); -} - -void SurfaceImpl::Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSource) { - // Maybe we have to make the Surface two contexts: - // a bitmap context which we do all the drawing on, and then a "real" context - // which we copy the output to when we call "Synchronize". Ugh! Gross and slow! - - // For now, assume that copy can only be called on PixMap surfaces - SurfaceImpl& source = static_cast<SurfaceImpl &>(surfaceSource); - - // Get the CGImageRef - CGImageRef image = source.GetImage(); - // If we could not get an image reference, fill the rectangle black - if ( image == NULL ) - { - FillRectangle( rc, ColourAllocated( 0 ) ); - return; - } - - // Now draw the image on the surface - - // Some fancy clipping work is required here: draw only inside of rc - CGContextSaveGState( gc ); - CGContextClipToRect( gc, PRectangleToCGRect( rc ) ); - - //Platform::DebugPrintf(stderr, "Copy: CGContextDrawImage: (%d, %d) - (%d X %d)\n", rc.left - from.x, rc.top - from.y, source.bitmapWidth, source.bitmapHeight ); - CGContextDrawImage( gc, CGRectMake( rc.left - from.x, rc.top - from.y, source.bitmapWidth, source.bitmapHeight ), image ); - - // Undo the clipping fun - CGContextRestoreGState( gc ); - - // Done with the image - CGImageRelease( image ); - image = NULL; -} - -void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, - ColourAllocated fore, ColourAllocated back) { - - FillRectangle(rc, back); - DrawTextTransparent( rc, font_, ybase, s, len, fore ); -} - -void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, - ColourAllocated fore, ColourAllocated back) { - - CGContextSaveGState( gc ); - CGContextClipToRect( gc, PRectangleToCGRect( rc ) ); - DrawTextNoClip( rc, font_, ybase, s, len, fore, back ); - CGContextRestoreGState( gc ); -} - -void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore) { - textLayout->setText (reinterpret_cast<const UInt8*>(s), len, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); - - // The Quartz RGB fill color influences the ATSUI color - FillColour(fore); - // Draw the text, with the Y axis flipped - textLayout->draw( rc.left, ybase, true ); -} - -void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) { - // sample at http://developer.apple.com/samplecode/ATSUICurveAccessDemo/listing1.html - // sample includes use of ATSUGetGlyphInfo which would be better for older - // OSX systems. We should expand to using that on older systems as well. - for (int i = 0; i < len; i++) - positions [i] = 0; - - // We need the right X coords, so we have to append a char to get the left coord of thast extra char - char* buf = (char*) malloc (len+1); - if (!buf) - return; - - memcpy (buf, s, len); - buf [len] = '.'; - - textLayout->setText (reinterpret_cast<const UInt8*>(buf), len+1, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); - ATSUGlyphInfoArray* theGlyphInfoArrayPtr; - ByteCount theArraySize; - - // Get the GlyphInfoArray - ATSUTextLayout layout = textLayout->getLayout(); - if ( noErr == ATSUGetGlyphInfo (layout, 0, textLayout->getLength(), &theArraySize, NULL)) - { - theGlyphInfoArrayPtr = (ATSUGlyphInfoArray *) malloc (theArraySize + sizeof(ItemCount) + sizeof(ATSUTextLayout)); - if (theGlyphInfoArrayPtr) - { - if (noErr == ATSUGetGlyphInfo (layout, 0, textLayout->getLength(), &theArraySize, theGlyphInfoArrayPtr)) - { - // do not count the first item, which is at the beginning of the line - for ( UniCharCount unicodePosition = 1, i = 0; i < len && unicodePosition < theGlyphInfoArrayPtr->numGlyphs; unicodePosition ++ ) - { - // The ideal position is the x coordinate of the glyph, relative to the beginning of the line - int position = (int)( theGlyphInfoArrayPtr->glyphs[unicodePosition].idealX + 0.5 ); // These older APIs return float values - unsigned char uch = s[i]; - positions[i++] = position; - - // If we are using unicode (UTF8), map the Unicode position back to the UTF8 characters, - // as 1 unicode character can map to multiple UTF8 characters. - // See: http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF - // Or: http://www.cl.cam.ac.uk/~mgk25/unicode.html - if ( unicodeMode ) - { - unsigned char mask = 0xc0; - int count = 1; - // Add one additonal byte for each extra high order one in the byte - while ( uch >= mask && count < 8 ) - { - positions[i++] = position; - count ++; - mask = mask >> 1 | 0x80; // add an additional one in the highest order position - } - } - } - } - - // Free the GlyphInfoArray - free (theGlyphInfoArrayPtr); - } - } - free (buf); -} - -int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { - if (font_.GetID()) - { - textLayout->setText (reinterpret_cast<const UInt8*>(s), len, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); - - // TODO: Maybe I should add some sort of text measurement features to QuartzTextLayout? - unsigned long actualNumberOfBounds = 0; - ATSTrapezoid glyphBounds; - - // We get a single bound, since the text should only require one. If it requires more, there is an issue - if ( ATSUGetGlyphBounds( textLayout->getLayout(), 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &glyphBounds, &actualNumberOfBounds ) != noErr || actualNumberOfBounds != 1 ) - { - Platform::DebugDisplay( "ATSUGetGlyphBounds failed in WidthText" ); - return 0; - } - - //Platform::DebugPrintf( "WidthText: \"%*s\" = %ld\n", len, s, Fix2Long( glyphBounds.upperRight.x - glyphBounds.upperLeft.x ) ); - return Fix2Long( glyphBounds.upperRight.x - glyphBounds.upperLeft.x ); - } - return 1; -} - -int SurfaceImpl::WidthChar(Font &font_, char ch) { - char str[2] = { ch, '\0' }; - if (font_.GetID()) - { - textLayout->setText (reinterpret_cast<const UInt8*>(str), 1, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); - - // TODO: Maybe I should add some sort of text measurement features to QuartzTextLayout? - unsigned long actualNumberOfBounds = 0; - ATSTrapezoid glyphBounds; - - // We get a single bound, since the text should only require one. If it requires more, there is an issue - if ( ATSUGetGlyphBounds( textLayout->getLayout(), 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &glyphBounds, &actualNumberOfBounds ) != noErr || actualNumberOfBounds != 1 ) - { - Platform::DebugDisplay( "ATSUGetGlyphBounds failed in WidthChar" ); - return 0; - } - - return Fix2Long( glyphBounds.upperRight.x - glyphBounds.upperLeft.x ); - } - else - return 1; -} - -// Three possible strategies for determining ascent and descent of font: -// 1) Call ATSUGetGlyphBounds with string containing all letters, numbers and punctuation. -// 2) Use the ascent and descent fields of the font. -// 3) Call ATSUGetGlyphBounds with string as 1 but also including accented capitals. -// Smallest values given by 1 and largest by 3 with 2 in between. -// Techniques 1 and 2 sometimes chop off extreme portions of ascenders and -// descenders but are mostly OK except for accented characters which are -// rarely used in code. - -// This string contains a good range of characters to test for size. -const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890" - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -int SurfaceImpl::Ascent(Font &font_) { - if (!font_.GetID()) - return 1; - - ATSUTextMeasurement ascent = reinterpret_cast<QuartzTextStyle*>( font_.GetID() )->getAttribute<ATSUTextMeasurement>( kATSUAscentTag ); - return Fix2Long( ascent ); -} - -int SurfaceImpl::Descent(Font &font_) { - if (!font_.GetID()) - return 1; - - ATSUTextMeasurement descent = reinterpret_cast<QuartzTextStyle*>( font_.GetID() )->getAttribute<ATSUTextMeasurement>( kATSUDescentTag ); - return Fix2Long( descent ); -} - -int SurfaceImpl::InternalLeading(Font &) { - // TODO: How do we get EM_Size? - // internal leading = ascent - descent - EM_size - return 0; -} - -int SurfaceImpl::ExternalLeading(Font &font_) { - if (!font_.GetID()) - return 1; - - ATSUTextMeasurement lineGap = reinterpret_cast<QuartzTextStyle*>( font_.GetID() )->getAttribute<ATSUTextMeasurement>( kATSULeadingTag ); - return Fix2Long( lineGap ); -} - -int SurfaceImpl::Height(Font &font_) { - return Ascent(font_) + Descent(font_); -} - -int SurfaceImpl::AverageCharWidth(Font &font_) { - - if (!font_.GetID()) - return 1; - - const int sizeStringLength = (sizeof( sizeString ) / sizeof( sizeString[0] ) - 1); - int width = WidthText( font_, sizeString, sizeStringLength ); - - return (int) ((width / (float) sizeStringLength) + 0.5); - - /* - ATSUStyle textStyle = reinterpret_cast<QuartzTextStyle*>( font_.GetID() )->getATSUStyle(); - ATSUFontID fontID; - - ByteCount actualSize = 0; - if ( ATSUGetAttribute( textStyle, kATSUFontTag, sizeof( fontID ), &fontID, &actualSize ) != noErr ) - { -Platform::DebugDisplay( "ATSUGetAttribute failed" ); - return 1; - } - - ATSFontMetrics metrics; - memset( &metrics, 0, sizeof( metrics ) ); - if ( ATSFontGetHorizontalMetrics( fontID, kATSOptionFlagsDefault, &metrics ) != noErr ) - { - Platform::DebugDisplay( "ATSFontGetHorizontalMetrics failed in AverageCharWidth" ); - return 1; - } - - printf( "%f %f %f\n", metrics.avgAdvanceWidth * 32, metrics.ascent * 32, metrics.descent * 32 ); - - return (int) (metrics.avgAdvanceWidth + 0.5);*/ -} - -int SurfaceImpl::SetPalette(Scintilla::Palette *, bool) { - // Mac OS X is always true colour (I think) so this doesn't matter - return 0; -} - -void SurfaceImpl::SetClip(PRectangle rc) { - CGContextClipToRect( gc, PRectangleToCGRect( rc ) ); -} - -void SurfaceImpl::FlushCachedState() { - CGContextSynchronize( gc ); -} - -void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) { - unicodeMode = unicodeMode_; -} - -void SurfaceImpl::SetDBCSMode(int codePage) { - // TODO: Implement this for code pages != UTF-8 -} - -Surface *Surface::Allocate() { - return new SurfaceImpl( ); -} - -Window::~Window() { -} - -void Window::Destroy() { - if (windowRef) { - DisposeWindow(reinterpret_cast<WindowRef>( windowRef )); - } - wid = 0; -} - -bool Window::HasFocus() { - // TODO: Test this - return HIViewSubtreeContainsFocus( reinterpret_cast<HIViewRef>( wid ) ); -} - -PRectangle Window::GetPosition() { - // Before any size allocated pretend its 1000 wide so not scrolled - PRectangle rc(0, 0, 1000, 1000); - - // The frame rectangle gives the position of this view inside the parent view - if (wid) { - HIRect controlFrame; - HIViewGetFrame( reinterpret_cast<HIViewRef>( wid ), &controlFrame ); - rc = CGRectToPRectangle( controlFrame ); - } - - return rc; -} - -void Window::SetPosition(PRectangle rc) { - // Moves this view inside the parent view - if ( wid ) - { - // Set the frame on the view, the function handles the rest - CGRect r = PRectangleToCGRect( rc ); - HIViewSetFrame( reinterpret_cast<HIViewRef>( wid ), &r ); - } -} - -void Window::SetPositionRelative(PRectangle rc, Window window) { - // used to actually move child windows (ie. listbox/calltip) so we have to move - // the window, not the hiview - if (windowRef) { - // we go through some contortions here to get an accurate location for our - // child windows. This is necessary due to the multiple ways an embedding - // app may be setup. See SciTest/main.c (GOOD && BAD) for test case. - WindowRef relativeWindow = GetControlOwner(reinterpret_cast<HIViewRef>( window.GetID() )); - WindowRef thisWindow = reinterpret_cast<WindowRef>( windowRef ); - - Rect portBounds; - ::GetWindowBounds(relativeWindow, kWindowStructureRgn, &portBounds); - //fprintf(stderr, "portBounds %d %d %d %d\n", portBounds.left, portBounds.top, portBounds.right, portBounds.bottom); - PRectangle hbounds = window.GetPosition(); - //fprintf(stderr, "hbounds %d %d %d %d\n", hbounds.left, hbounds.top, hbounds.right, hbounds.bottom); - HIViewRef parent = HIViewGetSuperview(reinterpret_cast<HIViewRef>( window.GetID() )); - Rect pbounds; - GetControlBounds(parent, &pbounds); - //fprintf(stderr, "pbounds %d %d %d %d\n", pbounds.left, pbounds.top, pbounds.right, pbounds.bottom); - - PRectangle bounds; - bounds.top = portBounds.top + pbounds.top + hbounds.top + rc.top; - bounds.bottom = bounds.top + rc.Height(); - bounds.left = portBounds.left + pbounds.left + hbounds.left + rc.left; - bounds.right = bounds.left + rc.Width(); - //fprintf(stderr, "bounds %d %d %d %d\n", bounds.left, bounds.top, bounds.right, bounds.bottom); - - MoveWindow(thisWindow, bounds.left, bounds.top, false); - SizeWindow(thisWindow, bounds.Width(), bounds.Height(), true); - - SetPosition(PRectangle(0,0,rc.Width(),rc.Height())); - } else { - SetPosition(rc); - } -} - -PRectangle Window::GetClientPosition() { - // This means, in MacOS X terms, get the "frame bounds". Call GetPosition, just like on Win32. - return GetPosition(); -} - -void Window::Show(bool show) { - if ( wid ) { - HIViewSetVisible( reinterpret_cast<HIViewRef>( wid ), show ); - } - // this is necessary for calltip/listbox - if (windowRef) { - WindowRef thisWindow = reinterpret_cast<WindowRef>( windowRef ); - if (show) { - ShowWindow( thisWindow ); - DrawControls( thisWindow ); - } else - HideWindow( thisWindow ); - } -} - -void Window::InvalidateAll() { - if ( wid ) { - HIViewSetNeedsDisplay( reinterpret_cast<HIViewRef>( wid ), true ); - } -} - -void Window::InvalidateRectangle(PRectangle rc) { - if (wid) { - // Create a rectangular region - RgnHandle region = NewRgn(); - SetRectRgn( region, rc.left, rc.top, rc.right, rc.bottom ); - - // Make that region invalid - HIViewSetNeedsDisplayInRegion( reinterpret_cast<HIViewRef>( wid ), region, true ); - DisposeRgn( region ); - } -} - -void Window::SetFont(Font &) { - // TODO: Do I need to implement this? MSDN: specifies the font that a control is to use when drawing text. -} - -void Window::SetCursor(Cursor curs) { - if (wid) { - // TODO: This isn't really implemented correctly. I should be using - // mouse tracking rectangles to only set the mouse cursor when it is over the control - ThemeCursor cursor; - - switch ( curs ) { - case cursorText: - cursor = kThemeIBeamCursor; - break; - case cursorArrow: - cursor = kThemeArrowCursor; - break; - case cursorWait: - cursor = kThemeWatchCursor; - break; - case cursorHoriz: - cursor = kThemeResizeLeftRightCursor; - break; - case cursorVert: - cursor = kThemeResizeUpDownCursor; - break; - case cursorReverseArrow: - case cursorUp: - default: - cursor = kThemeArrowCursor; - break; - } - - SetThemeCursor( cursor ); - } -} - -void Window::SetTitle(const char *s) { - WindowRef window = GetControlOwner(reinterpret_cast<HIViewRef>( wid )); - CFStringRef title = CFStringCreateWithCString(kCFAllocatorDefault, s, kCFStringEncodingMacRoman); - SetWindowTitleWithCFString(window, title); - CFRelease(title); -} - -PRectangle Window::GetMonitorRect(Point) { - return PRectangle(); -} - -ListBox::ListBox() {} - -ListBox::~ListBox() {} - -static const OSType scintillaListBoxType = 'sclb'; - -enum { - kItemsPerContainer = 1, - kIconColumn = 'icon', - kTextColumn = 'text' -}; -static SInt32 kScrollBarWidth = 0; - -class LineData { - int *types; - CFStringRef *strings; - int len; - int maximum; -public: - LineData() :types(0), strings(0), len(0), maximum(0) {} - ~LineData() { - Clear(); - } - void Clear() { - delete []types; - types = 0; - for (int i=0; i<maximum; i++) { - if (strings[i]) CFRelease(strings[i]); - } - delete []strings; - strings = 0; - len = 0; - maximum = 0; - } - void Add(int index, int type, CFStringRef str ) { - if (index >= maximum) { - if (index >= len) { - int lenNew = (index+1) * 2; - int *typesNew = new int[lenNew]; - CFStringRef *stringsNew = new CFStringRef[lenNew]; - for (int i=0; i<maximum; i++) { - typesNew[i] = types[i]; - stringsNew[i] = strings[i]; - } - delete []types; - delete []strings; - types = typesNew; - strings = stringsNew; - len = lenNew; - } - while (maximum < index) { - types[maximum] = 0; - strings[maximum] = 0; - maximum++; - } - } - types[index] = type; - strings[index] = str; - if (index == maximum) { - maximum++; - } - } - int GetType(int index) { - if (index < maximum) { - return types[index]; - } else { - return 0; - } - } - CFStringRef GetString(int index) { - if (index < maximum) { - return strings[index]; - } else { - return 0; - } - } -}; - -class ListBoxImpl : public ListBox { -private: - ControlRef lb; - XPMSet xset; - int lineHeight; - bool unicodeMode; - int desiredVisibleRows; - unsigned int maxItemWidth; - unsigned int aveCharWidth; - Font font; - int maxWidth; - - void InstallDataBrowserCustomCallbacks(); - void ConfigureDataBrowser(); - - static pascal OSStatus WindowEventHandler(EventHandlerCallRef inCallRef, - EventRef inEvent, - void *inUserData ); - EventHandlerRef eventHandler; - -protected: - WindowRef windowRef; - -public: - LineData ld; - CallBackAction doubleClickAction; - void *doubleClickActionData; - - ListBoxImpl() : lb(NULL), lineHeight(10), unicodeMode(false), - desiredVisibleRows(5), maxItemWidth(0), aveCharWidth(8), - doubleClickAction(NULL), doubleClickActionData(NULL) - { - if (kScrollBarWidth == 0) - GetThemeMetric(kThemeMetricScrollBarWidth, &kScrollBarWidth); - } - - ~ListBoxImpl() {}; - void SetFont(Font &font); - void Create(Window &parent, int ctrlID, Scintilla::Point pt, int lineHeight_, bool unicodeMode_); - void SetAverageCharWidth(int width); - void SetVisibleRows(int rows); - int GetVisibleRows() const; - PRectangle GetDesiredRect(); - int CaretFromEdge(); - void Clear(); - void Append(char *s, int type = -1); - int Length(); - void Select(int n); - int GetSelection(); - int Find(const char *prefix); - void GetValue(int n, char *value, int len); - void RegisterImage(int type, const char *xpm_data); - void RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage); - void ClearRegisteredImages(); - void SetDoubleClickAction(CallBackAction action, void *data) { - doubleClickAction = action; - doubleClickActionData = data; - } - - int IconWidth(); - void ShowHideScrollbar(); -#ifdef DB_TABLE_ROW_HEIGHT - void SetRowHeight(DataBrowserItemID itemID); -#endif - - void DrawRow(DataBrowserItemID item, - DataBrowserPropertyID property, - DataBrowserItemState itemState, - const Rect *theRect); - - void SetList(const char* list, char separator, char typesep); -}; - -ListBox *ListBox::Allocate() { - ListBoxImpl *lb = new ListBoxImpl(); - return lb; -} - -void ListBoxImpl::Create(Window &/*parent*/, int /*ctrlID*/, Scintilla::Point /*pt*/, - int lineHeight_, bool unicodeMode_) { - lineHeight = lineHeight_; - unicodeMode = unicodeMode_; - maxWidth = 2000; - - WindowClass windowClass = kHelpWindowClass; - WindowAttributes attributes = kWindowNoAttributes; - Rect contentBounds; - WindowRef outWindow; - - contentBounds.top = contentBounds.left = 0; - contentBounds.right = 100; - contentBounds.bottom = lineHeight * desiredVisibleRows; - - CreateNewWindow(windowClass, attributes, &contentBounds, &outWindow); - - InstallStandardEventHandler(GetWindowEventTarget(outWindow)); - - ControlRef root; - CreateRootControl(outWindow, &root); - - CreateDataBrowserControl(outWindow, &contentBounds, kDataBrowserListView, &lb); - -#ifdef DB_TABLE_ROW_HEIGHT - // XXX does not seem to have any effect - SetDataBrowserTableViewRowHeight(lb, lineHeight); -#endif - - // get rid of the frame, forces databrowser to the full size - // of the window - Boolean frameAndFocus = false; - SetControlData(lb, kControlNoPart, kControlDataBrowserIncludesFrameAndFocusTag, - sizeof(frameAndFocus), &frameAndFocus); - - ListBoxImpl* lbThis = this; - SetControlProperty( lb, scintillaListBoxType, 0, sizeof( this ), &lbThis ); - - ConfigureDataBrowser(); - InstallDataBrowserCustomCallbacks(); - - // install event handlers - static const EventTypeSpec kWindowEvents[] = - { - { kEventClassMouse, kEventMouseDown }, - { kEventClassMouse, kEventMouseMoved }, - }; - - eventHandler = NULL; - InstallWindowEventHandler( outWindow, WindowEventHandler, - GetEventTypeCount( kWindowEvents ), - kWindowEvents, this, &eventHandler ); - - wid = lb; - SetControlVisibility(lb, true, true); - SetControl(lb); - SetWindow(outWindow); -} - -pascal OSStatus ListBoxImpl::WindowEventHandler( - EventHandlerCallRef inCallRef, - EventRef inEvent, - void* inUserData ) -{ - - switch (GetEventClass(inEvent)) { - case kEventClassMouse: - switch (GetEventKind(inEvent)) - { - case kEventMouseMoved: - { - SetThemeCursor( kThemeArrowCursor ); - break; - } - case kEventMouseDown: - { - // we cannot handle the double click from the databrowser notify callback as - // calling doubleClickAction causes the listbox to be destroyed. It is - // safe to do it from this event handler since the destroy event will be queued - // until we're done here. - TCarbonEvent event( inEvent ); - EventMouseButton inMouseButton; - event.GetParameter<EventMouseButton>( kEventParamMouseButton, typeMouseButton, &inMouseButton ); - - UInt32 inClickCount; - event.GetParameter( kEventParamClickCount, &inClickCount ); - if (inMouseButton == kEventMouseButtonPrimary && inClickCount == 2) { - // handle our single mouse click now - ListBoxImpl* listbox = reinterpret_cast<ListBoxImpl*>( inUserData ); - const WindowRef window = GetControlOwner(listbox->lb); - const HIViewRef rootView = HIViewGetRoot( window ); - HIViewRef targetView = NULL; - HIViewGetViewForMouseEvent( rootView, inEvent, &targetView ); - if ( targetView == listbox->lb ) - { - if (listbox->doubleClickAction != NULL) { - listbox->doubleClickAction(listbox->doubleClickActionData); - } - } - } - } - } - } - return eventNotHandledErr; -} - -#ifdef DB_TABLE_ROW_HEIGHT -void ListBoxImpl::SetRowHeight(DataBrowserItemID itemID) -{ - // XXX does not seem to have any effect - SetDataBrowserTableViewItemRowHeight(lb, itemID, lineHeight); -} -#endif - -void ListBoxImpl::DrawRow(DataBrowserItemID item, - DataBrowserPropertyID property, - DataBrowserItemState itemState, - const Rect *theRect) -{ - Rect row = *theRect; - row.left = 0; - - ColourPair fore; - - if (itemState == kDataBrowserItemIsSelected) { - long systemVersion; - Gestalt( gestaltSystemVersion, &systemVersion ); - // Panther DB starts using kThemeBrushSecondaryHighlightColor for inactive browser hilighting - if ( (systemVersion >= 0x00001030) && (IsControlActive( lb ) == false) ) - SetThemePen( kThemeBrushSecondaryHighlightColor, 32, true ); - else - //SetThemePen( kThemeBrushPrimaryHighlightColor, 32, true ); - SetThemePen( kThemeBrushAlternatePrimaryHighlightColor, 32, true ); - - PaintRect(&row); - fore = ColourDesired(0xff,0xff,0xff); - } - - int widthPix = xset.GetWidth() + 2; - int pixId = ld.GetType(item - 1); - XPM *pxpm = xset.Get(pixId); - - char s[255]; - GetValue(item - 1, s, 255); - - Surface *surfaceItem = Surface::Allocate(); - if (surfaceItem) { - CGContextRef cgContext; - GrafPtr port; - Rect bounds; - - GetControlBounds(lb, &bounds); - GetPort( &port ); - QDBeginCGContext( port, &cgContext ); - - CGContextSaveGState( cgContext ); - CGContextTranslateCTM(cgContext, 0, bounds.bottom - bounds.top); - CGContextScaleCTM(cgContext, 1.0, -1.0); - - surfaceItem->Init(cgContext, NULL); - - int left = row.left; - if (pxpm) { - PRectangle rc(left + 1, row.top, - left + 1 + widthPix, row.bottom); - pxpm->Draw(surfaceItem, rc); - } - - // draw the text - PRectangle trc(left + 2 + widthPix, row.top, row.right, row.bottom); - int ascent = surfaceItem->Ascent(font) - surfaceItem->InternalLeading(font); - int ytext = trc.top + ascent + 1; - trc.bottom = ytext + surfaceItem->Descent(font) + 1; - surfaceItem->DrawTextTransparent( trc, font, ytext, s, strlen(s), fore.allocated ); - - CGContextRestoreGState( cgContext ); - QDEndCGContext( port, &cgContext ); - delete surfaceItem; - } -} - - -pascal void ListBoxDrawItemCallback(ControlRef browser, DataBrowserItemID item, - DataBrowserPropertyID property, - DataBrowserItemState itemState, - const Rect *theRect, SInt16 gdDepth, - Boolean colorDevice) -{ - if (property != kIconColumn) return; - ListBoxImpl* lbThis = NULL; - OSStatus err; - err = GetControlProperty( browser, scintillaListBoxType, 0, sizeof( lbThis ), NULL, &lbThis ); - // adjust our rect - lbThis->DrawRow(item, property, itemState, theRect); - -} - -void ListBoxImpl::ConfigureDataBrowser() -{ - DataBrowserViewStyle viewStyle; - DataBrowserSelectionFlags selectionFlags; - ::GetDataBrowserViewStyle(lb, &viewStyle); - - ::SetDataBrowserHasScrollBars(lb, false, true); - ::SetDataBrowserListViewHeaderBtnHeight(lb, 0); - ::GetDataBrowserSelectionFlags(lb, &selectionFlags); - ::SetDataBrowserSelectionFlags(lb, selectionFlags |= kDataBrowserSelectOnlyOne); - // if you change the hilite style, also change the style in ListBoxDrawItemCallback - ::SetDataBrowserTableViewHiliteStyle(lb, kDataBrowserTableViewFillHilite); - - Rect insetRect; - ::GetDataBrowserScrollBarInset(lb, &insetRect); - - insetRect.right = kScrollBarWidth - 1; - ::SetDataBrowserScrollBarInset(lb, &insetRect); - - switch (viewStyle) - { - case kDataBrowserListView: - { - DataBrowserListViewColumnDesc iconCol; - iconCol.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc; - iconCol.headerBtnDesc.minimumWidth = 0; - iconCol.headerBtnDesc.maximumWidth = maxWidth; - iconCol.headerBtnDesc.titleOffset = 0; - iconCol.headerBtnDesc.titleString = NULL; - iconCol.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing; - - iconCol.headerBtnDesc.btnFontStyle.flags = kControlUseJustMask; - iconCol.headerBtnDesc.btnFontStyle.just = teFlushLeft; - - iconCol.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly; - - iconCol.propertyDesc.propertyID = kIconColumn; - iconCol.propertyDesc.propertyType = kDataBrowserCustomType; - iconCol.propertyDesc.propertyFlags = kDataBrowserListViewSelectionColumn; - - ::AddDataBrowserListViewColumn(lb, &iconCol, kDataBrowserListViewAppendColumn); - } break; - - } -} - -void ListBoxImpl::InstallDataBrowserCustomCallbacks() -{ - DataBrowserCustomCallbacks callbacks; - - callbacks.version = kDataBrowserLatestCustomCallbacks; - verify_noerr(InitDataBrowserCustomCallbacks(&callbacks)); - callbacks.u.v1.drawItemCallback = NewDataBrowserDrawItemUPP(ListBoxDrawItemCallback); - callbacks.u.v1.hitTestCallback = NULL;//NewDataBrowserHitTestUPP(ListBoxHitTestCallback); - callbacks.u.v1.trackingCallback = NULL;//NewDataBrowserTrackingUPP(ListBoxTrackingCallback); - callbacks.u.v1.editTextCallback = NULL; - callbacks.u.v1.dragRegionCallback = NULL; - callbacks.u.v1.acceptDragCallback = NULL; - callbacks.u.v1.receiveDragCallback = NULL; - - SetDataBrowserCustomCallbacks(lb, &callbacks); -} - -void ListBoxImpl::SetFont(Font &font_) { - // Having to do this conversion is LAME - QuartzTextStyle *ts = reinterpret_cast<QuartzTextStyle*>( font_.GetID() ); - ControlFontStyleRec style; - ATSUAttributeValuePtr value; - ATSUFontID fontID; - style.flags = kControlUseFontMask | kControlUseSizeMask | kControlAddToMetaFontMask; - ts->getAttribute( kATSUFontTag, sizeof(fontID), &fontID, NULL ); - ATSUFontIDtoFOND(fontID, &style.font, NULL); - ts->getAttribute( kATSUSizeTag, sizeof(Fixed), &value, NULL ); - style.size = ((SInt16)FixRound((SInt32)value)); - SetControlFontStyle(lb, &style); - -#ifdef DB_TABLE_ROW_HEIGHT - // XXX this doesn't *stick* - ATSUTextMeasurement ascent = ts->getAttribute<ATSUTextMeasurement>( kATSUAscentTag ); - ATSUTextMeasurement descent = ts->getAttribute<ATSUTextMeasurement>( kATSUDescentTag ); - lineHeight = Fix2Long( ascent ) + Fix2Long( descent ); - SetDataBrowserTableViewRowHeight(lb, lineHeight + lineLeading); -#endif - - // !@&^#%$ we cant copy Font, but we need one for our custom drawing - Str255 fontName255; - char fontName[256]; - FMGetFontFamilyName(style.font, fontName255); - - CFStringRef fontNameCF = ::CFStringCreateWithPascalString( kCFAllocatorDefault, fontName255, kCFStringEncodingMacRoman ); - ::CFStringGetCString( fontNameCF, fontName, (CFIndex)255, kCFStringEncodingMacRoman ); - - font.Create((const char *)fontName, 0, style.size, false, false); -} - -void ListBoxImpl::SetAverageCharWidth(int width) { - aveCharWidth = width; -} - -void ListBoxImpl::SetVisibleRows(int rows) { - desiredVisibleRows = rows; -} - -int ListBoxImpl::GetVisibleRows() const { - // XXX Windows & GTK do this, but it seems incorrect to me. Other logic - // to do with visible rows is essentially the same across platforms. - return desiredVisibleRows; - /* - // This would be more correct - int rows = Length(); - if ((rows == 0) || (rows > desiredVisibleRows)) - rows = desiredVisibleRows; - return rows; - */ -} - -PRectangle ListBoxImpl::GetDesiredRect() { - PRectangle rcDesired = GetPosition(); - - // XXX because setting the line height on the table doesnt - // *stick*, we'll have to suffer and just use whatever - // the table desides is the correct height. - UInt16 itemHeight;// = lineHeight; - GetDataBrowserTableViewRowHeight(lb, &itemHeight); - - int rows = Length(); - if ((rows == 0) || (rows > desiredVisibleRows)) - rows = desiredVisibleRows; - - rcDesired.bottom = itemHeight * rows; - rcDesired.right = rcDesired.left + maxItemWidth + aveCharWidth; - - if (Length() > rows) - rcDesired.right += kScrollBarWidth; - rcDesired.right += IconWidth(); - - // Set the column width - ::SetDataBrowserTableViewColumnWidth (lb, UInt16 (rcDesired.right - rcDesired.left)); - return rcDesired; -} - -void ListBoxImpl::ShowHideScrollbar() { - int rows = Length(); - if (rows > desiredVisibleRows) { - ::SetDataBrowserHasScrollBars(lb, false, true); - } else { - ::SetDataBrowserHasScrollBars(lb, false, false); - } -} - -int ListBoxImpl::IconWidth() { - return xset.GetWidth() + 2; -} - -int ListBoxImpl::CaretFromEdge() { - return 0; -} - -void ListBoxImpl::Clear() { - // passing NULL to "items" arg 4 clears the list - maxItemWidth = 0; - ld.Clear(); - AddDataBrowserItems (lb, kDataBrowserNoItem, 0, NULL, kDataBrowserItemNoProperty); -} - -void ListBoxImpl::Append(char *s, int type) { - int count = Length(); - CFStringRef r = CFStringCreateWithCString(NULL, s, kTextEncodingMacRoman); - ld.Add(count, type, r); - - Scintilla::SurfaceImpl surface; - unsigned int width = surface.WidthText (font, s, strlen (s)); - if (width > maxItemWidth) - maxItemWidth = width; - - DataBrowserItemID items[1]; - items[0] = count + 1; - AddDataBrowserItems (lb, kDataBrowserNoItem, 1, items, kDataBrowserItemNoProperty); - ShowHideScrollbar(); -} - -void ListBoxImpl::SetList(const char* list, char separator, char typesep) { - // XXX copied from PlatGTK, should be in base class - Clear(); - int count = strlen(list) + 1; - char *words = new char[count]; - if (words) { - memcpy(words, list, count); - char *startword = words; - char *numword = NULL; - int i = 0; - for (; words[i]; i++) { - if (words[i] == separator) { - words[i] = '\0'; - if (numword) - *numword = '\0'; - Append(startword, numword?atoi(numword + 1):-1); - startword = words + i + 1; - numword = NULL; - } else if (words[i] == typesep) { - numword = words + i; - } - } - if (startword) { - if (numword) - *numword = '\0'; - Append(startword, numword?atoi(numword + 1):-1); - } - delete []words; - } -} - -int ListBoxImpl::Length() { - UInt32 numItems = 0; - GetDataBrowserItemCount(lb, kDataBrowserNoItem, false, kDataBrowserItemAnyState, &numItems); - return (int)numItems; -} - -void ListBoxImpl::Select(int n) { - DataBrowserItemID items[1]; - items[0] = n + 1; - SetDataBrowserSelectedItems(lb, 1, items, kDataBrowserItemsAssign); - RevealDataBrowserItem(lb, items[0], kIconColumn, kDataBrowserRevealOnly); - // force update on selection - Draw1Control(lb); -} - -int ListBoxImpl::GetSelection() { - Handle selectedItems = NewHandle(0); - GetDataBrowserItems(lb, kDataBrowserNoItem, true, kDataBrowserItemIsSelected, selectedItems); - UInt32 numSelectedItems = GetHandleSize(selectedItems)/sizeof(DataBrowserItemID); - if (numSelectedItems == 0) { - return -1; - } - HLock( selectedItems ); - DataBrowserItemID *individualItem = (DataBrowserItemID*)( *selectedItems ); - DataBrowserItemID selected[numSelectedItems]; - selected[0] = *individualItem; - HUnlock( selectedItems ); - return selected[0] - 1; -} - -int ListBoxImpl::Find(const char *prefix) { - int count = Length(); - char s[255]; - for (int i = 0; i < count; i++) { - GetValue(i, s, 255); - if (s[0] != NULL && (0 == strncmp(prefix, s, strlen(prefix)))) { - return i; - } - } - return - 1; -} - -void ListBoxImpl::GetValue(int n, char *value, int len) { - CFStringRef textString = ld.GetString(n); - if (textString == NULL) { - value[0] = '\0'; - return; - } - CFIndex numUniChars = CFStringGetLength( textString ); - - // XXX how do we know the encoding of the listbox? - CFStringEncoding encoding = kCFStringEncodingUTF8; //( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingASCII); - CFIndex maximumByteLength = CFStringGetMaximumSizeForEncoding( numUniChars, encoding ) + 1; - char* text = new char[maximumByteLength]; - CFIndex usedBufferLength = 0; - CFStringGetBytes( textString, CFRangeMake( 0, numUniChars ), encoding, - '?', false, reinterpret_cast<UInt8*>( text ), - maximumByteLength, &usedBufferLength ); - text[usedBufferLength] = '\0'; // null terminate the ASCII/UTF8 string - - if (text && len > 0) { - strncpy(value, text, len); - value[len - 1] = '\0'; - } else { - value[0] = '\0'; - } - delete []text; -} - -void ListBoxImpl::RegisterImage(int type, const char *xpm_data) { - xset.Add(type, xpm_data); -} - -void ListBoxImpl::RegisterRGBAImage(int /* type */, int /* width */, int /* height */, const unsigned char * /*pixelsImage */) { - // Not supported for Carbon -} - -void ListBoxImpl::ClearRegisteredImages() { - xset.Clear(); -} - -Menu::Menu() : mid(0) { } - -void Menu::CreatePopUp() { - // TODO: Could I just feed a constant menu ID parameter, or does - // it really need to be unique? - static int nextMenuID = 1; - Destroy(); - OSStatus err; - err = CreateNewMenu( nextMenuID++, 0, reinterpret_cast<MenuRef*>( &mid ) ); -} - -void Menu::Destroy() { - if ( mid != NULL ) - { - ReleaseMenu( reinterpret_cast<MenuRef>( mid ) ); - mid = NULL; - } -} - -void Menu::Show(Point pt, Window &) { - UInt32 userSelection = 0; - SInt16 menuId = 0; - MenuItemIndex menuItem = 0; - ::Point globalPoint; - globalPoint.h = pt.x; - globalPoint.v = pt.y; - OSStatus err; - err = ContextualMenuSelect( reinterpret_cast<MenuRef>( mid ), globalPoint, - false, kCMHelpItemRemoveHelp, NULL, - NULL, &userSelection, - &menuId, - &menuItem - ); -} - -// ElapsedTime is used for precise performance measurements during development -// and not for anything a user sees. - -ElapsedTime::ElapsedTime() { - struct timeval curTime; - int retVal; - retVal = gettimeofday( &curTime, NULL ); - - bigBit = curTime.tv_sec; - littleBit = curTime.tv_usec; -} - -double ElapsedTime::Duration(bool reset) { - struct timeval curTime; - int retVal; - retVal = gettimeofday( &curTime, NULL ); - long endBigBit = curTime.tv_sec; - long endLittleBit = curTime.tv_usec; - double result = 1000000.0 * (endBigBit - bigBit); - result += endLittleBit - littleBit; - result /= 1000000.0; - if (reset) { - bigBit = endBigBit; - littleBit = endLittleBit; - } - return result; -} - -ColourDesired Platform::Chrome() { - RGBColor c; - GetThemeBrushAsColor(kThemeBrushButtonActiveDarkShadow , 24, true, &c); - return ColourDesired(c.red>>8, c.green>>8, c.blue>>8); -} - -ColourDesired Platform::ChromeHighlight() { - RGBColor c; - GetThemeBrushAsColor(kThemeBrushButtonInactiveLightShadow , 24, true, &c); - return ColourDesired(c.red>>8, c.green>>8, c.blue>>8); -} - -static Str255 PlatformDefaultFontName; -const char *Platform::DefaultFont() { - long fid = HighShortFromLong(GetScriptVariable(smCurrentScript, smScriptAppFondSize)); - FMGetFontFamilyName(fid, PlatformDefaultFontName); - char* defaultFontName = (char*) PlatformDefaultFontName; - defaultFontName[defaultFontName[0]+1] = 0; - ++defaultFontName; - - return defaultFontName; -} - -int Platform::DefaultFontSize() { - return LowShortFromLong(GetScriptVariable(smCurrentScript, smScriptAppFondSize)); -} - -unsigned int Platform::DoubleClickTime() { - // Convert from ticks to milliseconds. I think it would be better to use the events to tell us - // when we have a double and triple click, but what do I know? - return static_cast<unsigned int>( TicksToEventTime( GetDblTime() ) / kEventDurationMillisecond ); -} - -bool Platform::MouseButtonBounce() { - return false; -} - -bool Platform::IsKeyDown(int keyCode) { - return false; - // TODO: Map Scintilla/Windows key codes to Mac OS X key codes - // TODO: Do I need this? - /* - // Inspired by code at: http://www.sover.net/~jams/Morgan/docs/GameInputMethods.txt - - // Get the keys - KeyMap keys; - GetKeys( keys ); - - // Calculate the key map index - long keyMapIndex = keys[keyCode/8]; - // Calculate the individual bit to check - short bitToCheck = keyCode % 8; - // Check the status of the key - return ( keyMapIndex >> bitToCheck ) & 0x01; - */ -} - -long Platform::SendScintilla(WindowID w, unsigned int msg, unsigned long wParam, long lParam) { - return scintilla_send_message( w, msg, wParam, lParam ); -} - -bool Platform::IsDBCSLeadByte(int /*codePage*/, char /*ch*/) { - // TODO: Implement this for code pages != UTF-8 - return false; -} - -int Platform::DBCSCharLength(int /*codePage*/, const char* /*s*/) { - // TODO: Implement this for code pages != UTF-8 - return 1; -} - -int Platform::DBCSCharMaxLength() { - // TODO: Implement this for code pages != UTF-8 - //return CFStringGetMaximumSizeForEncoding( 1, CFStringEncoding encoding ); - return 2; -} - -// These are utility functions not really tied to a platform -int Platform::Minimum(int a, int b) { - if (a < b) - return a; - else - return b; -} - -int Platform::Maximum(int a, int b) { - if (a > b) - return a; - else - return b; -} - -//#define TRACE -#ifdef TRACE - -void Platform::DebugDisplay(const char *s) { - fprintf( stderr, s ); -} - -void Platform::DebugPrintf(const char *format, ...) { - const int BUF_SIZE = 2000; - char buffer[BUF_SIZE]; - - va_list pArguments; - va_start(pArguments, format); - vsnprintf(buffer, BUF_SIZE, format, pArguments); - va_end(pArguments); - Platform::DebugDisplay(buffer); -} - -#else - -void Platform::DebugDisplay(const char *) {} - -void Platform::DebugPrintf(const char *, ...) {} - -#endif - -// Not supported for GTK+ -static bool assertionPopUps = true; - -bool Platform::ShowAssertionPopUps(bool assertionPopUps_) { - bool ret = assertionPopUps; - assertionPopUps = assertionPopUps_; - return ret; -} - -void Platform::Assert(const char *c, const char *file, int line) { - char buffer[2000]; - sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line); - strcat(buffer, "\r\n"); - Platform::DebugDisplay(buffer); -#ifdef DEBUG - // Jump into debugger in assert on Mac (CL269835) - ::Debugger(); -#endif -} - -int Platform::Clamp(int val, int minVal, int maxVal) { - if (val > maxVal) - val = maxVal; - if (val < minVal) - val = minVal; - return val; -} |