diff options
Diffstat (limited to 'src/osd/modules/debugger/osx/debugview.mm')
-rw-r--r-- | src/osd/modules/debugger/osx/debugview.mm | 849 |
1 files changed, 849 insertions, 0 deletions
diff --git a/src/osd/modules/debugger/osx/debugview.mm b/src/osd/modules/debugger/osx/debugview.mm new file mode 100644 index 00000000000..e793c649d9e --- /dev/null +++ b/src/osd/modules/debugger/osx/debugview.mm @@ -0,0 +1,849 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +//============================================================ +// +// debugview.m - MacOS X Cocoa debug window handling +// +//============================================================ + +#import "debugview.h" + +#include "debug/debugcpu.h" + +#include "modules/lib/osdobj_common.h" + +#include <string.h> + + +static NSColor *DefaultForeground; +static NSColor *ChangedForeground; +static NSColor *InvalidForeground; +static NSColor *CommentForeground; +static NSColor *DisabledChangedForeground; +static NSColor *DisabledInvalidForeground; +static NSColor *DisabledCommentForeground; + +static NSColor *DefaultBackground; +static NSColor *VisitedBackground; +static NSColor *AncillaryBackground; +static NSColor *SelectedBackground; +static NSColor *CurrentBackground; +static NSColor *SelectedCurrentBackground; +static NSColor *InactiveSelectedBackground; +static NSColor *InactiveSelectedCurrentBackground; + +static NSCharacterSet *NonWhiteCharacters; + + +static void debugwin_view_update(debug_view &view, void *osdprivate) +{ + NSAutoreleasePool *const pool = [[NSAutoreleasePool alloc] init]; + [(MAMEDebugView *)osdprivate update]; + [pool release]; +} + + +@implementation MAMEDebugView + ++ (void)initialize { + DefaultForeground = [[NSColor colorWithCalibratedWhite:0.0 alpha:1.0] retain]; + ChangedForeground = [[NSColor colorWithCalibratedRed:0.875 green:0.0 blue:0.0 alpha:1.0] retain]; + InvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:1.0 alpha:1.0] retain]; + CommentForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.375 blue:0.0 alpha:1.0] retain]; + DisabledChangedForeground = [[NSColor colorWithCalibratedRed:0.5 green:0.125 blue:0.125 alpha:1.0] retain]; + DisabledInvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.5 alpha:1.0] retain]; + DisabledCommentForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.25 blue:0.0 alpha:1.0] retain]; + + DefaultBackground = [[NSColor colorWithCalibratedWhite:1.0 alpha:1.0] retain]; + VisitedBackground = [[NSColor colorWithCalibratedRed:0.75 green:1.0 blue:0.75 alpha:1.0] retain]; + AncillaryBackground = [[NSColor colorWithCalibratedWhite:0.75 alpha:1.0] retain]; + SelectedBackground = [[NSColor colorWithCalibratedRed:0.75 green:0.875 blue:1.0 alpha:1.0] retain]; + CurrentBackground = [[NSColor colorWithCalibratedRed:1.0 green:0.75 blue:0.75 alpha:1.0] retain]; + SelectedCurrentBackground = [[NSColor colorWithCalibratedRed:0.875 green:0.625 blue:0.875 alpha:1.0] retain]; + InactiveSelectedBackground = [[NSColor colorWithCalibratedWhite:0.875 alpha:1.0] retain]; + InactiveSelectedCurrentBackground = [[NSColor colorWithCalibratedRed:0.875 green:0.5 blue:0.625 alpha:1.0] retain]; + + NonWhiteCharacters = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet]; +} + + +- (NSColor *)foregroundForAttribute:(UINT8)attrib { + if (attrib & DCA_COMMENT) + return (attrib & DCA_DISABLED) ? DisabledCommentForeground : CommentForeground; + else if (attrib & DCA_INVALID) + return (attrib & DCA_DISABLED) ? DisabledInvalidForeground : InvalidForeground; + else if (attrib & DCA_CHANGED) + return (attrib & DCA_DISABLED) ? DisabledChangedForeground : ChangedForeground; + else + return DefaultForeground; +} + + +- (NSColor *)backgroundForAttribute:(UINT8)attrib { + BOOL const active = [[self window] isKeyWindow] && ([[self window] firstResponder] == self); + if ((attrib & DCA_SELECTED) && (attrib & DCA_CURRENT)) + return active ? SelectedCurrentBackground : InactiveSelectedCurrentBackground; + else if (attrib & DCA_CURRENT) + return CurrentBackground; + else if (attrib & DCA_SELECTED) + return active ? SelectedBackground : InactiveSelectedBackground; + else if (attrib & DCA_ANCILLARY) + return AncillaryBackground; + else if (attrib & DCA_VISITED) + return VisitedBackground; + else + return DefaultBackground; +} + + +- (debug_view_xy)convertLocation:(NSPoint)location { + debug_view_xy position; + + position.y = lround(floor(location.y / fontHeight)); + if (position.y < 0) + position.y = 0; + else if (position.y >= totalHeight) + position.y = totalHeight - 1; + + debug_view_xy const origin = view->visible_position(); + debug_view_xy const size = view->visible_size(); + debug_view_char const *data = view->viewdata(); + if (!data || (position.y < origin.y) || (position.y >= origin.y + size.y)) + { + // y coordinate outside visible area, x will be a guess + position.x = lround(floor((location.x - [textContainer lineFragmentPadding]) / fontWidth)); + } + else + { + data += ((position.y - view->visible_position().y) * view->visible_size().x); + int attr = -1; + NSUInteger start = 0, length = 0; + for (UINT32 col = origin.x; col < origin.x + size.x; col++) + { + [[text mutableString] appendFormat:@"%c", data[col - origin.x].byte]; + if ((start < length) && (attr != data[col - origin.x].attrib)) + { + NSRange const run = NSMakeRange(start, length - start); + [text addAttribute:NSFontAttributeName + value:font + range:NSMakeRange(0, length)]; + [text addAttribute:NSForegroundColorAttributeName + value:[self foregroundForAttribute:attr] + range:run]; + start = length; + } + attr = data[col - origin.x].attrib; + length = [text length]; + } + if (start < length) + { + NSRange const run = NSMakeRange(start, length - start); + [text addAttribute:NSFontAttributeName + value:font + range:NSMakeRange(0, length)]; + [text addAttribute:NSForegroundColorAttributeName + value:[self foregroundForAttribute:attr] + range:run]; + } + CGFloat fraction; + NSUInteger const glyph = [layoutManager glyphIndexForPoint:NSMakePoint(location.x, fontHeight / 2) + inTextContainer:textContainer + fractionOfDistanceThroughGlyph:&fraction]; + position.x = [layoutManager characterIndexForGlyphAtIndex:glyph]; // FIXME: assumes 1:1 character mapping + [text deleteCharactersInRange:NSMakeRange(0, length)]; + } + if (position.x < 0) + position.x = 0; + else if (position.x >= totalWidth) + position.x = totalWidth - 1; + + return position; +} + + +- (void)convertBounds:(NSRect)b toFirstAffectedLine:(INT32 *)f count:(INT32 *)c { + *f = lround(floor(b.origin.y / fontHeight)); + *c = lround(ceil((b.origin.y + b.size.height) / fontHeight)) - *f; +} + + +- (void)recomputeVisible { + // this gets all the lines that are at least partially visible + debug_view_xy origin(0, 0), size(totalWidth, totalHeight); + [self convertBounds:[self visibleRect] toFirstAffectedLine:&origin.y count:&size.y]; + size.y = MIN(size.y, totalHeight - origin.y); + + // tell the underlying view how much real estate is available + view->set_visible_size(size); + view->set_visible_position(origin); + originTop = origin.y; +} + + +- (void)typeCharacterAndScrollToCursor:(char)ch { + debug_view_xy const oldPos = view->cursor_position(); + view->process_char(ch); + if (view->cursor_supported() && view->cursor_visible()) + { + debug_view_xy const newPos = view->cursor_position(); + if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y)) + { + // FIXME - use proper font metrics + [self scrollRectToVisible:NSMakeRect((newPos.x * fontWidth) + [textContainer lineFragmentPadding], + newPos.y * fontHeight, + fontWidth, + fontHeight)]; + } + } +} + + +- (void)adjustSizeAndRecomputeVisible { + NSSize const clip = [[[self enclosingScrollView] contentView] bounds].size; + NSSize content = NSMakeSize((fontWidth * totalWidth) + (2 * [textContainer lineFragmentPadding]), + fontHeight * totalHeight); + if (wholeLineScroll) + content.height += (fontHeight * 2) - 1; + [self setFrameSize:NSMakeSize(ceil(MAX(clip.width, content.width)), + ceil(MAX(clip.height, content.height)))]; + [self recomputeVisible]; +} + + ++ (NSFont *)defaultFontForMachine:(running_machine &)m { + osd_options const &options = downcast<osd_options const &>(m.options()); + char const *const face = options.debugger_font(); + float const size = options.debugger_font_size(); + + NSFont *const result = (('\0' != *face) && (0 != strcmp(OSDOPTVAL_AUTO, face))) + ? [NSFont fontWithName:[NSString stringWithUTF8String:face] size:MAX(0, size)] + : nil; + + return (nil != result) ? result : [NSFont userFixedPitchFontOfSize:MAX(0, size)]; +} + + +- (id)initWithFrame:(NSRect)f type:(debug_view_type)t machine:(running_machine &)m wholeLineScroll:(BOOL)w { + if (!(self = [super initWithFrame:f])) + return nil; + type = t; + machine = &m; + view = machine->debug_view().alloc_view((debug_view_type)type, debugwin_view_update, self); + if (view == nil) { + [self release]; + return nil; + } + wholeLineScroll = w; + debug_view_xy const size = view->total_size(); + totalWidth = size.x; + totalHeight = size.y; + originTop = 0; + + text = [[NSTextStorage alloc] init]; + textContainer = [[NSTextContainer alloc] init]; + layoutManager = [[NSLayoutManager alloc] init]; + [layoutManager addTextContainer:textContainer]; + [textContainer release]; + [text addLayoutManager:layoutManager]; + [layoutManager release]; + + [self setFont:[[self class] defaultFontForMachine:m]]; + + NSMenu *contextMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Context"]; + [self addContextMenuItemsToMenu:contextMenu]; + [self setMenu:contextMenu]; + [contextMenu release]; + + return self; +} + + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + if (view != NULL) machine->debug_view().free_view(*view); + if (font != nil) [font release]; + if (text != nil) [text release]; + [super dealloc]; +} + + +- (void)update { + // resize our frame if the total size has changed + debug_view_xy const newSize = view->total_size(); + BOOL const resized = (newSize.x != totalWidth) || (newSize.y != totalHeight); + if (resized) + { + NSScrollView *const scroller = [self enclosingScrollView]; + if (scroller) + { + NSSize const clip = [[scroller contentView] bounds].size; + NSSize content = NSMakeSize((fontWidth * newSize.x) + (2 * [textContainer lineFragmentPadding]), + fontHeight * newSize.y); + if (wholeLineScroll) + content.height += (fontHeight * 2) - 1; + [self setFrameSize:NSMakeSize(ceil(MAX(clip.width, content.width)), + ceil(MAX(clip.height, content.height)))]; + } + totalWidth = newSize.x; + totalHeight = newSize.y; + } + + // scroll the view if we're being told to + debug_view_xy const newOrigin = view->visible_position(); + if (newOrigin.y != originTop) + { + NSRect const visible = [self visibleRect]; + NSPoint scroll = NSMakePoint(visible.origin.x, newOrigin.y * fontHeight); + [self scrollPoint:scroll]; + originTop = newOrigin.y; + } + + // mark as dirty + [self setNeedsDisplay:YES]; +} + + +- (NSSize)maximumFrameSize { + debug_view_xy const max = view->total_size(); + return NSMakeSize(ceil((max.x * fontWidth) + (2 * [textContainer lineFragmentPadding])), + ceil((max.y + (wholeLineScroll ? 1 : 0)) * fontHeight)); +} + + +- (NSFont *)font { + return [[font retain] autorelease]; +} + + +- (void)setFont:(NSFont *)f { + [font autorelease]; + font = [f retain]; + fontWidth = [font maximumAdvancement].width; + fontHeight = ceil([font ascender] - [font descender]); + fontAscent = [font ascender]; + [[self enclosingScrollView] setLineScroll:fontHeight]; + totalWidth = totalHeight = 0; + [self update]; +} + + +- (BOOL)cursorSupported { + return view->cursor_supported(); +} + + +- (BOOL)cursorVisible { + return view->cursor_visible(); +} + + +- (debug_view_xy)cursorPosition { + return view->cursor_position(); +} + + +- (IBAction)copyVisible:(id)sender { + debug_view_xy const size = view->visible_size(); + debug_view_char const *data = view->viewdata(); + if (!data) + { + NSBeep(); + return; + } + + for (UINT32 row = 0; row < size.y; row++, data += size.x) + { + // add content for the line and set colours + int attr = -1; + NSUInteger start = [text length], length = start; + for (UINT32 col = 0; col < size.x; col++) + { + [[text mutableString] appendFormat:@"%c", data[col].byte]; + if ((start < length) && (attr != (data[col].attrib & ~DCA_SELECTED))) + { + NSRange const run = NSMakeRange(start, length - start); + [text addAttribute:NSForegroundColorAttributeName + value:[self foregroundForAttribute:attr] + range:run]; + [text addAttribute:NSBackgroundColorAttributeName + value:[self backgroundForAttribute:attr] + range:run]; + start = length; + } + attr = data[col].attrib & ~DCA_SELECTED; + length = [text length]; + } + + // clean up trailing whitespace + NSRange trim = [[text string] rangeOfCharacterFromSet:NonWhiteCharacters + options:NSBackwardsSearch + range:NSMakeRange(start, length - start)]; + if (trim.location != NSNotFound) + trim = [[text string] rangeOfComposedCharacterSequenceAtIndex:(trim.location + trim.length - 1)]; + else if (start > 0) + trim = [[text string] rangeOfComposedCharacterSequenceAtIndex:(start - 1)]; + else + trim = NSMakeRange(start, 0); + trim.location += trim.length; + trim.length = length - trim.location; + [text deleteCharactersInRange:trim]; + + // add the line ending and set colours + [[text mutableString] appendString:@"\n"]; + NSRange const run = NSMakeRange(start, [text length] - start); + [text addAttribute:NSForegroundColorAttributeName + value:[self foregroundForAttribute:attr] + range:run]; + [text addAttribute:NSBackgroundColorAttributeName + value:[self backgroundForAttribute:attr] + range:run]; + } + + // set the font and send it to the pasteboard + NSRange const run = NSMakeRange(0, [text length]); + [text addAttribute:NSFontAttributeName value:font range:run]; + NSPasteboard *const board = [NSPasteboard generalPasteboard]; + [board declareTypes:[NSArray arrayWithObject:NSRTFPboardType] owner:nil]; + [board setData:[text RTFFromRange:run documentAttributes:[NSDictionary dictionary]] forType:NSRTFPboardType]; + [text deleteCharactersInRange:run]; +} + + +- (IBAction)paste:(id)sender { + NSPasteboard *const board = [NSPasteboard generalPasteboard]; + NSString *const avail = [board availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; + if (avail == nil) + { + NSBeep(); + return; + } + + NSData *const data = [[board stringForType:avail] dataUsingEncoding:NSASCIIStringEncoding + allowLossyConversion:YES]; + char const *const bytes = (char const *)[data bytes]; + debug_view_xy const oldPos = view->cursor_position(); + for (NSUInteger i = 0, l = [data length]; i < l; i++) + view->process_char(bytes[i]); + if (view->cursor_supported() && view->cursor_visible()) + { + debug_view_xy const newPos = view->cursor_position(); + if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y)) + { + // FIXME - use proper font metrics + [self scrollRectToVisible:NSMakeRect((newPos.x * fontWidth) + [textContainer lineFragmentPadding], + newPos.y * fontHeight, + fontWidth, + fontHeight)]; + } + } +} + + +- (void)viewBoundsDidChange:(NSNotification *)notification { + NSView *const changed = [notification object]; + if (changed == [[self enclosingScrollView] contentView]) + [self adjustSizeAndRecomputeVisible]; +} + + +- (void)viewFrameDidChange:(NSNotification *)notification { + NSView *const changed = [notification object]; + if (changed == [[self enclosingScrollView] contentView]) + [self adjustSizeAndRecomputeVisible]; +} + + +- (void)windowDidBecomeKey:(NSNotification *)notification { + NSWindow *const win = [notification object]; + if ((win == [self window]) && ([win firstResponder] == self) && view->cursor_supported()) + [self setNeedsDisplay:YES]; +} + + +- (void)windowDidResignKey:(NSNotification *)notification { + NSWindow *const win = [notification object]; + if ((win == [self window]) && ([win firstResponder] == self) && view->cursor_supported()) + [self setNeedsDisplay:YES]; +} + + +- (void)addContextMenuItemsToMenu:(NSMenu *)menu { + NSMenuItem *item; + + item = [menu addItemWithTitle:@"Copy Visible" + action:@selector(copyVisible:) + keyEquivalent:@""]; + [item setTarget:self]; + + item = [menu addItemWithTitle:@"Paste" + action:@selector(paste:) + keyEquivalent:@""]; + [item setTarget:self]; +} + + +- (BOOL)acceptsFirstResponder { + return view->cursor_supported(); +} + + +- (BOOL)becomeFirstResponder { + if (view->cursor_supported()) + { + debug_view_xy pos; + view->set_cursor_visible(true); + pos = view->cursor_position(); + [self scrollRectToVisible:NSMakeRect((pos.x * fontWidth) + [textContainer lineFragmentPadding], + pos.y * fontHeight, + fontWidth, + fontHeight)]; // FIXME: metrics + [self setNeedsDisplay:YES]; + return [super becomeFirstResponder]; + } + else + { + return NO; + } +} + + +- (BOOL)resignFirstResponder { + if (view->cursor_supported()) + [self setNeedsDisplay:YES]; + return [super resignFirstResponder]; +} + + +- (void)viewDidMoveToSuperview { + [super viewDidMoveToSuperview]; + + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSViewBoundsDidChangeNotification + object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSViewFrameDidChangeNotification + object:nil]; + + NSScrollView *const scroller = [self enclosingScrollView]; + if (scroller != nil) + { + [scroller setLineScroll:fontHeight]; + [[scroller contentView] setPostsBoundsChangedNotifications:YES]; + [[scroller contentView] setPostsFrameChangedNotifications:YES]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(viewBoundsDidChange:) + name:NSViewBoundsDidChangeNotification + object:[scroller contentView]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(viewFrameDidChange:) + name:NSViewFrameDidChangeNotification + object:[scroller contentView]]; + [self adjustSizeAndRecomputeVisible]; + } +} + + +- (void)viewDidMoveToWindow { + [super viewDidMoveToWindow]; + + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSWindowDidBecomeKeyNotification + object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSWindowDidResignKeyNotification + object:nil]; + + if ([self window] != nil) + { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidBecomeKey:) + name:NSWindowDidBecomeKeyNotification + object:[self window]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidResignKey:) + name:NSWindowDidResignKeyNotification + object:[self window]]; + [self setNeedsDisplay:YES]; + } +} + + +- (BOOL)isFlipped { + return YES; +} + + +- (BOOL)isOpaque { + return YES; +} + + +- (NSRect)adjustScroll:(NSRect)proposedVisibleRect { + if (wholeLineScroll) + { + CGFloat const clamp = [self bounds].size.height - fontHeight - proposedVisibleRect.size.height; + proposedVisibleRect.origin.y = MIN(proposedVisibleRect.origin.y, MAX(clamp, 0)); + proposedVisibleRect.origin.y -= fmod(proposedVisibleRect.origin.y, fontHeight); + } + return proposedVisibleRect; +} + + +- (void)drawRect:(NSRect)dirtyRect { + // work out what's available + [self recomputeVisible]; + debug_view_xy const origin = view->visible_position(); + debug_view_xy const size = view->visible_size(); + + // work out how much we need to draw + INT32 row, clip; + [self convertBounds:dirtyRect toFirstAffectedLine:&row count:&clip]; + clip += row; + row = MAX(row, origin.y); + clip = MIN(clip, origin.y + size.y); + + // this gets the text for the whole visible area + debug_view_char const *data = view->viewdata(); + if (!data) + return; + + // clear any space above the available content + data += ((row - origin.y) * size.x); + if (dirtyRect.origin.y < (row * fontHeight)) + { + [DefaultBackground set]; + [NSBezierPath fillRect:NSMakeRect(0, + dirtyRect.origin.y, + [self bounds].size.width, + (row * fontHeight) - dirtyRect.origin.y)]; + } + + // render entire lines to get character alignment right + for ( ; row < clip; row++, data += size.x) + { + int attr = -1; + NSUInteger start = 0, length = 0; + for (UINT32 col = origin.x; col < origin.x + size.x; col++) + { + [[text mutableString] appendFormat:@"%c", data[col - origin.x].byte]; + if ((start < length) && (attr != data[col - origin.x].attrib)) + { + NSRange const run = NSMakeRange(start, length - start); + [text addAttribute:NSFontAttributeName + value:font + range:NSMakeRange(0, length)]; + [text addAttribute:NSForegroundColorAttributeName + value:[self foregroundForAttribute:attr] + range:run]; + NSRange const glyphs = [layoutManager glyphRangeForCharacterRange:run + actualCharacterRange:NULL]; + NSRect box = [layoutManager boundingRectForGlyphRange:glyphs + inTextContainer:textContainer]; + if (start == 0) + { + box.size.width += box.origin.x; + box.origin.x = 0; + } + [[self backgroundForAttribute:attr] set]; + [NSBezierPath fillRect:NSMakeRect(box.origin.x, + row * fontHeight, + box.size.width, + fontHeight)]; + start = length; + } + attr = data[col - origin.x].attrib; + length = [text length]; + } + NSRange const run = NSMakeRange(start, length - start); + [text addAttribute:NSFontAttributeName + value:font + range:NSMakeRange(0, length)]; + [text addAttribute:NSForegroundColorAttributeName + value:[self foregroundForAttribute:attr] + range:run]; + NSRange const glyphs = [layoutManager glyphRangeForCharacterRange:run + actualCharacterRange:NULL]; + NSRect box = [layoutManager boundingRectForGlyphRange:glyphs + inTextContainer:textContainer]; + if (start == 0) + box.origin.x = 0; + box.size.width = MAX([self bounds].size.width - box.origin.x, 0); + [[self backgroundForAttribute:attr] set]; + [NSBezierPath fillRect:NSMakeRect(box.origin.x, + row * fontHeight, + box.size.width, + fontHeight)]; + [layoutManager drawGlyphsForGlyphRange:[layoutManager glyphRangeForTextContainer:textContainer] + atPoint:NSMakePoint(0, row * fontHeight)]; + [text deleteCharactersInRange:NSMakeRange(0, length)]; + } + + // clear any space below the available content + if ((dirtyRect.origin.y + dirtyRect.size.height) > (row * fontHeight)) + { + [DefaultBackground set]; + [NSBezierPath fillRect:NSMakeRect(0, + row * fontHeight, + [self bounds].size.width, + (dirtyRect.origin.y + dirtyRect.size.height) - (row * fontHeight))]; + } +} + + +- (void)mouseDown:(NSEvent *)event { + NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; + NSUInteger const modifiers = [event modifierFlags]; + view->process_click(((modifiers & NSCommandKeyMask) && [[self window] isMainWindow]) ? DCK_RIGHT_CLICK + : (modifiers & NSAlternateKeyMask) ? DCK_MIDDLE_CLICK + : DCK_LEFT_CLICK, + [self convertLocation:location]); +} + + +- (void)mouseDragged:(NSEvent *)event { + [self autoscroll:event]; + NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; + NSUInteger const modifiers = [event modifierFlags]; + if (view->cursor_supported() + && !(modifiers & NSAlternateKeyMask) + && (!(modifiers & NSCommandKeyMask) || ![[self window] isMainWindow])) + { + view->set_cursor_position([self convertLocation:location]); + view->set_cursor_visible(true); + [self setNeedsDisplay:YES]; + } +} + + +- (void)rightMouseDown:(NSEvent *)event { + NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; + if (view->cursor_supported()) + { + view->set_cursor_position([self convertLocation:location]); + view->set_cursor_visible(true); + [self setNeedsDisplay:YES]; + } + [super rightMouseDown:event]; +} + + +- (void)keyDown:(NSEvent *)event { + NSUInteger modifiers = [event modifierFlags]; + NSString *str = [event charactersIgnoringModifiers]; + + if ([str length] == 1) + { + if (modifiers & NSNumericPadKeyMask) + { + switch ([str characterAtIndex:0]) + { + case NSUpArrowFunctionKey: + if (modifiers & NSCommandKeyMask) + view->process_char(DCH_CTRLHOME); + else + view->process_char(DCH_UP); + return; + case NSDownArrowFunctionKey: + if (modifiers & NSCommandKeyMask) + view->process_char(DCH_CTRLEND); + else + view->process_char(DCH_DOWN); + return; + case NSLeftArrowFunctionKey: + if (modifiers & NSCommandKeyMask) + [self typeCharacterAndScrollToCursor:DCH_HOME]; + else if (modifiers & NSAlternateKeyMask) + [self typeCharacterAndScrollToCursor:DCH_CTRLLEFT]; + else + [self typeCharacterAndScrollToCursor:DCH_LEFT]; + return; + case NSRightArrowFunctionKey: + if (modifiers & NSCommandKeyMask) + [self typeCharacterAndScrollToCursor:DCH_END]; + else if (modifiers & NSAlternateKeyMask) + [self typeCharacterAndScrollToCursor:DCH_CTRLRIGHT]; + else + [self typeCharacterAndScrollToCursor:DCH_RIGHT]; + return; + default: + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; + return; + } + } + else if (modifiers & NSFunctionKeyMask) + { + switch ([str characterAtIndex:0]) + { + case NSPageUpFunctionKey: + if (modifiers & NSAlternateKeyMask) + { + view->process_char(DCH_PUP); + return; + } + case NSPageDownFunctionKey: + if (modifiers & NSAlternateKeyMask) + { + view->process_char(DCH_PDOWN); + return; + } + default: + ; + } + [super keyDown:event]; + return; + } + } + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; +} + + +- (void)insertTab:(id)sender { + if ([[self window] firstResponder] == self) + [[self window] selectNextKeyView:self]; +} + + +- (void)insertBacktab:(id)sender { + if ([[self window] firstResponder] == self) + [[self window] selectPreviousKeyView:self]; +} + + +- (void)insertNewline:(id)sender { + debug_cpu_get_visible_cpu(*machine)->debug()->single_step(); +} + + +- (void)insertText:(id)string { + NSUInteger len; + NSRange found; + if ([string isKindOfClass:[NSAttributedString class]]) + string = [string string]; + for (len = [string length], found = NSMakeRange(0, 0); + found.location < len; + found.location += found.length) { + found = [string rangeOfComposedCharacterSequenceAtIndex:found.location]; + if (found.length == 1) { + unichar ch = [string characterAtIndex:found.location]; + if ((ch >= 32) && (ch < 127)) + [self typeCharacterAndScrollToCursor:ch]; + } + } +} + + +- (BOOL)validateMenuItem:(NSMenuItem *)item { + SEL action = [item action]; + + if (action == @selector(paste:)) + { + NSPasteboard *const board = [NSPasteboard generalPasteboard]; + return [board availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] != nil; + } + else + { + return YES; + } +} + +@end |