123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910 |
- /**
- * @file llopenglview-objc.mm
- * @brief Class implementation for most of the Mac facing window functionality.
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
- #import "llopenglview-objc.h"
- #import "llwindowmacosx-objc.h"
- #import "llappdelegate-objc.h"
- #pragma mark local functions
- // Fix for deprecated convertScreenToBase, should make new function instead.
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- extern bool gHiDPISupport;
- NativeKeyEventData extractKeyDataFromKeyEvent(NSEvent* theEvent)
- {
- NativeKeyEventData eventData;
- eventData.mKeyEvent = NativeKeyEventData::KEYUNKNOWN;
- eventData.mEventType = [theEvent type];
- eventData.mEventModifiers = [theEvent modifierFlags];
- eventData.mEventKeyCode = [theEvent keyCode];
- NSString *strEventChars = [theEvent characters];
- eventData.mEventChars = (strEventChars.length) ? [strEventChars characterAtIndex:0] : 0;
- NSString *strEventUChars = [theEvent charactersIgnoringModifiers];
- eventData.mEventUnmodChars = (strEventUChars.length) ? [strEventUChars characterAtIndex:0] : 0;
- eventData.mEventRepeat = [theEvent isARepeat];
- return eventData;
- }
- NativeKeyEventData extractKeyDataFromModifierEvent(NSEvent* theEvent)
- {
- NativeKeyEventData eventData;
- eventData.mKeyEvent = NativeKeyEventData::KEYUNKNOWN;
- eventData.mEventType = [theEvent type];
- eventData.mEventModifiers = [theEvent modifierFlags];
- eventData.mEventKeyCode = [theEvent keyCode];
- return eventData;
- }
- attributedStringInfo getSegments(NSAttributedString *str)
- {
- attributedStringInfo segments;
- segment_lengths seg_lengths;
- segment_standouts seg_standouts;
- NSRange effectiveRange;
- NSRange limitRange = NSMakeRange(0, [str length]);
- while (limitRange.length > 0) {
- NSNumber *attr = [str attribute:NSUnderlineStyleAttributeName atIndex:limitRange.location longestEffectiveRange:&effectiveRange inRange:limitRange];
- limitRange = NSMakeRange(NSMaxRange(effectiveRange), NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
- if (effectiveRange.length <= 0)
- {
- effectiveRange.length = 1;
- }
- if ([attr integerValue] == 2)
- {
- seg_lengths.push_back(effectiveRange.length);
- seg_standouts.push_back(true);
- }
- else
- {
- seg_lengths.push_back(effectiveRange.length);
- seg_standouts.push_back(false);
- }
- }
- segments.seg_lengths = seg_lengths;
- segments.seg_standouts = seg_standouts;
- return segments;
- }
- #pragma mark class implementations
- @implementation NSScreen (PointConversion)
- + (NSScreen *)currentScreenForMouseLocation
- {
- NSPoint mouseLocation = [NSEvent mouseLocation];
- NSEnumerator *screenEnumerator = [[NSScreen screens] objectEnumerator];
- NSScreen *screen;
- while ((screen = [screenEnumerator nextObject]) && !NSMouseInRect(mouseLocation, screen.frame, NO))
- ;
- return screen;
- }
- - (NSPoint)convertPointToScreenCoordinates:(NSPoint)aPoint
- {
- float normalizedX = fabs(fabs(self.frame.origin.x) - fabs(aPoint.x));
- float normalizedY = aPoint.y - self.frame.origin.y;
- return NSMakePoint(normalizedX, normalizedY);
- }
- - (NSPoint)flipPoint:(NSPoint)aPoint
- {
- return NSMakePoint(aPoint.x, self.frame.size.height - aPoint.y);
- }
- @end
- @implementation LLOpenGLView
- - (unsigned long)getVramSize
- {
- CGLRendererInfoObj info = 0;
- GLint vram_megabytes = 0;
- int num_renderers = 0;
- CGLError the_err = CGLQueryRendererInfo (CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers);
- if(0 == the_err)
- {
- CGLDescribeRenderer (info, 0, kCGLRPTextureMemoryMegabytes, &vram_megabytes);
- CGLDestroyRendererInfo (info);
- }
- else
- {
- vram_megabytes = 256;
- }
- return (unsigned long)vram_megabytes; // return value is in megabytes.
- }
- - (void)viewDidMoveToWindow
- {
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(windowResized:) name:NSWindowDidResizeNotification
- object:[self window]];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification
- object:[self window]];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification
- object:[self window]];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification
- object:[self window]];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification
- object:[self window]];
- NSRect wnd_rect = [[self window] frame];
- NSRect dev_rect = [self convertRectToBacking:wnd_rect];
- if (!NSEqualSizes(wnd_rect.size, dev_rect.size))
- {
- callResize(dev_rect.size.width, dev_rect.size.height);
- }
- }
- - (void)windowResized:(NSNotification *)notification;
- {
- NSSize dev_sz = gHiDPISupport ? [self convertSizeToBacking:[self frame].size] : [self frame].size;
- callResize(dev_sz.width, dev_sz.height);
- }
- - (void)windowWillMiniaturize:(NSNotification *)notification;
- {
- callWindowHide();
- }
- - (void)windowDidDeminiaturize:(NSNotification *)notification;
- {
- callWindowUnhide();
- }
- - (void)windowDidBecomeKey:(NSNotification *)notification;
- {
- mModifiers = [NSEvent modifierFlags];
- }
- -(void)windowDidChangeScreen:(NSNotification *)notification;
- {
- callWindowDidChangeScreen();
- }
- - (void)dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [super dealloc];
- }
- - (id) init
- {
- return [self initWithFrame:[self bounds] withSamples:2 andVsync:TRUE andCoreGL:FALSE];
- }
- - (id) initWithFrame:(NSRect)frame withSamples:(NSUInteger)samples andVsync:(BOOL)vsync andCoreGL:(BOOL)core_gl
- {
- [self registerForDraggedTypes:[NSArray arrayWithObject:NSURLPboardType]];
- [self initWithFrame:frame];
- // Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6.
- // Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat.
- // 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons).
- NSOpenGLPixelFormatAttribute attrs[] = {
- NSOpenGLPFANoRecovery,
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFAClosestPolicy,
- NSOpenGLPFAAccelerated,
- NSOpenGLPFASampleBuffers,
- static_cast<NSOpenGLPixelFormatAttribute>(samples > 0 ? 1 : 0),
- NSOpenGLPFASamples, static_cast<NSOpenGLPixelFormatAttribute>(samples),
- NSOpenGLPFAStencilSize, 8,
- NSOpenGLPFADepthSize, 24,
- NSOpenGLPFAAlphaSize, 8,
- NSOpenGLPFAColorSize, 24,
- NSOpenGLPFAOpenGLProfile,
- (core_gl ? NSOpenGLProfileVersion3_2Core
- : NSOpenGLProfileVersionLegacy),
- 0
- };
- NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
- if (pixelFormat == nil)
- {
- NSLog(@"Failed to create pixel format !", nil);
- return nil;
- }
- NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
- if (glContext == nil)
- {
- NSLog(@"Failed to create OpenGL context !", nil);
- return nil;
- }
- [self setPixelFormat:pixelFormat];
- // For Retina support
- // IMPORTANT NOTE: this code seems totally insufficent, when the viewer is
- // compiled with Xcode 10 or 11: HiDPI support must be specified via a true
- // or false value for a NSHighResolutionCapable key in the Info.plist file,
- // or bad things happen... This is the reason why this code is actually
- // hard-wired to non-HiDPI mode (gHiDPISupport always false) in the Cool VL
- // Viewer. HB
- if (gHiDPISupport)
- {
- [self setWantsBestResolutionOpenGLSurface:YES];
- }
- else
- {
- [self setWantsBestResolutionOpenGLSurface:NO];
- }
- [self setOpenGLContext:glContext];
- [glContext setView:self];
- [glContext makeCurrentContext];
- if (vsync)
- {
- GLint value = 1;
- [glContext setValues:&value forParameter:NSOpenGLCPSwapInterval];
- }
- else
- {
- // Suppress this error after move to Xcode 7:
- // error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull]
- // Tried using ObjC 'nonnull' keyword as per SO article but didn't build
- GLint swapInterval=0;
- [glContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
- }
- return self;
- }
- - (BOOL) rebuildContext
- {
- return [self rebuildContextWithFormat:[self pixelFormat]];
- }
- - (BOOL) rebuildContextWithFormat:(NSOpenGLPixelFormat *)format
- {
- NSOpenGLContext *ctx = [self openGLContext];
- [ctx clearDrawable];
- [ctx initWithFormat:format shareContext:nil];
- if (ctx == nil)
- {
- NSLog(@"Failed to create OpenGL context !", nil);
- return false;
- }
- [self setOpenGLContext:ctx];
- [ctx setView:self];
- [ctx makeCurrentContext];
- return true;
- }
- - (CGLContextObj)getCGLContextObj
- {
- NSOpenGLContext *ctx = [self openGLContext];
- return (CGLContextObj)[ctx CGLContextObj];
- }
- - (CGLPixelFormatObj*)getCGLPixelFormatObj
- {
- NSOpenGLPixelFormat *fmt = [self pixelFormat];
- return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj];
- }
- // Various events can be intercepted by our view, thus not reaching our window.
- // Intercept these events, and pass them to the window as needed. - Geenz
- - (void) mouseDown:(NSEvent *)theEvent
- {
- NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow];
- mMousePos[0] = mPoint.x;
- mMousePos[1] = mPoint.y;
- // Apparently people still use this?
- if ([theEvent modifierFlags] & NSCommandKeyMask &&
- !([theEvent modifierFlags] & NSControlKeyMask) &&
- !([theEvent modifierFlags] & NSShiftKeyMask) &&
- !([theEvent modifierFlags] & NSAlternateKeyMask) &&
- !([theEvent modifierFlags] & NSAlphaShiftKeyMask) &&
- !([theEvent modifierFlags] & NSFunctionKeyMask) &&
- !([theEvent modifierFlags] & NSHelpKeyMask))
- {
- callRightMouseDown(mMousePos, [theEvent modifierFlags]);
- mSimulatedRightClick = true;
- }
- else
- {
- if ([theEvent clickCount] >= 2)
- {
- callDoubleClick(mMousePos, [theEvent modifierFlags]);
- }
- else if ([theEvent clickCount] == 1)
- {
- callLeftMouseDown(mMousePos, [theEvent modifierFlags]);
- }
- }
- }
- - (void) mouseUp:(NSEvent *)theEvent
- {
- if (mSimulatedRightClick)
- {
- callRightMouseUp(mMousePos, [theEvent modifierFlags]);
- mSimulatedRightClick = false;
- }
- else
- {
- NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow];
- mMousePos[0] = mPoint.x;
- mMousePos[1] = mPoint.y;
- callLeftMouseUp(mMousePos, [theEvent modifierFlags]);
- }
- }
- - (void) rightMouseDown:(NSEvent *)theEvent
- {
- NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow];
- mMousePos[0] = mPoint.x;
- mMousePos[1] = mPoint.y;
- callRightMouseDown(mMousePos, [theEvent modifierFlags]);
- }
- - (void) rightMouseUp:(NSEvent *)theEvent
- {
- NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow];
- mMousePos[0] = mPoint.x;
- mMousePos[1] = mPoint.y;
- callRightMouseUp(mMousePos, [theEvent modifierFlags]);
- }
- - (void)mouseMoved:(NSEvent *)theEvent
- {
- NSPoint dev_delta = gHiDPISupport ? [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])] : NSMakePoint([theEvent deltaX], [theEvent deltaY]);
- float mouseDeltas[] = {
- float(dev_delta.x),
- float(dev_delta.y)
- };
- callDeltaUpdate(mouseDeltas, 0);
- NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow];
- mMousePos[0] = mPoint.x;
- mMousePos[1] = mPoint.y;
- callMouseMoved(mMousePos, 0);
- }
- // NSWindow doesn't trigger mouseMoved when the mouse is being clicked and dragged.
- // Use mouseDragged for situations like this to trigger our movement callback instead.
- - (void) mouseDragged:(NSEvent *)theEvent
- {
- // Trust the deltas supplied by NSEvent.
- // The old CoreGraphics APIs we previously relied on are now flagged as obsolete.
- // NSEvent isn't obsolete, and provides us with the correct deltas.
- NSPoint dev_delta = gHiDPISupport ? [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])] : NSMakePoint([theEvent deltaX], [theEvent deltaY]);
- float mouseDeltas[] = {
- float(dev_delta.x),
- float(dev_delta.y)
- };
- callDeltaUpdate(mouseDeltas, 0);
- NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow];
- mMousePos[0] = mPoint.x;
- mMousePos[1] = mPoint.y;
- callMouseDragged(mMousePos, 0);
- }
- - (void) otherMouseDown:(NSEvent *)theEvent
- {
- NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow];
- mMousePos[0] = mPoint.x;
- mMousePos[1] = mPoint.y;
- callMiddleMouseDown(mMousePos, [theEvent modifierFlags]);
- }
- - (void) otherMouseUp:(NSEvent *)theEvent
- {
- NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow];
- mMousePos[0] = mPoint.x;
- mMousePos[1] = mPoint.y;
- callMiddleMouseUp(mMousePos, [theEvent modifierFlags]);
- }
- - (void) rightMouseDragged:(NSEvent *)theEvent
- {
- [self mouseDragged:theEvent];
- }
- - (void) otherMouseDragged:(NSEvent *)theEvent
- {
- [self mouseDragged:theEvent];
- }
- - (void) scrollWheel:(NSEvent *)theEvent
- {
- callScrollMoved(-[theEvent deltaY]);
- }
- - (void) mouseExited:(NSEvent *)theEvent
- {
- callMouseExit();
- }
- - (void) keyUp:(NSEvent *)theEvent
- {
- NativeKeyEventData eventData = extractKeyDataFromKeyEvent(theEvent);
- eventData.mKeyEvent = NativeKeyEventData::KEYUP;
- callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]);
- }
- - (void) keyDown:(NSEvent *)theEvent
- {
- NativeKeyEventData eventData = extractKeyDataFromKeyEvent(theEvent);
- eventData.mKeyEvent = NativeKeyEventData::KEYDOWN;
- uint keycode = [theEvent keyCode];
- // We must not depend on flagsChange event to detect modifier flags changed,
- // must depend on the modifire flags in the event parameter.
- // Because flagsChange event handler misses event when other window is activated,
- // e.g. OS Window for upload something or Input Window...
- // mModifiers instance variable is for insertText: or insertText:replacementRange: (by Pell Smit)
- mModifiers = [theEvent modifierFlags];
- bool acceptsText = mHasMarkedText ? false : callKeyDown(&eventData, keycode, mModifiers);
- unichar ch;
- if (acceptsText &&
- !mMarkedTextAllowed &&
- !(mModifiers & (NSControlKeyMask | NSCommandKeyMask)) && // commands don't invoke InputWindow
- ![(LLAppDelegate*)[NSApp delegate] romanScript] &&
- (ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]) > ' ' &&
- ch != NSDeleteCharacter &&
- (ch < 0xF700 || ch > 0xF8FF)) // 0xF700-0xF8FF: reserved for function keys on the keyboard(from NSEvent.h)
- {
- [(LLAppDelegate*)[NSApp delegate] showInputWindow:true withEvent:theEvent];
- }
- else
- {
- [[self inputContext] handleEvent:theEvent];
- }
- // OS X (intentionally) does not send key-up information on cmd-key
- // combinations. Since SL assumes we receive those, we fake it here.
- if (mModifiers & NSCommandKeyMask && !mHasMarkedText)
- {
- eventData.mKeyEvent = NativeKeyEventData::KEYUP;
- callKeyUp(&eventData, [theEvent keyCode], mModifiers);
- }
- }
- - (void)flagsChanged:(NSEvent *)theEvent
- {
- NativeKeyEventData eventData = extractKeyDataFromModifierEvent(theEvent);
- mModifiers = [theEvent modifierFlags];
- callModifier([theEvent modifierFlags]);
- NSInteger mask = 0;
- switch([theEvent keyCode])
- {
- case 56:
- mask = NSShiftKeyMask;
- break;
- case 58:
- mask = NSAlternateKeyMask;
- break;
- case 59:
- mask = NSControlKeyMask;
- break;
- default:
- return;
- }
- if (mModifiers & mask)
- {
- eventData.mKeyEvent = NativeKeyEventData::KEYDOWN;
- callKeyDown(&eventData, [theEvent keyCode], 0);
- }
- else
- {
- eventData.mKeyEvent = NativeKeyEventData::KEYUP;
- callKeyUp(&eventData, [theEvent keyCode], 0);
- }
- }
- - (BOOL) acceptsFirstResponder
- {
- return YES;
- }
- - (NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender
- {
- NSPasteboard *pboard;
- NSDragOperation sourceDragMask;
- sourceDragMask = [sender draggingSourceOperationMask];
- pboard = [sender draggingPasteboard];
- if ([[pboard types] containsObject:NSURLPboardType])
- {
- if (sourceDragMask & NSDragOperationLink) {
- NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0];
- mLastDraggedUrl = [[fileUrl absoluteString] UTF8String];
- return NSDragOperationLink;
- }
- }
- return NSDragOperationNone;
- }
- - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
- {
- callHandleDragUpdated(mLastDraggedUrl);
- return NSDragOperationLink;
- }
- - (void) draggingExited:(id<NSDraggingInfo>)sender
- {
- callHandleDragExited(mLastDraggedUrl);
- }
- - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
- {
- return YES;
- }
- - (BOOL) performDragOperation:(id<NSDraggingInfo>)sender
- {
- callHandleDragDropped(mLastDraggedUrl);
- return true;
- }
- - (BOOL)hasMarkedText
- {
- return mHasMarkedText;
- }
- - (NSRange)markedRange
- {
- int range[2];
- getPreeditMarkedRange(&range[0], &range[1]);
- return NSMakeRange(range[0], range[1]);
- }
- - (NSRange)selectedRange
- {
- int range[2];
- getPreeditSelectionRange(&range[0], &range[1]);
- return NSMakeRange(range[0], range[1]);
- }
- - (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
- {
- // Apple says aString can be either an NSString or NSAttributedString instance.
- // But actually it's NSConcreteMutableAttributedString or __NSCFConstantString.
- // I observed aString was __NSCFConstantString only aString was null string(zero length).
- // Apple also says when aString is an NSString object,
- // the receiver is expected to render the marked text with distinguishing appearance.
- // So I tried to make attributedStringInfo, but it won't be used... (Pell Smit)
- if (mMarkedTextAllowed)
- {
- unsigned int selected[2] = {
- unsigned(selectedRange.location),
- unsigned(selectedRange.length)
- };
- unsigned int replacement[2] = {
- unsigned(replacementRange.location),
- unsigned(replacementRange.length)
- };
- int string_length = [aString length];
- unichar text[string_length];
- attributedStringInfo segments;
- // I used 'respondsToSelector:@selector(string)'
- // to judge aString is an attributed string or not.
- if ([aString respondsToSelector:@selector(string)])
- {
- // aString is attibuted
- [[aString string] getCharacters:text range:NSMakeRange(0, string_length)];
- segments = getSegments((NSAttributedString *)aString);
- }
- else
- {
- // aString is not attributed
- [aString getCharacters:text range:NSMakeRange(0, string_length)];
- segments.seg_lengths.push_back(string_length);
- segments.seg_standouts.push_back(true);
- }
- setMarkedText(text, selected, replacement, string_length, segments);
- if (string_length > 0)
- {
- mHasMarkedText = TRUE;
- mMarkedTextLength = string_length;
- }
- else
- {
- // we must clear the marked text when aString is null.
- [self unmarkText];
- }
- }
- else
- {
- if (mHasMarkedText)
- {
- [self unmarkText];
- }
- }
- }
- - (void)commitCurrentPreedit
- {
- if (mHasMarkedText)
- {
- if ([[self inputContext] respondsToSelector:@selector(commitEditing)])
- {
- [[self inputContext] commitEditing];
- }
- }
- }
- - (void)unmarkText
- {
- [[self inputContext] discardMarkedText];
- resetPreedit();
- mHasMarkedText = FALSE;
- }
- // We don't support attributed strings.
- - (NSArray *)validAttributesForMarkedText
- {
- return [NSArray array];
- }
- // See above.
- - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
- {
- return nil;
- }
- - (void)insertText:(id)insertString
- {
- if (insertString != nil)
- {
- [self insertText:insertString replacementRange:NSMakeRange(0, [insertString length])];
- }
- }
- - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
- {
- if (!mHasMarkedText)
- {
- for (NSInteger i = 0; i < [aString length]; i++)
- {
- callUnicodeCallback([aString characterAtIndex:i], mModifiers);
- }
- }
- else
- {
- resetPreedit();
- // We may never get this point since unmarkText may be called before insertText ever gets called once we submit our text.
- // But just in case...
- for (NSInteger i = 0; i < [aString length]; i++)
- {
- handleUnicodeCharacter([aString characterAtIndex:i]);
- }
- mHasMarkedText = FALSE;
- }
- }
- - (void) insertNewline:(id)sender
- {
- if (!(mModifiers & NSCommandKeyMask) &&
- !(mModifiers & NSShiftKeyMask) &&
- !(mModifiers & NSAlternateKeyMask))
- {
- callUnicodeCallback(13, 0);
- }
- else
- {
- callUnicodeCallback(13, mModifiers);
- }
- }
- - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
- {
- return NSNotFound;
- }
- - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
- {
- float pos[4] = {0, 0, 0, 0};
- getPreeditLocation(pos, mMarkedTextLength);
- return NSMakeRect(pos[0], pos[1], pos[2], pos[3]);
- }
- - (void)doCommandBySelector:(SEL)aSelector
- {
- if (aSelector == @selector(insertNewline:))
- {
- [self insertNewline:self];
- }
- }
- - (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex
- {
- return NO;
- }
- - (void) allowMarkedTextInput:(bool)allowed
- {
- mMarkedTextAllowed = allowed;
- }
- @end
- @implementation LLUserInputWindow
- - (void) close
- {
- [self orderOut:self];
- }
- @end
- @implementation LLNonInlineTextView
- /* Input Window is a legacy of 20 century, so we want to remove related classes.
- But unfortunately, Viwer web browser has no support for modern inline input,
- we need to leave these classes...
- We will be back to get rid of Input Window after fixing viewer web browser.
- How Input Window should work:
- 1) Input Window must not be empty.
- It must close when it become empty result of edithing.
- 2) Input Window must not close when it still has input data.
- It must keep open user types next char before commit. by Pell Smit
- */
- - (void) setGLView:(LLOpenGLView *)view
- {
- glview = view;
- }
- - (void)keyDown:(NSEvent *)theEvent
- {
- // mKeyPressed is used later to determine whethere Input Window should close or not
- mKeyPressed = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
- // setMarkedText and insertText is called indirectly from inside keyDown: method
- [super keyDown:theEvent];
- }
- // setMarkedText: is called for incomplete input(on the way to conversion).
- - (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
- {
- [super setMarkedText:aString selectedRange:selectedRange replacementRange:replacementRange];
- if ([aString length] == 0) // this means Input Widow becomes empty
- {
- [self.window orderOut:self.window]; // Close this to avoid empty Input Window
- }
- }
- // insertText: is called for inserting commited text.
- // There are two ways to be called here:
- // a) explicitly commited (must close)
- // In case of user typed commit key(usually return key) or delete key or something
- // b) automatically commited (must not close)
- // In case of user typed next letter after conversion
- - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
- {
- [[self inputContext] discardMarkedText];
- [self setString:@""];
- [glview insertText:aString replacementRange:replacementRange];
- if (mKeyPressed == NSEnterCharacter ||
- mKeyPressed == NSBackspaceCharacter ||
- mKeyPressed == NSTabCharacter ||
- mKeyPressed == NSNewlineCharacter ||
- mKeyPressed == NSCarriageReturnCharacter ||
- mKeyPressed == NSDeleteCharacter ||
- (mKeyPressed >= 0xF700 && mKeyPressed <= 0xF8FF))
- {
- // this is case a) of above comment
- [self.window orderOut:self.window]; // to avoid empty Input Window
- }
- }
- @end
- @implementation LLNSWindow
- - (id) init
- {
- return self;
- }
- - (NSPoint)convertToScreenFromLocalPoint:(NSPoint)point relativeToView:(NSView *)view
- {
- NSScreen *currentScreen = [NSScreen currentScreenForMouseLocation];
- if(currentScreen)
- {
- NSPoint windowPoint = [view convertPoint:point toView:nil];
- NSPoint screenPoint = [[view window] convertBaseToScreen:windowPoint];
- NSPoint flippedScreenPoint = [currentScreen flipPoint:screenPoint];
- flippedScreenPoint.y += [currentScreen frame].origin.y;
- return flippedScreenPoint;
- }
- return NSZeroPoint;
- }
- - (NSPoint)flipPoint:(NSPoint)aPoint
- {
- return NSMakePoint(aPoint.x, self.frame.size.height - aPoint.y);
- }
- - (BOOL) becomeFirstResponder
- {
- callFocus();
- return true;
- }
- - (BOOL) resignFirstResponder
- {
- callFocusLost();
- return true;
- }
- - (BOOL)windowShouldClose:(NSWindow *)sender;
- {
- handleQuit();
- return NO;
- }
- - (void) close
- {
- callQuitHandler();
- }
- @end
|