trunk/src/osd/modules/debugger/osx/debugconsole.m
| r250164 | r250165 | |
| 1 | | // license:BSD-3-Clause |
| 2 | | // copyright-holders:Vas Crabb |
| 3 | | //============================================================ |
| 4 | | // |
| 5 | | // debugconsole.m - MacOS X Cocoa debug window handling |
| 6 | | // |
| 7 | | //============================================================ |
| 8 | | |
| 9 | | #import "debugconsole.h" |
| 10 | | |
| 11 | | #import "debugcommandhistory.h" |
| 12 | | #import "consoleview.h" |
| 13 | | #import "debugview.h" |
| 14 | | #import "deviceinfoviewer.h" |
| 15 | | #import "devicesviewer.h" |
| 16 | | #import "disassemblyview.h" |
| 17 | | #import "disassemblyviewer.h" |
| 18 | | #import "errorlogviewer.h" |
| 19 | | #import "memoryviewer.h" |
| 20 | | #import "pointsviewer.h" |
| 21 | | #import "registersview.h" |
| 22 | | |
| 23 | | #include "debug/debugcon.h" |
| 24 | | #include "debug/debugcpu.h" |
| 25 | | |
| 26 | | |
| 27 | | @implementation MAMEDebugConsole |
| 28 | | |
| 29 | | - (id)initWithMachine:(running_machine &)m { |
| 30 | | NSSplitView *regSplit, *dasmSplit; |
| 31 | | NSScrollView *regScroll, *dasmScroll, *consoleScroll; |
| 32 | | NSView *consoleContainer; |
| 33 | | NSPopUpButton *actionButton; |
| 34 | | NSRect rct; |
| 35 | | |
| 36 | | // initialise superclass |
| 37 | | if (!(self = [super initWithMachine:m title:@"Debug"])) |
| 38 | | return nil; |
| 39 | | history = [[MAMEDebugCommandHistory alloc] init]; |
| 40 | | auxiliaryWindows = [[NSMutableArray alloc] init]; |
| 41 | | NSFont *const defaultFont = [[MAMEDebugView class] defaultFontForMachine:m]; |
| 42 | | |
| 43 | | // create the register view |
| 44 | | regView = [[MAMERegistersView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; |
| 45 | | regScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 46 | | [regScroll setHasHorizontalScroller:YES]; |
| 47 | | [regScroll setHasVerticalScroller:YES]; |
| 48 | | [regScroll setAutohidesScrollers:YES]; |
| 49 | | [regScroll setBorderType:NSBezelBorder]; |
| 50 | | [regScroll setDocumentView:regView]; |
| 51 | | [regView release]; |
| 52 | | |
| 53 | | // create the disassembly view |
| 54 | | dasmView = [[MAMEDisassemblyView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; |
| 55 | | [dasmView setExpression:@"curpc"]; |
| 56 | | dasmScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 57 | | [dasmScroll setHasHorizontalScroller:YES]; |
| 58 | | [dasmScroll setHasVerticalScroller:YES]; |
| 59 | | [dasmScroll setAutohidesScrollers:YES]; |
| 60 | | [dasmScroll setBorderType:NSBezelBorder]; |
| 61 | | [dasmScroll setDocumentView:dasmView]; |
| 62 | | [dasmView release]; |
| 63 | | |
| 64 | | // create the console view |
| 65 | | consoleView = [[MAMEConsoleView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; |
| 66 | | consoleScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 67 | | [consoleScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 68 | | [consoleScroll setHasHorizontalScroller:YES]; |
| 69 | | [consoleScroll setHasVerticalScroller:YES]; |
| 70 | | [consoleScroll setAutohidesScrollers:YES]; |
| 71 | | [consoleScroll setBorderType:NSBezelBorder]; |
| 72 | | [consoleScroll setDocumentView:consoleView]; |
| 73 | | [consoleView release]; |
| 74 | | |
| 75 | | // create the command field |
| 76 | | commandField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; |
| 77 | | [commandField setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)]; |
| 78 | | [commandField setFont:defaultFont]; |
| 79 | | [commandField setFocusRingType:NSFocusRingTypeNone]; |
| 80 | | [commandField setTarget:self]; |
| 81 | | [commandField setAction:@selector(doCommand:)]; |
| 82 | | [commandField setDelegate:self]; |
| 83 | | [commandField sizeToFit]; |
| 84 | | rct = [commandField frame]; |
| 85 | | [commandField setFrame:NSMakeRect(rct.size.height, 0, 100 - rct.size.height, rct.size.height)]; |
| 86 | | |
| 87 | | // create the action pull-down button |
| 88 | | actionButton = [[self class] newActionButtonWithFrame:NSMakeRect(0, 0, rct.size.height, rct.size.height)]; |
| 89 | | [actionButton setAutoresizingMask:(NSViewMaxXMargin | NSViewMaxYMargin)]; |
| 90 | | [actionButton setFont:[NSFont systemFontOfSize:[defaultFont pointSize]]]; |
| 91 | | [dasmView insertActionItemsInMenu:[actionButton menu] atIndex:1]; |
| 92 | | |
| 93 | | // create the container for the console and command input field |
| 94 | | consoleContainer = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 95 | | [consoleScroll setFrame:NSMakeRect(0, |
| 96 | | rct.size.height, |
| 97 | | 100, |
| 98 | | [consoleContainer bounds].size.height - rct.size.height)]; |
| 99 | | [consoleContainer addSubview:consoleScroll]; |
| 100 | | [consoleContainer addSubview:commandField]; |
| 101 | | [consoleContainer addSubview:actionButton]; |
| 102 | | [consoleScroll release]; |
| 103 | | [commandField release]; |
| 104 | | [actionButton release]; |
| 105 | | |
| 106 | | // create the split between the disassembly and the console |
| 107 | | dasmSplit = [[NSSplitView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 108 | | [dasmSplit setDelegate:self]; |
| 109 | | [dasmSplit setVertical:NO]; |
| 110 | | [dasmSplit addSubview:dasmScroll]; |
| 111 | | [dasmSplit addSubview:consoleContainer]; |
| 112 | | [dasmScroll release]; |
| 113 | | [consoleContainer release]; |
| 114 | | |
| 115 | | // create the split between the registers and the console |
| 116 | | regSplit = [[NSSplitView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 117 | | [regSplit setDelegate:self]; |
| 118 | | [regSplit setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 119 | | [regSplit setVertical:YES]; |
| 120 | | [regSplit addSubview:regScroll]; |
| 121 | | [regSplit addSubview:dasmSplit]; |
| 122 | | [regScroll release]; |
| 123 | | [dasmSplit release]; |
| 124 | | |
| 125 | | // put the split views in the window and get them into a half-reasonable state |
| 126 | | [window setContentView:regSplit]; |
| 127 | | [regSplit release]; |
| 128 | | [regSplit adjustSubviews]; |
| 129 | | [dasmSplit adjustSubviews]; |
| 130 | | |
| 131 | | // keyboard focus should start on the command field |
| 132 | | [window makeFirstResponder:commandField]; |
| 133 | | |
| 134 | | // calculate the optimal size for everything |
| 135 | | NSRect const available = [[NSScreen mainScreen] visibleFrame]; |
| 136 | | NSSize const regCurrent = [regScroll frame].size; |
| 137 | | NSSize const regSize = [NSScrollView frameSizeForContentSize:[regView maximumFrameSize] |
| 138 | | hasHorizontalScroller:YES |
| 139 | | hasVerticalScroller:YES |
| 140 | | borderType:[regScroll borderType]]; |
| 141 | | NSSize const dasmCurrent = [dasmScroll frame].size; |
| 142 | | NSSize const dasmSize = [NSScrollView frameSizeForContentSize:[dasmView maximumFrameSize] |
| 143 | | hasHorizontalScroller:YES |
| 144 | | hasVerticalScroller:YES |
| 145 | | borderType:[dasmScroll borderType]]; |
| 146 | | NSSize const consoleCurrent = [consoleContainer frame].size; |
| 147 | | NSSize consoleSize = [NSScrollView frameSizeForContentSize:[consoleView maximumFrameSize] |
| 148 | | hasHorizontalScroller:YES |
| 149 | | hasVerticalScroller:YES |
| 150 | | borderType:[consoleScroll borderType]]; |
| 151 | | NSRect windowFrame = [window frame]; |
| 152 | | NSSize adjustment; |
| 153 | | |
| 154 | | consoleSize.width += consoleCurrent.width - [consoleScroll frame].size.width; |
| 155 | | consoleSize.height += consoleCurrent.height - [consoleScroll frame].size.height; |
| 156 | | adjustment.width = regSize.width - regCurrent.width; |
| 157 | | adjustment.height = regSize.height - regCurrent.height; |
| 158 | | adjustment.width += MAX(dasmSize.width - dasmCurrent.width, consoleSize.width - consoleCurrent.width); |
| 159 | | |
| 160 | | windowFrame.size.width += adjustment.width; |
| 161 | | windowFrame.size.height += adjustment.height; // not used - better to go for fixed height |
| 162 | | windowFrame.size.height = MIN(512.0, available.size.height); |
| 163 | | windowFrame.size.width = MIN(windowFrame.size.width, available.size.width); |
| 164 | | windowFrame.origin.x = available.origin.x + available.size.width - windowFrame.size.width; |
| 165 | | windowFrame.origin.y = available.origin.y; |
| 166 | | [window setFrame:windowFrame display:YES]; |
| 167 | | |
| 168 | | NSRect lhsFrame = [regScroll frame]; |
| 169 | | NSRect rhsFrame = [dasmSplit frame]; |
| 170 | | adjustment.width = MIN(regSize.width, ([regSplit frame].size.width - [regSplit dividerThickness]) / 2); |
| 171 | | rhsFrame.origin.x -= lhsFrame.size.width - adjustment.width; |
| 172 | | rhsFrame.size.width += lhsFrame.size.width - adjustment.width; |
| 173 | | lhsFrame.size.width = adjustment.width; |
| 174 | | [regScroll setFrame:lhsFrame]; |
| 175 | | [dasmSplit setFrame:rhsFrame]; |
| 176 | | |
| 177 | | // select the current processor |
| 178 | | [self setCPU:machine->firstcpu]; |
| 179 | | |
| 180 | | [[NSNotificationCenter defaultCenter] addObserver:self |
| 181 | | selector:@selector(auxiliaryWindowWillClose:) |
| 182 | | name:MAMEAuxiliaryDebugWindowWillCloseNotification |
| 183 | | object:nil]; |
| 184 | | |
| 185 | | // don't forget the return value |
| 186 | | return self; |
| 187 | | } |
| 188 | | |
| 189 | | |
| 190 | | - (void)dealloc { |
| 191 | | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 192 | | |
| 193 | | if (history != nil) |
| 194 | | [history release]; |
| 195 | | if (auxiliaryWindows != nil) |
| 196 | | [auxiliaryWindows release]; |
| 197 | | |
| 198 | | [super dealloc]; |
| 199 | | } |
| 200 | | |
| 201 | | |
| 202 | | - (void)setCPU:(device_t *)device { |
| 203 | | [regView selectSubviewForDevice:device]; |
| 204 | | [dasmView selectSubviewForDevice:device]; |
| 205 | | [window setTitle:[NSString stringWithFormat:@"Debug: %s - %s '%s'", |
| 206 | | device->machine().system().name, |
| 207 | | device->name(), |
| 208 | | device->tag()]]; |
| 209 | | } |
| 210 | | |
| 211 | | |
| 212 | | - (IBAction)doCommand:(id)sender { |
| 213 | | NSString *command = [sender stringValue]; |
| 214 | | if ([command length] == 0) |
| 215 | | { |
| 216 | | debug_cpu_get_visible_cpu(*machine)->debug()->single_step(); |
| 217 | | [history reset]; |
| 218 | | } |
| 219 | | else |
| 220 | | { |
| 221 | | debug_console_execute_command(*machine, [command UTF8String], 1); |
| 222 | | [history add:command]; |
| 223 | | [history edit]; |
| 224 | | } |
| 225 | | [sender setStringValue:@""]; |
| 226 | | } |
| 227 | | |
| 228 | | |
| 229 | | - (IBAction)debugToggleBreakpoint:(id)sender { |
| 230 | | device_t &device = [dasmView source]->device(); |
| 231 | | if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) |
| 232 | | { |
| 233 | | offs_t const address = [dasmView selectedAddress]; |
| 234 | | device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:address |
| 235 | | forDevice:device]; |
| 236 | | |
| 237 | | // if it doesn't exist, add a new one |
| 238 | | NSString *command; |
| 239 | | if (bp == NULL) |
| 240 | | command = [NSString stringWithFormat:@"bpset 0x%lX", (unsigned long)address]; |
| 241 | | else |
| 242 | | command = [NSString stringWithFormat:@"bpclear 0x%X", (unsigned)bp->index()]; |
| 243 | | debug_console_execute_command(*machine, [command UTF8String], 1); |
| 244 | | } |
| 245 | | } |
| 246 | | |
| 247 | | |
| 248 | | - (IBAction)debugToggleBreakpointEnable:(id)sender { |
| 249 | | device_t &device = [dasmView source]->device(); |
| 250 | | if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) |
| 251 | | { |
| 252 | | device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:[dasmView selectedAddress] |
| 253 | | forDevice:device]; |
| 254 | | if (bp != NULL) |
| 255 | | { |
| 256 | | NSString *command; |
| 257 | | if (bp->enabled()) |
| 258 | | command = [NSString stringWithFormat:@"bpdisable 0x%X", (unsigned)bp->index()]; |
| 259 | | else |
| 260 | | command = [NSString stringWithFormat:@"bpenable 0x%X", (unsigned)bp->index()]; |
| 261 | | debug_console_execute_command(*machine, [command UTF8String], 1); |
| 262 | | } |
| 263 | | } |
| 264 | | } |
| 265 | | |
| 266 | | |
| 267 | | - (IBAction)debugRunToCursor:(id)sender { |
| 268 | | device_t &device = [dasmView source]->device(); |
| 269 | | if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) |
| 270 | | { |
| 271 | | NSString *command = [NSString stringWithFormat:@"go 0x%lX", (unsigned long)[dasmView selectedAddress]]; |
| 272 | | debug_console_execute_command(*machine, [command UTF8String], 1); |
| 273 | | } |
| 274 | | } |
| 275 | | |
| 276 | | |
| 277 | | - (IBAction)debugNewMemoryWindow:(id)sender { |
| 278 | | MAMEMemoryViewer *win = [[MAMEMemoryViewer alloc] initWithMachine:*machine console:self]; |
| 279 | | [auxiliaryWindows addObject:win]; |
| 280 | | [win release]; |
| 281 | | [win activate]; |
| 282 | | } |
| 283 | | |
| 284 | | |
| 285 | | - (IBAction)debugNewDisassemblyWindow:(id)sender { |
| 286 | | MAMEDisassemblyViewer *win = [[MAMEDisassemblyViewer alloc] initWithMachine:*machine console:self]; |
| 287 | | [auxiliaryWindows addObject:win]; |
| 288 | | [win release]; |
| 289 | | [win activate]; |
| 290 | | } |
| 291 | | |
| 292 | | |
| 293 | | - (IBAction)debugNewErrorLogWindow:(id)sender { |
| 294 | | MAMEErrorLogViewer *win = [[MAMEErrorLogViewer alloc] initWithMachine:*machine console:self]; |
| 295 | | [auxiliaryWindows addObject:win]; |
| 296 | | [win release]; |
| 297 | | [win activate]; |
| 298 | | } |
| 299 | | |
| 300 | | |
| 301 | | - (IBAction)debugNewPointsWindow:(id)sender{ |
| 302 | | MAMEPointsViewer *win = [[MAMEPointsViewer alloc] initWithMachine:*machine console:self]; |
| 303 | | [auxiliaryWindows addObject:win]; |
| 304 | | [win release]; |
| 305 | | [win activate]; |
| 306 | | } |
| 307 | | |
| 308 | | |
| 309 | | - (IBAction)debugNewDevicesWindow:(id)sender { |
| 310 | | MAMEDevicesViewer *win = [[MAMEDevicesViewer alloc] initWithMachine:*machine console:self]; |
| 311 | | [auxiliaryWindows addObject:win]; |
| 312 | | [win release]; |
| 313 | | [win activate]; |
| 314 | | } |
| 315 | | |
| 316 | | |
| 317 | | - (void)debugNewMemoryWindowForSpace:(address_space *)space device:(device_t *)device expression:(NSString *)expression { |
| 318 | | MAMEMemoryViewer *win = [[MAMEMemoryViewer alloc] initWithMachine:*machine console:self]; |
| 319 | | [auxiliaryWindows addObject:win]; |
| 320 | | [win release]; |
| 321 | | if ([win selectSubviewForSpace:space]) |
| 322 | | { |
| 323 | | if (expression != nil) |
| 324 | | [win setExpression:expression]; |
| 325 | | } |
| 326 | | else |
| 327 | | { |
| 328 | | [win selectSubviewForDevice:device]; |
| 329 | | } |
| 330 | | [win activate]; |
| 331 | | } |
| 332 | | |
| 333 | | |
| 334 | | - (void)debugNewDisassemblyWindowForSpace:(address_space *)space device:(device_t *)device expression:(NSString *)expression { |
| 335 | | MAMEDisassemblyViewer *win = [[MAMEDisassemblyViewer alloc] initWithMachine:*machine console:self]; |
| 336 | | [auxiliaryWindows addObject:win]; |
| 337 | | [win release]; |
| 338 | | if ([win selectSubviewForSpace:space]) |
| 339 | | { |
| 340 | | if (expression != nil) |
| 341 | | [win setExpression:expression]; |
| 342 | | } |
| 343 | | else |
| 344 | | { |
| 345 | | [win selectSubviewForDevice:device]; |
| 346 | | } |
| 347 | | [win activate]; |
| 348 | | } |
| 349 | | |
| 350 | | |
| 351 | | - (void)debugNewInfoWindowForDevice:(device_t &)device { |
| 352 | | MAMEDeviceInfoViewer *win = [[MAMEDeviceInfoViewer alloc] initWithDevice:device |
| 353 | | machine:*machine |
| 354 | | console:self]; |
| 355 | | [auxiliaryWindows addObject:win]; |
| 356 | | [win release]; |
| 357 | | [win activate]; |
| 358 | | } |
| 359 | | |
| 360 | | |
| 361 | | - (void)showDebugger:(NSNotification *)notification { |
| 362 | | device_t *device = (device_t * )[[[notification userInfo] objectForKey:@"MAMEDebugDevice"] pointerValue]; |
| 363 | | if (&device->machine() == machine) |
| 364 | | { |
| 365 | | [self setCPU:device]; |
| 366 | | [window makeKeyAndOrderFront:self]; |
| 367 | | } |
| 368 | | } |
| 369 | | |
| 370 | | |
| 371 | | - (void)auxiliaryWindowWillClose:(NSNotification *)notification { |
| 372 | | [auxiliaryWindows removeObjectIdenticalTo:[notification object]]; |
| 373 | | } |
| 374 | | |
| 375 | | |
| 376 | | - (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor { |
| 377 | | if (control == commandField) |
| 378 | | [history edit]; |
| 379 | | |
| 380 | | return YES; |
| 381 | | } |
| 382 | | |
| 383 | | |
| 384 | | - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command { |
| 385 | | if (control == commandField) { |
| 386 | | if (command == @selector(cancelOperation:)) { |
| 387 | | [commandField setStringValue:@""]; |
| 388 | | [history reset]; |
| 389 | | return YES; |
| 390 | | } else if (command == @selector(moveUp:)) { |
| 391 | | NSString *hist = [history previous:[commandField stringValue]]; |
| 392 | | if (hist != nil) { |
| 393 | | [commandField setStringValue:hist]; |
| 394 | | [commandField selectText:self]; |
| 395 | | [(NSText *)[window firstResponder] setSelectedRange:NSMakeRange([hist length], 0)]; |
| 396 | | } |
| 397 | | return YES; |
| 398 | | } else if (command == @selector(moveDown:)) { |
| 399 | | NSString *hist = [history next:[commandField stringValue]]; |
| 400 | | if (hist != nil) { |
| 401 | | [commandField setStringValue:hist]; |
| 402 | | [commandField selectText:self]; |
| 403 | | [(NSText *)[window firstResponder] setSelectedRange:NSMakeRange([hist length], 0)]; |
| 404 | | } |
| 405 | | return YES; |
| 406 | | } |
| 407 | | } |
| 408 | | return NO; |
| 409 | | } |
| 410 | | |
| 411 | | |
| 412 | | - (void)windowWillClose:(NSNotification *)notification { |
| 413 | | if ([notification object] == window) |
| 414 | | { |
| 415 | | NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithPointer:machine], |
| 416 | | @"MAMEDebugMachine", |
| 417 | | nil]; |
| 418 | | [[NSNotificationCenter defaultCenter] postNotificationName:MAMEHideDebuggerNotification |
| 419 | | object:self |
| 420 | | userInfo:info]; |
| 421 | | debug_cpu_get_visible_cpu(*machine)->debug()->go(); |
| 422 | | } |
| 423 | | } |
| 424 | | |
| 425 | | |
| 426 | | - (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)min ofSubviewAt:(NSInteger)offs { |
| 427 | | return (min < 100) ? 100 : min; |
| 428 | | } |
| 429 | | |
| 430 | | |
| 431 | | - (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)max ofSubviewAt:(NSInteger)offs { |
| 432 | | NSSize sz = [sender bounds].size; |
| 433 | | CGFloat allowed = ([sender isVertical] ? sz.width : sz.height) - 100 - [sender dividerThickness]; |
| 434 | | return (max > allowed) ? allowed : max; |
| 435 | | } |
| 436 | | |
| 437 | | |
| 438 | | - (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview { |
| 439 | | // allow registers or disassembly to be collapsed, but not console |
| 440 | | return [[sender subviews] indexOfObjectIdenticalTo:subview] == 0; |
| 441 | | } |
| 442 | | |
| 443 | | |
| 444 | | - (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize { |
| 445 | | // This can only deal with a single split, but that's all we use, anyway |
| 446 | | NSRect first, second; |
| 447 | | [sender adjustSubviews]; |
| 448 | | first = [[[sender subviews] objectAtIndex:0] frame]; |
| 449 | | second = [[[sender subviews] objectAtIndex:1] frame]; |
| 450 | | if ([sender isVertical]) { |
| 451 | | if (first.size.width < 100) { |
| 452 | | CGFloat diff = 100 - first.size.width; |
| 453 | | first.size.width = 100; |
| 454 | | second.origin.x += diff; |
| 455 | | second.size.width -= diff; |
| 456 | | } else if (second.size.width < 100) { |
| 457 | | CGFloat diff = 100 - second.size.width; |
| 458 | | second.size.width = 100; |
| 459 | | second.origin.x -= diff; |
| 460 | | first.size.width -= diff; |
| 461 | | } |
| 462 | | } else { |
| 463 | | if (first.size.height < 100) { |
| 464 | | CGFloat diff = 100 - first.size.height; |
| 465 | | first.size.height = 100; |
| 466 | | second.origin.y += diff; |
| 467 | | second.size.height -= diff; |
| 468 | | } else if (second.size.height < 100) { |
| 469 | | CGFloat diff = 100 - second.size.height; |
| 470 | | second.size.height = 100; |
| 471 | | second.origin.y -= diff; |
| 472 | | first.size.height -= diff; |
| 473 | | } |
| 474 | | } |
| 475 | | [[[sender subviews] objectAtIndex:0] setFrame:first]; |
| 476 | | [[[sender subviews] objectAtIndex:1] setFrame:second]; |
| 477 | | } |
| 478 | | |
| 479 | | |
| 480 | | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 481 | | SEL const action = [item action]; |
| 482 | | BOOL const inContextMenu = ([item menu] == [dasmView menu]); |
| 483 | | BOOL const haveCursor = [dasmView cursorVisible]; |
| 484 | | BOOL const isCurrent = (debug_cpu_get_visible_cpu(*machine) == &[dasmView source]->device()); |
| 485 | | |
| 486 | | device_debug::breakpoint *breakpoint = NULL; |
| 487 | | if (haveCursor) |
| 488 | | { |
| 489 | | breakpoint = [[self class] findBreakpointAtAddress:[dasmView selectedAddress] |
| 490 | | forDevice:[dasmView source]->device()]; |
| 491 | | } |
| 492 | | |
| 493 | | if (action == @selector(debugToggleBreakpoint:)) |
| 494 | | { |
| 495 | | if (haveCursor) |
| 496 | | { |
| 497 | | if (breakpoint != NULL) |
| 498 | | { |
| 499 | | if (inContextMenu) |
| 500 | | [item setTitle:@"Clear Breakpoint"]; |
| 501 | | else |
| 502 | | [item setTitle:@"Clear Breakpoint at Cursor"]; |
| 503 | | } |
| 504 | | else |
| 505 | | { |
| 506 | | if (inContextMenu) |
| 507 | | [item setTitle:@"Set Breakpoint"]; |
| 508 | | else |
| 509 | | [item setTitle:@"Set Breakpoint at Cursor"]; |
| 510 | | } |
| 511 | | } |
| 512 | | else |
| 513 | | { |
| 514 | | if (inContextMenu) |
| 515 | | [item setTitle:@"Toggle Breakpoint"]; |
| 516 | | else |
| 517 | | [item setTitle:@"Toggle Breakpoint at Cursor"]; |
| 518 | | } |
| 519 | | return haveCursor && isCurrent; |
| 520 | | } |
| 521 | | else if (action == @selector(debugToggleBreakpointEnable:)) |
| 522 | | { |
| 523 | | if ((breakpoint != NULL) && !breakpoint->enabled()) |
| 524 | | { |
| 525 | | if (inContextMenu) |
| 526 | | [item setTitle:@"Enable Breakpoint"]; |
| 527 | | else |
| 528 | | [item setTitle:@"Enable Breakpoint at Cursor"]; |
| 529 | | } |
| 530 | | else |
| 531 | | { |
| 532 | | if (inContextMenu) |
| 533 | | [item setTitle:@"Disable Breakpoint"]; |
| 534 | | else |
| 535 | | [item setTitle:@"Disable Breakpoint at Cursor"]; |
| 536 | | } |
| 537 | | return (breakpoint != NULL) && isCurrent; |
| 538 | | } |
| 539 | | else if (action == @selector(debugRunToCursor:)) |
| 540 | | { |
| 541 | | return isCurrent; |
| 542 | | } |
| 543 | | else |
| 544 | | { |
| 545 | | return YES; |
| 546 | | } |
| 547 | | } |
| 548 | | |
| 549 | | @end |
trunk/src/osd/modules/debugger/osx/debugconsole.mm
| r0 | r250165 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Vas Crabb |
| 3 | //============================================================ |
| 4 | // |
| 5 | // debugconsole.m - MacOS X Cocoa debug window handling |
| 6 | // |
| 7 | //============================================================ |
| 8 | |
| 9 | #import "debugconsole.h" |
| 10 | |
| 11 | #import "debugcommandhistory.h" |
| 12 | #import "consoleview.h" |
| 13 | #import "debugview.h" |
| 14 | #import "deviceinfoviewer.h" |
| 15 | #import "devicesviewer.h" |
| 16 | #import "disassemblyview.h" |
| 17 | #import "disassemblyviewer.h" |
| 18 | #import "errorlogviewer.h" |
| 19 | #import "memoryviewer.h" |
| 20 | #import "pointsviewer.h" |
| 21 | #import "registersview.h" |
| 22 | |
| 23 | #include "debug/debugcon.h" |
| 24 | #include "debug/debugcpu.h" |
| 25 | |
| 26 | |
| 27 | @implementation MAMEDebugConsole |
| 28 | |
| 29 | - (id)initWithMachine:(running_machine &)m { |
| 30 | NSSplitView *regSplit, *dasmSplit; |
| 31 | NSScrollView *regScroll, *dasmScroll, *consoleScroll; |
| 32 | NSView *consoleContainer; |
| 33 | NSPopUpButton *actionButton; |
| 34 | NSRect rct; |
| 35 | |
| 36 | // initialise superclass |
| 37 | if (!(self = [super initWithMachine:m title:@"Debug"])) |
| 38 | return nil; |
| 39 | history = [[MAMEDebugCommandHistory alloc] init]; |
| 40 | auxiliaryWindows = [[NSMutableArray alloc] init]; |
| 41 | NSFont *const defaultFont = [[MAMEDebugView class] defaultFontForMachine:m]; |
| 42 | |
| 43 | // create the register view |
| 44 | regView = [[MAMERegistersView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; |
| 45 | regScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 46 | [regScroll setHasHorizontalScroller:YES]; |
| 47 | [regScroll setHasVerticalScroller:YES]; |
| 48 | [regScroll setAutohidesScrollers:YES]; |
| 49 | [regScroll setBorderType:NSBezelBorder]; |
| 50 | [regScroll setDocumentView:regView]; |
| 51 | [regView release]; |
| 52 | |
| 53 | // create the disassembly view |
| 54 | dasmView = [[MAMEDisassemblyView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; |
| 55 | [dasmView setExpression:@"curpc"]; |
| 56 | dasmScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 57 | [dasmScroll setHasHorizontalScroller:YES]; |
| 58 | [dasmScroll setHasVerticalScroller:YES]; |
| 59 | [dasmScroll setAutohidesScrollers:YES]; |
| 60 | [dasmScroll setBorderType:NSBezelBorder]; |
| 61 | [dasmScroll setDocumentView:dasmView]; |
| 62 | [dasmView release]; |
| 63 | |
| 64 | // create the console view |
| 65 | consoleView = [[MAMEConsoleView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; |
| 66 | consoleScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 67 | [consoleScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 68 | [consoleScroll setHasHorizontalScroller:YES]; |
| 69 | [consoleScroll setHasVerticalScroller:YES]; |
| 70 | [consoleScroll setAutohidesScrollers:YES]; |
| 71 | [consoleScroll setBorderType:NSBezelBorder]; |
| 72 | [consoleScroll setDocumentView:consoleView]; |
| 73 | [consoleView release]; |
| 74 | |
| 75 | // create the command field |
| 76 | commandField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; |
| 77 | [commandField setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)]; |
| 78 | [commandField setFont:defaultFont]; |
| 79 | [commandField setFocusRingType:NSFocusRingTypeNone]; |
| 80 | [commandField setTarget:self]; |
| 81 | [commandField setAction:@selector(doCommand:)]; |
| 82 | [commandField setDelegate:self]; |
| 83 | [commandField sizeToFit]; |
| 84 | rct = [commandField frame]; |
| 85 | [commandField setFrame:NSMakeRect(rct.size.height, 0, 100 - rct.size.height, rct.size.height)]; |
| 86 | |
| 87 | // create the action pull-down button |
| 88 | actionButton = [[self class] newActionButtonWithFrame:NSMakeRect(0, 0, rct.size.height, rct.size.height)]; |
| 89 | [actionButton setAutoresizingMask:(NSViewMaxXMargin | NSViewMaxYMargin)]; |
| 90 | [actionButton setFont:[NSFont systemFontOfSize:[defaultFont pointSize]]]; |
| 91 | [dasmView insertActionItemsInMenu:[actionButton menu] atIndex:1]; |
| 92 | |
| 93 | // create the container for the console and command input field |
| 94 | consoleContainer = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 95 | [consoleScroll setFrame:NSMakeRect(0, |
| 96 | rct.size.height, |
| 97 | 100, |
| 98 | [consoleContainer bounds].size.height - rct.size.height)]; |
| 99 | [consoleContainer addSubview:consoleScroll]; |
| 100 | [consoleContainer addSubview:commandField]; |
| 101 | [consoleContainer addSubview:actionButton]; |
| 102 | [consoleScroll release]; |
| 103 | [commandField release]; |
| 104 | [actionButton release]; |
| 105 | |
| 106 | // create the split between the disassembly and the console |
| 107 | dasmSplit = [[NSSplitView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 108 | [dasmSplit setDelegate:self]; |
| 109 | [dasmSplit setVertical:NO]; |
| 110 | [dasmSplit addSubview:dasmScroll]; |
| 111 | [dasmSplit addSubview:consoleContainer]; |
| 112 | [dasmScroll release]; |
| 113 | [consoleContainer release]; |
| 114 | |
| 115 | // create the split between the registers and the console |
| 116 | regSplit = [[NSSplitView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
| 117 | [regSplit setDelegate:self]; |
| 118 | [regSplit setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 119 | [regSplit setVertical:YES]; |
| 120 | [regSplit addSubview:regScroll]; |
| 121 | [regSplit addSubview:dasmSplit]; |
| 122 | [regScroll release]; |
| 123 | [dasmSplit release]; |
| 124 | |
| 125 | // put the split views in the window and get them into a half-reasonable state |
| 126 | [window setContentView:regSplit]; |
| 127 | [regSplit release]; |
| 128 | [regSplit adjustSubviews]; |
| 129 | [dasmSplit adjustSubviews]; |
| 130 | |
| 131 | // keyboard focus should start on the command field |
| 132 | [window makeFirstResponder:commandField]; |
| 133 | |
| 134 | // calculate the optimal size for everything |
| 135 | NSRect const available = [[NSScreen mainScreen] visibleFrame]; |
| 136 | NSSize const regCurrent = [regScroll frame].size; |
| 137 | NSSize const regSize = [NSScrollView frameSizeForContentSize:[regView maximumFrameSize] |
| 138 | hasHorizontalScroller:YES |
| 139 | hasVerticalScroller:YES |
| 140 | borderType:[regScroll borderType]]; |
| 141 | NSSize const dasmCurrent = [dasmScroll frame].size; |
| 142 | NSSize const dasmSize = [NSScrollView frameSizeForContentSize:[dasmView maximumFrameSize] |
| 143 | hasHorizontalScroller:YES |
| 144 | hasVerticalScroller:YES |
| 145 | borderType:[dasmScroll borderType]]; |
| 146 | NSSize const consoleCurrent = [consoleContainer frame].size; |
| 147 | NSSize consoleSize = [NSScrollView frameSizeForContentSize:[consoleView maximumFrameSize] |
| 148 | hasHorizontalScroller:YES |
| 149 | hasVerticalScroller:YES |
| 150 | borderType:[consoleScroll borderType]]; |
| 151 | NSRect windowFrame = [window frame]; |
| 152 | NSSize adjustment; |
| 153 | |
| 154 | consoleSize.width += consoleCurrent.width - [consoleScroll frame].size.width; |
| 155 | consoleSize.height += consoleCurrent.height - [consoleScroll frame].size.height; |
| 156 | adjustment.width = regSize.width - regCurrent.width; |
| 157 | adjustment.height = regSize.height - regCurrent.height; |
| 158 | adjustment.width += MAX(dasmSize.width - dasmCurrent.width, consoleSize.width - consoleCurrent.width); |
| 159 | |
| 160 | windowFrame.size.width += adjustment.width; |
| 161 | windowFrame.size.height += adjustment.height; // not used - better to go for fixed height |
| 162 | windowFrame.size.height = MIN(512.0, available.size.height); |
| 163 | windowFrame.size.width = MIN(windowFrame.size.width, available.size.width); |
| 164 | windowFrame.origin.x = available.origin.x + available.size.width - windowFrame.size.width; |
| 165 | windowFrame.origin.y = available.origin.y; |
| 166 | [window setFrame:windowFrame display:YES]; |
| 167 | |
| 168 | NSRect lhsFrame = [regScroll frame]; |
| 169 | NSRect rhsFrame = [dasmSplit frame]; |
| 170 | adjustment.width = MIN(regSize.width, ([regSplit frame].size.width - [regSplit dividerThickness]) / 2); |
| 171 | rhsFrame.origin.x -= lhsFrame.size.width - adjustment.width; |
| 172 | rhsFrame.size.width += lhsFrame.size.width - adjustment.width; |
| 173 | lhsFrame.size.width = adjustment.width; |
| 174 | [regScroll setFrame:lhsFrame]; |
| 175 | [dasmSplit setFrame:rhsFrame]; |
| 176 | |
| 177 | // select the current processor |
| 178 | [self setCPU:machine->firstcpu]; |
| 179 | |
| 180 | [[NSNotificationCenter defaultCenter] addObserver:self |
| 181 | selector:@selector(auxiliaryWindowWillClose:) |
| 182 | name:MAMEAuxiliaryDebugWindowWillCloseNotification |
| 183 | object:nil]; |
| 184 | |
| 185 | // don't forget the return value |
| 186 | return self; |
| 187 | } |
| 188 | |
| 189 | |
| 190 | - (void)dealloc { |
| 191 | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 192 | |
| 193 | if (history != nil) |
| 194 | [history release]; |
| 195 | if (auxiliaryWindows != nil) |
| 196 | [auxiliaryWindows release]; |
| 197 | |
| 198 | [super dealloc]; |
| 199 | } |
| 200 | |
| 201 | |
| 202 | - (void)setCPU:(device_t *)device { |
| 203 | [regView selectSubviewForDevice:device]; |
| 204 | [dasmView selectSubviewForDevice:device]; |
| 205 | [window setTitle:[NSString stringWithFormat:@"Debug: %s - %s '%s'", |
| 206 | device->machine().system().name, |
| 207 | device->name(), |
| 208 | device->tag()]]; |
| 209 | } |
| 210 | |
| 211 | |
| 212 | - (IBAction)doCommand:(id)sender { |
| 213 | NSString *command = [sender stringValue]; |
| 214 | if ([command length] == 0) |
| 215 | { |
| 216 | debug_cpu_get_visible_cpu(*machine)->debug()->single_step(); |
| 217 | [history reset]; |
| 218 | } |
| 219 | else |
| 220 | { |
| 221 | debug_console_execute_command(*machine, [command UTF8String], 1); |
| 222 | [history add:command]; |
| 223 | [history edit]; |
| 224 | } |
| 225 | [sender setStringValue:@""]; |
| 226 | } |
| 227 | |
| 228 | |
| 229 | - (IBAction)debugToggleBreakpoint:(id)sender { |
| 230 | device_t &device = [dasmView source]->device(); |
| 231 | if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) |
| 232 | { |
| 233 | offs_t const address = [dasmView selectedAddress]; |
| 234 | device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:address |
| 235 | forDevice:device]; |
| 236 | |
| 237 | // if it doesn't exist, add a new one |
| 238 | NSString *command; |
| 239 | if (bp == NULL) |
| 240 | command = [NSString stringWithFormat:@"bpset 0x%lX", (unsigned long)address]; |
| 241 | else |
| 242 | command = [NSString stringWithFormat:@"bpclear 0x%X", (unsigned)bp->index()]; |
| 243 | debug_console_execute_command(*machine, [command UTF8String], 1); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | |
| 248 | - (IBAction)debugToggleBreakpointEnable:(id)sender { |
| 249 | device_t &device = [dasmView source]->device(); |
| 250 | if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) |
| 251 | { |
| 252 | device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:[dasmView selectedAddress] |
| 253 | forDevice:device]; |
| 254 | if (bp != NULL) |
| 255 | { |
| 256 | NSString *command; |
| 257 | if (bp->enabled()) |
| 258 | command = [NSString stringWithFormat:@"bpdisable 0x%X", (unsigned)bp->index()]; |
| 259 | else |
| 260 | command = [NSString stringWithFormat:@"bpenable 0x%X", (unsigned)bp->index()]; |
| 261 | debug_console_execute_command(*machine, [command UTF8String], 1); |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | |
| 267 | - (IBAction)debugRunToCursor:(id)sender { |
| 268 | device_t &device = [dasmView source]->device(); |
| 269 | if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) |
| 270 | { |
| 271 | NSString *command = [NSString stringWithFormat:@"go 0x%lX", (unsigned long)[dasmView selectedAddress]]; |
| 272 | debug_console_execute_command(*machine, [command UTF8String], 1); |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | |
| 277 | - (IBAction)debugNewMemoryWindow:(id)sender { |
| 278 | MAMEMemoryViewer *win = [[MAMEMemoryViewer alloc] initWithMachine:*machine console:self]; |
| 279 | [auxiliaryWindows addObject:win]; |
| 280 | [win release]; |
| 281 | [win activate]; |
| 282 | } |
| 283 | |
| 284 | |
| 285 | - (IBAction)debugNewDisassemblyWindow:(id)sender { |
| 286 | MAMEDisassemblyViewer *win = [[MAMEDisassemblyViewer alloc] initWithMachine:*machine console:self]; |
| 287 | [auxiliaryWindows addObject:win]; |
| 288 | [win release]; |
| 289 | [win activate]; |
| 290 | } |
| 291 | |
| 292 | |
| 293 | - (IBAction)debugNewErrorLogWindow:(id)sender { |
| 294 | MAMEErrorLogViewer *win = [[MAMEErrorLogViewer alloc] initWithMachine:*machine console:self]; |
| 295 | [auxiliaryWindows addObject:win]; |
| 296 | [win release]; |
| 297 | [win activate]; |
| 298 | } |
| 299 | |
| 300 | |
| 301 | - (IBAction)debugNewPointsWindow:(id)sender{ |
| 302 | MAMEPointsViewer *win = [[MAMEPointsViewer alloc] initWithMachine:*machine console:self]; |
| 303 | [auxiliaryWindows addObject:win]; |
| 304 | [win release]; |
| 305 | [win activate]; |
| 306 | } |
| 307 | |
| 308 | |
| 309 | - (IBAction)debugNewDevicesWindow:(id)sender { |
| 310 | MAMEDevicesViewer *win = [[MAMEDevicesViewer alloc] initWithMachine:*machine console:self]; |
| 311 | [auxiliaryWindows addObject:win]; |
| 312 | [win release]; |
| 313 | [win activate]; |
| 314 | } |
| 315 | |
| 316 | |
| 317 | - (void)debugNewMemoryWindowForSpace:(address_space *)space device:(device_t *)device expression:(NSString *)expression { |
| 318 | MAMEMemoryViewer *win = [[MAMEMemoryViewer alloc] initWithMachine:*machine console:self]; |
| 319 | [auxiliaryWindows addObject:win]; |
| 320 | [win release]; |
| 321 | if ([win selectSubviewForSpace:space]) |
| 322 | { |
| 323 | if (expression != nil) |
| 324 | [win setExpression:expression]; |
| 325 | } |
| 326 | else |
| 327 | { |
| 328 | [win selectSubviewForDevice:device]; |
| 329 | } |
| 330 | [win activate]; |
| 331 | } |
| 332 | |
| 333 | |
| 334 | - (void)debugNewDisassemblyWindowForSpace:(address_space *)space device:(device_t *)device expression:(NSString *)expression { |
| 335 | MAMEDisassemblyViewer *win = [[MAMEDisassemblyViewer alloc] initWithMachine:*machine console:self]; |
| 336 | [auxiliaryWindows addObject:win]; |
| 337 | [win release]; |
| 338 | if ([win selectSubviewForSpace:space]) |
| 339 | { |
| 340 | if (expression != nil) |
| 341 | [win setExpression:expression]; |
| 342 | } |
| 343 | else |
| 344 | { |
| 345 | [win selectSubviewForDevice:device]; |
| 346 | } |
| 347 | [win activate]; |
| 348 | } |
| 349 | |
| 350 | |
| 351 | - (void)debugNewInfoWindowForDevice:(device_t &)device { |
| 352 | MAMEDeviceInfoViewer *win = [[MAMEDeviceInfoViewer alloc] initWithDevice:device |
| 353 | machine:*machine |
| 354 | console:self]; |
| 355 | [auxiliaryWindows addObject:win]; |
| 356 | [win release]; |
| 357 | [win activate]; |
| 358 | } |
| 359 | |
| 360 | |
| 361 | - (void)showDebugger:(NSNotification *)notification { |
| 362 | device_t *device = (device_t * )[[[notification userInfo] objectForKey:@"MAMEDebugDevice"] pointerValue]; |
| 363 | if (&device->machine() == machine) |
| 364 | { |
| 365 | [self setCPU:device]; |
| 366 | [window makeKeyAndOrderFront:self]; |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | |
| 371 | - (void)auxiliaryWindowWillClose:(NSNotification *)notification { |
| 372 | [auxiliaryWindows removeObjectIdenticalTo:[notification object]]; |
| 373 | } |
| 374 | |
| 375 | |
| 376 | - (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor { |
| 377 | if (control == commandField) |
| 378 | [history edit]; |
| 379 | |
| 380 | return YES; |
| 381 | } |
| 382 | |
| 383 | |
| 384 | - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command { |
| 385 | if (control == commandField) { |
| 386 | if (command == @selector(cancelOperation:)) { |
| 387 | [commandField setStringValue:@""]; |
| 388 | [history reset]; |
| 389 | return YES; |
| 390 | } else if (command == @selector(moveUp:)) { |
| 391 | NSString *hist = [history previous:[commandField stringValue]]; |
| 392 | if (hist != nil) { |
| 393 | [commandField setStringValue:hist]; |
| 394 | [commandField selectText:self]; |
| 395 | [(NSText *)[window firstResponder] setSelectedRange:NSMakeRange([hist length], 0)]; |
| 396 | } |
| 397 | return YES; |
| 398 | } else if (command == @selector(moveDown:)) { |
| 399 | NSString *hist = [history next:[commandField stringValue]]; |
| 400 | if (hist != nil) { |
| 401 | [commandField setStringValue:hist]; |
| 402 | [commandField selectText:self]; |
| 403 | [(NSText *)[window firstResponder] setSelectedRange:NSMakeRange([hist length], 0)]; |
| 404 | } |
| 405 | return YES; |
| 406 | } |
| 407 | } |
| 408 | return NO; |
| 409 | } |
| 410 | |
| 411 | |
| 412 | - (void)windowWillClose:(NSNotification *)notification { |
| 413 | if ([notification object] == window) |
| 414 | { |
| 415 | NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithPointer:machine], |
| 416 | @"MAMEDebugMachine", |
| 417 | nil]; |
| 418 | [[NSNotificationCenter defaultCenter] postNotificationName:MAMEHideDebuggerNotification |
| 419 | object:self |
| 420 | userInfo:info]; |
| 421 | debug_cpu_get_visible_cpu(*machine)->debug()->go(); |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | |
| 426 | - (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)min ofSubviewAt:(NSInteger)offs { |
| 427 | return (min < 100) ? 100 : min; |
| 428 | } |
| 429 | |
| 430 | |
| 431 | - (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)max ofSubviewAt:(NSInteger)offs { |
| 432 | NSSize sz = [sender bounds].size; |
| 433 | CGFloat allowed = ([sender isVertical] ? sz.width : sz.height) - 100 - [sender dividerThickness]; |
| 434 | return (max > allowed) ? allowed : max; |
| 435 | } |
| 436 | |
| 437 | |
| 438 | - (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview { |
| 439 | // allow registers or disassembly to be collapsed, but not console |
| 440 | return [[sender subviews] indexOfObjectIdenticalTo:subview] == 0; |
| 441 | } |
| 442 | |
| 443 | |
| 444 | - (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize { |
| 445 | // This can only deal with a single split, but that's all we use, anyway |
| 446 | NSRect first, second; |
| 447 | [sender adjustSubviews]; |
| 448 | first = [[[sender subviews] objectAtIndex:0] frame]; |
| 449 | second = [[[sender subviews] objectAtIndex:1] frame]; |
| 450 | if ([sender isVertical]) { |
| 451 | if (first.size.width < 100) { |
| 452 | CGFloat diff = 100 - first.size.width; |
| 453 | first.size.width = 100; |
| 454 | second.origin.x += diff; |
| 455 | second.size.width -= diff; |
| 456 | } else if (second.size.width < 100) { |
| 457 | CGFloat diff = 100 - second.size.width; |
| 458 | second.size.width = 100; |
| 459 | second.origin.x -= diff; |
| 460 | first.size.width -= diff; |
| 461 | } |
| 462 | } else { |
| 463 | if (first.size.height < 100) { |
| 464 | CGFloat diff = 100 - first.size.height; |
| 465 | first.size.height = 100; |
| 466 | second.origin.y += diff; |
| 467 | second.size.height -= diff; |
| 468 | } else if (second.size.height < 100) { |
| 469 | CGFloat diff = 100 - second.size.height; |
| 470 | second.size.height = 100; |
| 471 | second.origin.y -= diff; |
| 472 | first.size.height -= diff; |
| 473 | } |
| 474 | } |
| 475 | [[[sender subviews] objectAtIndex:0] setFrame:first]; |
| 476 | [[[sender subviews] objectAtIndex:1] setFrame:second]; |
| 477 | } |
| 478 | |
| 479 | |
| 480 | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 481 | SEL const action = [item action]; |
| 482 | BOOL const inContextMenu = ([item menu] == [dasmView menu]); |
| 483 | BOOL const haveCursor = [dasmView cursorVisible]; |
| 484 | BOOL const isCurrent = (debug_cpu_get_visible_cpu(*machine) == &[dasmView source]->device()); |
| 485 | |
| 486 | device_debug::breakpoint *breakpoint = NULL; |
| 487 | if (haveCursor) |
| 488 | { |
| 489 | breakpoint = [[self class] findBreakpointAtAddress:[dasmView selectedAddress] |
| 490 | forDevice:[dasmView source]->device()]; |
| 491 | } |
| 492 | |
| 493 | if (action == @selector(debugToggleBreakpoint:)) |
| 494 | { |
| 495 | if (haveCursor) |
| 496 | { |
| 497 | if (breakpoint != NULL) |
| 498 | { |
| 499 | if (inContextMenu) |
| 500 | [item setTitle:@"Clear Breakpoint"]; |
| 501 | else |
| 502 | [item setTitle:@"Clear Breakpoint at Cursor"]; |
| 503 | } |
| 504 | else |
| 505 | { |
| 506 | if (inContextMenu) |
| 507 | [item setTitle:@"Set Breakpoint"]; |
| 508 | else |
| 509 | [item setTitle:@"Set Breakpoint at Cursor"]; |
| 510 | } |
| 511 | } |
| 512 | else |
| 513 | { |
| 514 | if (inContextMenu) |
| 515 | [item setTitle:@"Toggle Breakpoint"]; |
| 516 | else |
| 517 | [item setTitle:@"Toggle Breakpoint at Cursor"]; |
| 518 | } |
| 519 | return haveCursor && isCurrent; |
| 520 | } |
| 521 | else if (action == @selector(debugToggleBreakpointEnable:)) |
| 522 | { |
| 523 | if ((breakpoint != NULL) && !breakpoint->enabled()) |
| 524 | { |
| 525 | if (inContextMenu) |
| 526 | [item setTitle:@"Enable Breakpoint"]; |
| 527 | else |
| 528 | [item setTitle:@"Enable Breakpoint at Cursor"]; |
| 529 | } |
| 530 | else |
| 531 | { |
| 532 | if (inContextMenu) |
| 533 | [item setTitle:@"Disable Breakpoint"]; |
| 534 | else |
| 535 | [item setTitle:@"Disable Breakpoint at Cursor"]; |
| 536 | } |
| 537 | return (breakpoint != NULL) && isCurrent; |
| 538 | } |
| 539 | else if (action == @selector(debugRunToCursor:)) |
| 540 | { |
| 541 | return isCurrent; |
| 542 | } |
| 543 | else |
| 544 | { |
| 545 | return YES; |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | @end |
trunk/src/osd/modules/debugger/osx/debugview.m
| r250164 | r250165 | |
| 1 | | // license:BSD-3-Clause |
| 2 | | // copyright-holders:Vas Crabb |
| 3 | | //============================================================ |
| 4 | | // |
| 5 | | // debugview.m - MacOS X Cocoa debug window handling |
| 6 | | // |
| 7 | | //============================================================ |
| 8 | | |
| 9 | | #import "debugview.h" |
| 10 | | |
| 11 | | #include "debug/debugcpu.h" |
| 12 | | |
| 13 | | #include "modules/lib/osdobj_common.h" |
| 14 | | |
| 15 | | #include <string.h> |
| 16 | | |
| 17 | | |
| 18 | | static NSColor *DefaultForeground; |
| 19 | | static NSColor *ChangedForeground; |
| 20 | | static NSColor *InvalidForeground; |
| 21 | | static NSColor *CommentForeground; |
| 22 | | static NSColor *DisabledChangedForeground; |
| 23 | | static NSColor *DisabledInvalidForeground; |
| 24 | | static NSColor *DisabledCommentForeground; |
| 25 | | |
| 26 | | static NSColor *DefaultBackground; |
| 27 | | static NSColor *VisitedBackground; |
| 28 | | static NSColor *AncillaryBackground; |
| 29 | | static NSColor *SelectedBackground; |
| 30 | | static NSColor *CurrentBackground; |
| 31 | | static NSColor *SelectedCurrentBackground; |
| 32 | | static NSColor *InactiveSelectedBackground; |
| 33 | | static NSColor *InactiveSelectedCurrentBackground; |
| 34 | | |
| 35 | | static NSCharacterSet *NonWhiteCharacters; |
| 36 | | |
| 37 | | |
| 38 | | static void debugwin_view_update(debug_view &view, void *osdprivate) |
| 39 | | { |
| 40 | | NSAutoreleasePool *const pool = [[NSAutoreleasePool alloc] init]; |
| 41 | | [(MAMEDebugView *)osdprivate update]; |
| 42 | | [pool release]; |
| 43 | | } |
| 44 | | |
| 45 | | |
| 46 | | @implementation MAMEDebugView |
| 47 | | |
| 48 | | + (void)initialize { |
| 49 | | DefaultForeground = [[NSColor colorWithCalibratedWhite:0.0 alpha:1.0] retain]; |
| 50 | | ChangedForeground = [[NSColor colorWithCalibratedRed:0.875 green:0.0 blue:0.0 alpha:1.0] retain]; |
| 51 | | InvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:1.0 alpha:1.0] retain]; |
| 52 | | CommentForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.375 blue:0.0 alpha:1.0] retain]; |
| 53 | | DisabledChangedForeground = [[NSColor colorWithCalibratedRed:0.5 green:0.125 blue:0.125 alpha:1.0] retain]; |
| 54 | | DisabledInvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.5 alpha:1.0] retain]; |
| 55 | | DisabledCommentForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.25 blue:0.0 alpha:1.0] retain]; |
| 56 | | |
| 57 | | DefaultBackground = [[NSColor colorWithCalibratedWhite:1.0 alpha:1.0] retain]; |
| 58 | | VisitedBackground = [[NSColor colorWithCalibratedRed:0.75 green:1.0 blue:0.75 alpha:1.0] retain]; |
| 59 | | AncillaryBackground = [[NSColor colorWithCalibratedWhite:0.75 alpha:1.0] retain]; |
| 60 | | SelectedBackground = [[NSColor colorWithCalibratedRed:0.75 green:0.875 blue:1.0 alpha:1.0] retain]; |
| 61 | | CurrentBackground = [[NSColor colorWithCalibratedRed:1.0 green:0.75 blue:0.75 alpha:1.0] retain]; |
| 62 | | SelectedCurrentBackground = [[NSColor colorWithCalibratedRed:0.875 green:0.625 blue:0.875 alpha:1.0] retain]; |
| 63 | | InactiveSelectedBackground = [[NSColor colorWithCalibratedWhite:0.875 alpha:1.0] retain]; |
| 64 | | InactiveSelectedCurrentBackground = [[NSColor colorWithCalibratedRed:0.875 green:0.5 blue:0.625 alpha:1.0] retain]; |
| 65 | | |
| 66 | | NonWhiteCharacters = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet]; |
| 67 | | } |
| 68 | | |
| 69 | | |
| 70 | | - (NSColor *)foregroundForAttribute:(UINT8)attrib { |
| 71 | | if (attrib & DCA_COMMENT) |
| 72 | | return (attrib & DCA_DISABLED) ? DisabledCommentForeground : CommentForeground; |
| 73 | | else if (attrib & DCA_INVALID) |
| 74 | | return (attrib & DCA_DISABLED) ? DisabledInvalidForeground : InvalidForeground; |
| 75 | | else if (attrib & DCA_CHANGED) |
| 76 | | return (attrib & DCA_DISABLED) ? DisabledChangedForeground : ChangedForeground; |
| 77 | | else |
| 78 | | return DefaultForeground; |
| 79 | | } |
| 80 | | |
| 81 | | |
| 82 | | - (NSColor *)backgroundForAttribute:(UINT8)attrib { |
| 83 | | BOOL const active = [[self window] isKeyWindow] && ([[self window] firstResponder] == self); |
| 84 | | if ((attrib & DCA_SELECTED) && (attrib & DCA_CURRENT)) |
| 85 | | return active ? SelectedCurrentBackground : InactiveSelectedCurrentBackground; |
| 86 | | else if (attrib & DCA_CURRENT) |
| 87 | | return CurrentBackground; |
| 88 | | else if (attrib & DCA_SELECTED) |
| 89 | | return active ? SelectedBackground : InactiveSelectedBackground; |
| 90 | | else if (attrib & DCA_ANCILLARY) |
| 91 | | return AncillaryBackground; |
| 92 | | else if (attrib & DCA_VISITED) |
| 93 | | return VisitedBackground; |
| 94 | | else |
| 95 | | return DefaultBackground; |
| 96 | | } |
| 97 | | |
| 98 | | |
| 99 | | - (debug_view_xy)convertLocation:(NSPoint)location { |
| 100 | | debug_view_xy position; |
| 101 | | |
| 102 | | position.y = lround(floor(location.y / fontHeight)); |
| 103 | | if (position.y < 0) |
| 104 | | position.y = 0; |
| 105 | | else if (position.y >= totalHeight) |
| 106 | | position.y = totalHeight - 1; |
| 107 | | |
| 108 | | debug_view_xy const origin = view->visible_position(); |
| 109 | | debug_view_xy const size = view->visible_size(); |
| 110 | | debug_view_char const *data = view->viewdata(); |
| 111 | | if (!data || (position.y < origin.y) || (position.y >= origin.y + size.y)) |
| 112 | | { |
| 113 | | // y coordinate outside visible area, x will be a guess |
| 114 | | position.x = lround(floor((location.x - [textContainer lineFragmentPadding]) / fontWidth)); |
| 115 | | } |
| 116 | | else |
| 117 | | { |
| 118 | | data += ((position.y - view->visible_position().y) * view->visible_size().x); |
| 119 | | int attr = -1; |
| 120 | | NSUInteger start = 0, length = 0; |
| 121 | | for (UINT32 col = origin.x; col < origin.x + size.x; col++) |
| 122 | | { |
| 123 | | [[text mutableString] appendFormat:@"%c", data[col - origin.x].byte]; |
| 124 | | if ((start < length) && (attr != data[col - origin.x].attrib)) |
| 125 | | { |
| 126 | | NSRange const run = NSMakeRange(start, length - start); |
| 127 | | [text addAttribute:NSFontAttributeName |
| 128 | | value:font |
| 129 | | range:NSMakeRange(0, length)]; |
| 130 | | [text addAttribute:NSForegroundColorAttributeName |
| 131 | | value:[self foregroundForAttribute:attr] |
| 132 | | range:run]; |
| 133 | | start = length; |
| 134 | | } |
| 135 | | attr = data[col - origin.x].attrib; |
| 136 | | length = [text length]; |
| 137 | | } |
| 138 | | if (start < length) |
| 139 | | { |
| 140 | | NSRange const run = NSMakeRange(start, length - start); |
| 141 | | [text addAttribute:NSFontAttributeName |
| 142 | | value:font |
| 143 | | range:NSMakeRange(0, length)]; |
| 144 | | [text addAttribute:NSForegroundColorAttributeName |
| 145 | | value:[self foregroundForAttribute:attr] |
| 146 | | range:run]; |
| 147 | | } |
| 148 | | CGFloat fraction; |
| 149 | | NSUInteger const glyph = [layoutManager glyphIndexForPoint:NSMakePoint(location.x, fontHeight / 2) |
| 150 | | inTextContainer:textContainer |
| 151 | | fractionOfDistanceThroughGlyph:&fraction]; |
| 152 | | position.x = [layoutManager characterIndexForGlyphAtIndex:glyph]; // FIXME: assumes 1:1 character mapping |
| 153 | | [text deleteCharactersInRange:NSMakeRange(0, length)]; |
| 154 | | } |
| 155 | | if (position.x < 0) |
| 156 | | position.x = 0; |
| 157 | | else if (position.x >= totalWidth) |
| 158 | | position.x = totalWidth - 1; |
| 159 | | |
| 160 | | return position; |
| 161 | | } |
| 162 | | |
| 163 | | |
| 164 | | - (void)convertBounds:(NSRect)b toFirstAffectedLine:(INT32 *)f count:(INT32 *)c { |
| 165 | | *f = lround(floor(b.origin.y / fontHeight)); |
| 166 | | *c = lround(ceil((b.origin.y + b.size.height) / fontHeight)) - *f; |
| 167 | | } |
| 168 | | |
| 169 | | |
| 170 | | - (void)recomputeVisible { |
| 171 | | // this gets all the lines that are at least partially visible |
| 172 | | debug_view_xy origin(0, 0), size(totalWidth, totalHeight); |
| 173 | | [self convertBounds:[self visibleRect] toFirstAffectedLine:&origin.y count:&size.y]; |
| 174 | | size.y = MIN(size.y, totalHeight - origin.y); |
| 175 | | |
| 176 | | // tell the underlying view how much real estate is available |
| 177 | | view->set_visible_size(size); |
| 178 | | view->set_visible_position(origin); |
| 179 | | originTop = origin.y; |
| 180 | | } |
| 181 | | |
| 182 | | |
| 183 | | - (void)typeCharacterAndScrollToCursor:(char)ch { |
| 184 | | debug_view_xy const oldPos = view->cursor_position(); |
| 185 | | view->process_char(ch); |
| 186 | | if (view->cursor_supported() && view->cursor_visible()) |
| 187 | | { |
| 188 | | debug_view_xy const newPos = view->cursor_position(); |
| 189 | | if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y)) |
| 190 | | { |
| 191 | | // FIXME - use proper font metrics |
| 192 | | [self scrollRectToVisible:NSMakeRect((newPos.x * fontWidth) + [textContainer lineFragmentPadding], |
| 193 | | newPos.y * fontHeight, |
| 194 | | fontWidth, |
| 195 | | fontHeight)]; |
| 196 | | } |
| 197 | | } |
| 198 | | } |
| 199 | | |
| 200 | | |
| 201 | | - (void)adjustSizeAndRecomputeVisible { |
| 202 | | NSSize const clip = [[[self enclosingScrollView] contentView] bounds].size; |
| 203 | | NSSize content = NSMakeSize((fontWidth * totalWidth) + (2 * [textContainer lineFragmentPadding]), |
| 204 | | fontHeight * totalHeight); |
| 205 | | if (wholeLineScroll) |
| 206 | | content.height += (fontHeight * 2) - 1; |
| 207 | | [self setFrameSize:NSMakeSize(ceil(MAX(clip.width, content.width)), |
| 208 | | ceil(MAX(clip.height, content.height)))]; |
| 209 | | [self recomputeVisible]; |
| 210 | | } |
| 211 | | |
| 212 | | |
| 213 | | + (NSFont *)defaultFontForMachine:(running_machine &)m { |
| 214 | | osd_options const &options = downcast<osd_options const &>(m.options()); |
| 215 | | char const *const face = options.debugger_font(); |
| 216 | | float const size = options.debugger_font_size(); |
| 217 | | |
| 218 | | NSFont *const result = (('\0' != *face) && (0 != strcmp(OSDOPTVAL_AUTO, face))) |
| 219 | | ? [NSFont fontWithName:[NSString stringWithUTF8String:face] size:MAX(0, size)] |
| 220 | | : nil; |
| 221 | | |
| 222 | | return (nil != result) ? result : [NSFont userFixedPitchFontOfSize:MAX(0, size)]; |
| 223 | | } |
| 224 | | |
| 225 | | |
| 226 | | - (id)initWithFrame:(NSRect)f type:(debug_view_type)t machine:(running_machine &)m wholeLineScroll:(BOOL)w { |
| 227 | | if (!(self = [super initWithFrame:f])) |
| 228 | | return nil; |
| 229 | | type = t; |
| 230 | | machine = &m; |
| 231 | | view = machine->debug_view().alloc_view((debug_view_type)type, debugwin_view_update, self); |
| 232 | | if (view == nil) { |
| 233 | | [self release]; |
| 234 | | return nil; |
| 235 | | } |
| 236 | | wholeLineScroll = w; |
| 237 | | debug_view_xy const size = view->total_size(); |
| 238 | | totalWidth = size.x; |
| 239 | | totalHeight = size.y; |
| 240 | | originTop = 0; |
| 241 | | |
| 242 | | text = [[NSTextStorage alloc] init]; |
| 243 | | textContainer = [[NSTextContainer alloc] init]; |
| 244 | | layoutManager = [[NSLayoutManager alloc] init]; |
| 245 | | [layoutManager addTextContainer:textContainer]; |
| 246 | | [textContainer release]; |
| 247 | | [text addLayoutManager:layoutManager]; |
| 248 | | [layoutManager release]; |
| 249 | | |
| 250 | | [self setFont:[[self class] defaultFontForMachine:m]]; |
| 251 | | |
| 252 | | NSMenu *contextMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Context"]; |
| 253 | | [self addContextMenuItemsToMenu:contextMenu]; |
| 254 | | [self setMenu:contextMenu]; |
| 255 | | [contextMenu release]; |
| 256 | | |
| 257 | | return self; |
| 258 | | } |
| 259 | | |
| 260 | | |
| 261 | | - (void)dealloc { |
| 262 | | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 263 | | if (view != NULL) machine->debug_view().free_view(*view); |
| 264 | | if (font != nil) [font release]; |
| 265 | | if (text != nil) [text release]; |
| 266 | | [super dealloc]; |
| 267 | | } |
| 268 | | |
| 269 | | |
| 270 | | - (void)update { |
| 271 | | // resize our frame if the total size has changed |
| 272 | | debug_view_xy const newSize = view->total_size(); |
| 273 | | BOOL const resized = (newSize.x != totalWidth) || (newSize.y != totalHeight); |
| 274 | | if (resized) |
| 275 | | { |
| 276 | | NSScrollView *const scroller = [self enclosingScrollView]; |
| 277 | | if (scroller) |
| 278 | | { |
| 279 | | NSSize const clip = [[scroller contentView] bounds].size; |
| 280 | | NSSize content = NSMakeSize((fontWidth * newSize.x) + (2 * [textContainer lineFragmentPadding]), |
| 281 | | fontHeight * newSize.y); |
| 282 | | if (wholeLineScroll) |
| 283 | | content.height += (fontHeight * 2) - 1; |
| 284 | | [self setFrameSize:NSMakeSize(ceil(MAX(clip.width, content.width)), |
| 285 | | ceil(MAX(clip.height, content.height)))]; |
| 286 | | } |
| 287 | | totalWidth = newSize.x; |
| 288 | | totalHeight = newSize.y; |
| 289 | | } |
| 290 | | |
| 291 | | // scroll the view if we're being told to |
| 292 | | debug_view_xy const newOrigin = view->visible_position(); |
| 293 | | if (newOrigin.y != originTop) |
| 294 | | { |
| 295 | | NSRect const visible = [self visibleRect]; |
| 296 | | NSPoint scroll = NSMakePoint(visible.origin.x, newOrigin.y * fontHeight); |
| 297 | | [self scrollPoint:scroll]; |
| 298 | | originTop = newOrigin.y; |
| 299 | | } |
| 300 | | |
| 301 | | // mark as dirty |
| 302 | | [self setNeedsDisplay:YES]; |
| 303 | | } |
| 304 | | |
| 305 | | |
| 306 | | - (NSSize)maximumFrameSize { |
| 307 | | debug_view_xy const max = view->total_size(); |
| 308 | | return NSMakeSize(ceil((max.x * fontWidth) + (2 * [textContainer lineFragmentPadding])), |
| 309 | | ceil((max.y + (wholeLineScroll ? 1 : 0)) * fontHeight)); |
| 310 | | } |
| 311 | | |
| 312 | | |
| 313 | | - (NSFont *)font { |
| 314 | | return [[font retain] autorelease]; |
| 315 | | } |
| 316 | | |
| 317 | | |
| 318 | | - (void)setFont:(NSFont *)f { |
| 319 | | [font autorelease]; |
| 320 | | font = [f retain]; |
| 321 | | fontWidth = [font maximumAdvancement].width; |
| 322 | | fontHeight = ceil([font ascender] - [font descender]); |
| 323 | | fontAscent = [font ascender]; |
| 324 | | [[self enclosingScrollView] setLineScroll:fontHeight]; |
| 325 | | totalWidth = totalHeight = 0; |
| 326 | | [self update]; |
| 327 | | } |
| 328 | | |
| 329 | | |
| 330 | | - (BOOL)cursorSupported { |
| 331 | | return view->cursor_supported(); |
| 332 | | } |
| 333 | | |
| 334 | | |
| 335 | | - (BOOL)cursorVisible { |
| 336 | | return view->cursor_visible(); |
| 337 | | } |
| 338 | | |
| 339 | | |
| 340 | | - (debug_view_xy)cursorPosition { |
| 341 | | return view->cursor_position(); |
| 342 | | } |
| 343 | | |
| 344 | | |
| 345 | | - (IBAction)copyVisible:(id)sender { |
| 346 | | debug_view_xy const size = view->visible_size(); |
| 347 | | debug_view_char const *data = view->viewdata(); |
| 348 | | if (!data) |
| 349 | | { |
| 350 | | NSBeep(); |
| 351 | | return; |
| 352 | | } |
| 353 | | |
| 354 | | for (UINT32 row = 0; row < size.y; row++, data += size.x) |
| 355 | | { |
| 356 | | // add content for the line and set colours |
| 357 | | int attr = -1; |
| 358 | | NSUInteger start = [text length], length = start; |
| 359 | | for (UINT32 col = 0; col < size.x; col++) |
| 360 | | { |
| 361 | | [[text mutableString] appendFormat:@"%c", data[col].byte]; |
| 362 | | if ((start < length) && (attr != (data[col].attrib & ~DCA_SELECTED))) |
| 363 | | { |
| 364 | | NSRange const run = NSMakeRange(start, length - start); |
| 365 | | [text addAttribute:NSForegroundColorAttributeName |
| 366 | | value:[self foregroundForAttribute:attr] |
| 367 | | range:run]; |
| 368 | | [text addAttribute:NSBackgroundColorAttributeName |
| 369 | | value:[self backgroundForAttribute:attr] |
| 370 | | range:run]; |
| 371 | | start = length; |
| 372 | | } |
| 373 | | attr = data[col].attrib & ~DCA_SELECTED; |
| 374 | | length = [text length]; |
| 375 | | } |
| 376 | | |
| 377 | | // clean up trailing whitespace |
| 378 | | NSRange trim = [[text string] rangeOfCharacterFromSet:NonWhiteCharacters |
| 379 | | options:NSBackwardsSearch |
| 380 | | range:NSMakeRange(start, length - start)]; |
| 381 | | if (trim.location != NSNotFound) |
| 382 | | trim = [[text string] rangeOfComposedCharacterSequenceAtIndex:(trim.location + trim.length - 1)]; |
| 383 | | else if (start > 0) |
| 384 | | trim = [[text string] rangeOfComposedCharacterSequenceAtIndex:(start - 1)]; |
| 385 | | else |
| 386 | | trim = NSMakeRange(start, 0); |
| 387 | | trim.location += trim.length; |
| 388 | | trim.length = length - trim.location; |
| 389 | | [text deleteCharactersInRange:trim]; |
| 390 | | |
| 391 | | // add the line ending and set colours |
| 392 | | [[text mutableString] appendString:@"\n"]; |
| 393 | | NSRange const run = NSMakeRange(start, [text length] - start); |
| 394 | | [text addAttribute:NSForegroundColorAttributeName |
| 395 | | value:[self foregroundForAttribute:attr] |
| 396 | | range:run]; |
| 397 | | [text addAttribute:NSBackgroundColorAttributeName |
| 398 | | value:[self backgroundForAttribute:attr] |
| 399 | | range:run]; |
| 400 | | } |
| 401 | | |
| 402 | | // set the font and send it to the pasteboard |
| 403 | | NSRange const run = NSMakeRange(0, [text length]); |
| 404 | | [text addAttribute:NSFontAttributeName value:font range:run]; |
| 405 | | NSPasteboard *const board = [NSPasteboard generalPasteboard]; |
| 406 | | [board declareTypes:[NSArray arrayWithObject:NSRTFPboardType] owner:nil]; |
| 407 | | [board setData:[text RTFFromRange:run documentAttributes:[NSDictionary dictionary]] forType:NSRTFPboardType]; |
| 408 | | [text deleteCharactersInRange:run]; |
| 409 | | } |
| 410 | | |
| 411 | | |
| 412 | | - (IBAction)paste:(id)sender { |
| 413 | | NSPasteboard *const board = [NSPasteboard generalPasteboard]; |
| 414 | | NSString *const avail = [board availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; |
| 415 | | if (avail == nil) |
| 416 | | { |
| 417 | | NSBeep(); |
| 418 | | return; |
| 419 | | } |
| 420 | | |
| 421 | | NSData *const data = [[board stringForType:avail] dataUsingEncoding:NSASCIIStringEncoding |
| 422 | | allowLossyConversion:YES]; |
| 423 | | char const *const bytes = (char const *)[data bytes]; |
| 424 | | debug_view_xy const oldPos = view->cursor_position(); |
| 425 | | for (NSUInteger i = 0, l = [data length]; i < l; i++) |
| 426 | | view->process_char(bytes[i]); |
| 427 | | if (view->cursor_supported() && view->cursor_visible()) |
| 428 | | { |
| 429 | | debug_view_xy const newPos = view->cursor_position(); |
| 430 | | if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y)) |
| 431 | | { |
| 432 | | // FIXME - use proper font metrics |
| 433 | | [self scrollRectToVisible:NSMakeRect((newPos.x * fontWidth) + [textContainer lineFragmentPadding], |
| 434 | | newPos.y * fontHeight, |
| 435 | | fontWidth, |
| 436 | | fontHeight)]; |
| 437 | | } |
| 438 | | } |
| 439 | | } |
| 440 | | |
| 441 | | |
| 442 | | - (void)viewBoundsDidChange:(NSNotification *)notification { |
| 443 | | NSView *const changed = [notification object]; |
| 444 | | if (changed == [[self enclosingScrollView] contentView]) |
| 445 | | [self adjustSizeAndRecomputeVisible]; |
| 446 | | } |
| 447 | | |
| 448 | | |
| 449 | | - (void)viewFrameDidChange:(NSNotification *)notification { |
| 450 | | NSView *const changed = [notification object]; |
| 451 | | if (changed == [[self enclosingScrollView] contentView]) |
| 452 | | [self adjustSizeAndRecomputeVisible]; |
| 453 | | } |
| 454 | | |
| 455 | | |
| 456 | | - (void)windowDidBecomeKey:(NSNotification *)notification { |
| 457 | | NSWindow *const win = [notification object]; |
| 458 | | if ((win == [self window]) && ([win firstResponder] == self) && view->cursor_supported()) |
| 459 | | [self setNeedsDisplay:YES]; |
| 460 | | } |
| 461 | | |
| 462 | | |
| 463 | | - (void)windowDidResignKey:(NSNotification *)notification { |
| 464 | | NSWindow *const win = [notification object]; |
| 465 | | if ((win == [self window]) && ([win firstResponder] == self) && view->cursor_supported()) |
| 466 | | [self setNeedsDisplay:YES]; |
| 467 | | } |
| 468 | | |
| 469 | | |
| 470 | | - (void)addContextMenuItemsToMenu:(NSMenu *)menu { |
| 471 | | NSMenuItem *item; |
| 472 | | |
| 473 | | item = [menu addItemWithTitle:@"Copy Visible" |
| 474 | | action:@selector(copyVisible:) |
| 475 | | keyEquivalent:@""]; |
| 476 | | [item setTarget:self]; |
| 477 | | |
| 478 | | item = [menu addItemWithTitle:@"Paste" |
| 479 | | action:@selector(paste:) |
| 480 | | keyEquivalent:@""]; |
| 481 | | [item setTarget:self]; |
| 482 | | } |
| 483 | | |
| 484 | | |
| 485 | | - (BOOL)acceptsFirstResponder { |
| 486 | | return view->cursor_supported(); |
| 487 | | } |
| 488 | | |
| 489 | | |
| 490 | | - (BOOL)becomeFirstResponder { |
| 491 | | if (view->cursor_supported()) |
| 492 | | { |
| 493 | | debug_view_xy pos; |
| 494 | | view->set_cursor_visible(true); |
| 495 | | pos = view->cursor_position(); |
| 496 | | [self scrollRectToVisible:NSMakeRect((pos.x * fontWidth) + [textContainer lineFragmentPadding], |
| 497 | | pos.y * fontHeight, |
| 498 | | fontWidth, |
| 499 | | fontHeight)]; // FIXME: metrics |
| 500 | | [self setNeedsDisplay:YES]; |
| 501 | | return [super becomeFirstResponder]; |
| 502 | | } |
| 503 | | else |
| 504 | | { |
| 505 | | return NO; |
| 506 | | } |
| 507 | | } |
| 508 | | |
| 509 | | |
| 510 | | - (BOOL)resignFirstResponder { |
| 511 | | if (view->cursor_supported()) |
| 512 | | [self setNeedsDisplay:YES]; |
| 513 | | return [super resignFirstResponder]; |
| 514 | | } |
| 515 | | |
| 516 | | |
| 517 | | - (void)viewDidMoveToSuperview { |
| 518 | | [super viewDidMoveToSuperview]; |
| 519 | | |
| 520 | | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 521 | | name:NSViewBoundsDidChangeNotification |
| 522 | | object:nil]; |
| 523 | | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 524 | | name:NSViewFrameDidChangeNotification |
| 525 | | object:nil]; |
| 526 | | |
| 527 | | NSScrollView *const scroller = [self enclosingScrollView]; |
| 528 | | if (scroller != nil) |
| 529 | | { |
| 530 | | [scroller setLineScroll:fontHeight]; |
| 531 | | [[scroller contentView] setPostsBoundsChangedNotifications:YES]; |
| 532 | | [[scroller contentView] setPostsFrameChangedNotifications:YES]; |
| 533 | | [[NSNotificationCenter defaultCenter] addObserver:self |
| 534 | | selector:@selector(viewBoundsDidChange:) |
| 535 | | name:NSViewBoundsDidChangeNotification |
| 536 | | object:[scroller contentView]]; |
| 537 | | [[NSNotificationCenter defaultCenter] addObserver:self |
| 538 | | selector:@selector(viewFrameDidChange:) |
| 539 | | name:NSViewFrameDidChangeNotification |
| 540 | | object:[scroller contentView]]; |
| 541 | | [self adjustSizeAndRecomputeVisible]; |
| 542 | | } |
| 543 | | } |
| 544 | | |
| 545 | | |
| 546 | | - (void)viewDidMoveToWindow { |
| 547 | | [super viewDidMoveToWindow]; |
| 548 | | |
| 549 | | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 550 | | name:NSWindowDidBecomeKeyNotification |
| 551 | | object:nil]; |
| 552 | | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 553 | | name:NSWindowDidResignKeyNotification |
| 554 | | object:nil]; |
| 555 | | |
| 556 | | if ([self window] != nil) |
| 557 | | { |
| 558 | | [[NSNotificationCenter defaultCenter] addObserver:self |
| 559 | | selector:@selector(windowDidBecomeKey:) |
| 560 | | name:NSWindowDidBecomeKeyNotification |
| 561 | | object:[self window]]; |
| 562 | | [[NSNotificationCenter defaultCenter] addObserver:self |
| 563 | | selector:@selector(windowDidResignKey:) |
| 564 | | name:NSWindowDidResignKeyNotification |
| 565 | | object:[self window]]; |
| 566 | | [self setNeedsDisplay:YES]; |
| 567 | | } |
| 568 | | } |
| 569 | | |
| 570 | | |
| 571 | | - (BOOL)isFlipped { |
| 572 | | return YES; |
| 573 | | } |
| 574 | | |
| 575 | | |
| 576 | | - (BOOL)isOpaque { |
| 577 | | return YES; |
| 578 | | } |
| 579 | | |
| 580 | | |
| 581 | | - (NSRect)adjustScroll:(NSRect)proposedVisibleRect { |
| 582 | | if (wholeLineScroll) |
| 583 | | { |
| 584 | | CGFloat const clamp = [self bounds].size.height - fontHeight - proposedVisibleRect.size.height; |
| 585 | | proposedVisibleRect.origin.y = MIN(proposedVisibleRect.origin.y, MAX(clamp, 0)); |
| 586 | | proposedVisibleRect.origin.y -= fmod(proposedVisibleRect.origin.y, fontHeight); |
| 587 | | } |
| 588 | | return proposedVisibleRect; |
| 589 | | } |
| 590 | | |
| 591 | | |
| 592 | | - (void)drawRect:(NSRect)dirtyRect { |
| 593 | | // work out what's available |
| 594 | | [self recomputeVisible]; |
| 595 | | debug_view_xy const origin = view->visible_position(); |
| 596 | | debug_view_xy const size = view->visible_size(); |
| 597 | | |
| 598 | | // work out how much we need to draw |
| 599 | | INT32 row, clip; |
| 600 | | [self convertBounds:dirtyRect toFirstAffectedLine:&row count:&clip]; |
| 601 | | clip += row; |
| 602 | | row = MAX(row, origin.y); |
| 603 | | clip = MIN(clip, origin.y + size.y); |
| 604 | | |
| 605 | | // this gets the text for the whole visible area |
| 606 | | debug_view_char const *data = view->viewdata(); |
| 607 | | if (!data) |
| 608 | | return; |
| 609 | | |
| 610 | | // clear any space above the available content |
| 611 | | data += ((row - origin.y) * size.x); |
| 612 | | if (dirtyRect.origin.y < (row * fontHeight)) |
| 613 | | { |
| 614 | | [DefaultBackground set]; |
| 615 | | [NSBezierPath fillRect:NSMakeRect(0, |
| 616 | | dirtyRect.origin.y, |
| 617 | | [self bounds].size.width, |
| 618 | | (row * fontHeight) - dirtyRect.origin.y)]; |
| 619 | | } |
| 620 | | |
| 621 | | // render entire lines to get character alignment right |
| 622 | | for ( ; row < clip; row++, data += size.x) |
| 623 | | { |
| 624 | | int attr = -1; |
| 625 | | NSUInteger start = 0, length = 0; |
| 626 | | for (UINT32 col = origin.x; col < origin.x + size.x; col++) |
| 627 | | { |
| 628 | | [[text mutableString] appendFormat:@"%c", data[col - origin.x].byte]; |
| 629 | | if ((start < length) && (attr != data[col - origin.x].attrib)) |
| 630 | | { |
| 631 | | NSRange const run = NSMakeRange(start, length - start); |
| 632 | | [text addAttribute:NSFontAttributeName |
| 633 | | value:font |
| 634 | | range:NSMakeRange(0, length)]; |
| 635 | | [text addAttribute:NSForegroundColorAttributeName |
| 636 | | value:[self foregroundForAttribute:attr] |
| 637 | | range:run]; |
| 638 | | NSRange const glyphs = [layoutManager glyphRangeForCharacterRange:run |
| 639 | | actualCharacterRange:NULL]; |
| 640 | | NSRect box = [layoutManager boundingRectForGlyphRange:glyphs |
| 641 | | inTextContainer:textContainer]; |
| 642 | | if (start == 0) |
| 643 | | { |
| 644 | | box.size.width += box.origin.x; |
| 645 | | box.origin.x = 0; |
| 646 | | } |
| 647 | | [[self backgroundForAttribute:attr] set]; |
| 648 | | [NSBezierPath fillRect:NSMakeRect(box.origin.x, |
| 649 | | row * fontHeight, |
| 650 | | box.size.width, |
| 651 | | fontHeight)]; |
| 652 | | start = length; |
| 653 | | } |
| 654 | | attr = data[col - origin.x].attrib; |
| 655 | | length = [text length]; |
| 656 | | } |
| 657 | | NSRange const run = NSMakeRange(start, length - start); |
| 658 | | [text addAttribute:NSFontAttributeName |
| 659 | | value:font |
| 660 | | range:NSMakeRange(0, length)]; |
| 661 | | [text addAttribute:NSForegroundColorAttributeName |
| 662 | | value:[self foregroundForAttribute:attr] |
| 663 | | range:run]; |
| 664 | | NSRange const glyphs = [layoutManager glyphRangeForCharacterRange:run |
| 665 | | actualCharacterRange:NULL]; |
| 666 | | NSRect box = [layoutManager boundingRectForGlyphRange:glyphs |
| 667 | | inTextContainer:textContainer]; |
| 668 | | if (start == 0) |
| 669 | | box.origin.x = 0; |
| 670 | | box.size.width = MAX([self bounds].size.width - box.origin.x, 0); |
| 671 | | [[self backgroundForAttribute:attr] set]; |
| 672 | | [NSBezierPath fillRect:NSMakeRect(box.origin.x, |
| 673 | | row * fontHeight, |
| 674 | | box.size.width, |
| 675 | | fontHeight)]; |
| 676 | | [layoutManager drawGlyphsForGlyphRange:[layoutManager glyphRangeForTextContainer:textContainer] |
| 677 | | atPoint:NSMakePoint(0, row * fontHeight)]; |
| 678 | | [text deleteCharactersInRange:NSMakeRange(0, length)]; |
| 679 | | } |
| 680 | | |
| 681 | | // clear any space below the available content |
| 682 | | if ((dirtyRect.origin.y + dirtyRect.size.height) > (row * fontHeight)) |
| 683 | | { |
| 684 | | [DefaultBackground set]; |
| 685 | | [NSBezierPath fillRect:NSMakeRect(0, |
| 686 | | row * fontHeight, |
| 687 | | [self bounds].size.width, |
| 688 | | (dirtyRect.origin.y + dirtyRect.size.height) - (row * fontHeight))]; |
| 689 | | } |
| 690 | | } |
| 691 | | |
| 692 | | |
| 693 | | - (void)mouseDown:(NSEvent *)event { |
| 694 | | NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; |
| 695 | | NSUInteger const modifiers = [event modifierFlags]; |
| 696 | | view->process_click(((modifiers & NSCommandKeyMask) && [[self window] isMainWindow]) ? DCK_RIGHT_CLICK |
| 697 | | : (modifiers & NSAlternateKeyMask) ? DCK_MIDDLE_CLICK |
| 698 | | : DCK_LEFT_CLICK, |
| 699 | | [self convertLocation:location]); |
| 700 | | } |
| 701 | | |
| 702 | | |
| 703 | | - (void)mouseDragged:(NSEvent *)event { |
| 704 | | [self autoscroll:event]; |
| 705 | | NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; |
| 706 | | NSUInteger const modifiers = [event modifierFlags]; |
| 707 | | if (view->cursor_supported() |
| 708 | | && !(modifiers & NSAlternateKeyMask) |
| 709 | | && (!(modifiers & NSCommandKeyMask) || ![[self window] isMainWindow])) |
| 710 | | { |
| 711 | | view->set_cursor_position([self convertLocation:location]); |
| 712 | | view->set_cursor_visible(true); |
| 713 | | [self setNeedsDisplay:YES]; |
| 714 | | } |
| 715 | | } |
| 716 | | |
| 717 | | |
| 718 | | - (void)rightMouseDown:(NSEvent *)event { |
| 719 | | NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; |
| 720 | | if (view->cursor_supported()) |
| 721 | | { |
| 722 | | view->set_cursor_position([self convertLocation:location]); |
| 723 | | view->set_cursor_visible(true); |
| 724 | | [self setNeedsDisplay:YES]; |
| 725 | | } |
| 726 | | [super rightMouseDown:event]; |
| 727 | | } |
| 728 | | |
| 729 | | |
| 730 | | - (void)keyDown:(NSEvent *)event { |
| 731 | | NSUInteger modifiers = [event modifierFlags]; |
| 732 | | NSString *str = [event charactersIgnoringModifiers]; |
| 733 | | |
| 734 | | if ([str length] == 1) |
| 735 | | { |
| 736 | | if (modifiers & NSNumericPadKeyMask) |
| 737 | | { |
| 738 | | switch ([str characterAtIndex:0]) |
| 739 | | { |
| 740 | | case NSUpArrowFunctionKey: |
| 741 | | if (modifiers & NSCommandKeyMask) |
| 742 | | view->process_char(DCH_CTRLHOME); |
| 743 | | else |
| 744 | | view->process_char(DCH_UP); |
| 745 | | return; |
| 746 | | case NSDownArrowFunctionKey: |
| 747 | | if (modifiers & NSCommandKeyMask) |
| 748 | | view->process_char(DCH_CTRLEND); |
| 749 | | else |
| 750 | | view->process_char(DCH_DOWN); |
| 751 | | return; |
| 752 | | case NSLeftArrowFunctionKey: |
| 753 | | if (modifiers & NSCommandKeyMask) |
| 754 | | [self typeCharacterAndScrollToCursor:DCH_HOME]; |
| 755 | | else if (modifiers & NSAlternateKeyMask) |
| 756 | | [self typeCharacterAndScrollToCursor:DCH_CTRLLEFT]; |
| 757 | | else |
| 758 | | [self typeCharacterAndScrollToCursor:DCH_LEFT]; |
| 759 | | return; |
| 760 | | case NSRightArrowFunctionKey: |
| 761 | | if (modifiers & NSCommandKeyMask) |
| 762 | | [self typeCharacterAndScrollToCursor:DCH_END]; |
| 763 | | else if (modifiers & NSAlternateKeyMask) |
| 764 | | [self typeCharacterAndScrollToCursor:DCH_CTRLRIGHT]; |
| 765 | | else |
| 766 | | [self typeCharacterAndScrollToCursor:DCH_RIGHT]; |
| 767 | | return; |
| 768 | | default: |
| 769 | | [self interpretKeyEvents:[NSArray arrayWithObject:event]]; |
| 770 | | return; |
| 771 | | } |
| 772 | | } |
| 773 | | else if (modifiers & NSFunctionKeyMask) |
| 774 | | { |
| 775 | | switch ([str characterAtIndex:0]) |
| 776 | | { |
| 777 | | case NSPageUpFunctionKey: |
| 778 | | if (modifiers & NSAlternateKeyMask) |
| 779 | | { |
| 780 | | view->process_char(DCH_PUP); |
| 781 | | return; |
| 782 | | } |
| 783 | | case NSPageDownFunctionKey: |
| 784 | | if (modifiers & NSAlternateKeyMask) |
| 785 | | { |
| 786 | | view->process_char(DCH_PDOWN); |
| 787 | | return; |
| 788 | | } |
| 789 | | default: |
| 790 | | ; |
| 791 | | } |
| 792 | | [super keyDown:event]; |
| 793 | | return; |
| 794 | | } |
| 795 | | } |
| 796 | | [self interpretKeyEvents:[NSArray arrayWithObject:event]]; |
| 797 | | } |
| 798 | | |
| 799 | | |
| 800 | | - (void)insertTab:(id)sender { |
| 801 | | if ([[self window] firstResponder] == self) |
| 802 | | [[self window] selectNextKeyView:self]; |
| 803 | | } |
| 804 | | |
| 805 | | |
| 806 | | - (void)insertBacktab:(id)sender { |
| 807 | | if ([[self window] firstResponder] == self) |
| 808 | | [[self window] selectPreviousKeyView:self]; |
| 809 | | } |
| 810 | | |
| 811 | | |
| 812 | | - (void)insertNewline:(id)sender { |
| 813 | | debug_cpu_get_visible_cpu(*machine)->debug()->single_step(); |
| 814 | | } |
| 815 | | |
| 816 | | |
| 817 | | - (void)insertText:(id)string { |
| 818 | | NSUInteger len; |
| 819 | | NSRange found; |
| 820 | | if ([string isKindOfClass:[NSAttributedString class]]) |
| 821 | | string = [string string]; |
| 822 | | for (len = [string length], found = NSMakeRange(0, 0); |
| 823 | | found.location < len; |
| 824 | | found.location += found.length) { |
| 825 | | found = [string rangeOfComposedCharacterSequenceAtIndex:found.location]; |
| 826 | | if (found.length == 1) { |
| 827 | | unichar ch = [string characterAtIndex:found.location]; |
| 828 | | if ((ch >= 32) && (ch < 127)) |
| 829 | | [self typeCharacterAndScrollToCursor:ch]; |
| 830 | | } |
| 831 | | } |
| 832 | | } |
| 833 | | |
| 834 | | |
| 835 | | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 836 | | SEL action = [item action]; |
| 837 | | |
| 838 | | if (action == @selector(paste:)) |
| 839 | | { |
| 840 | | NSPasteboard *const board = [NSPasteboard generalPasteboard]; |
| 841 | | return [board availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] != nil; |
| 842 | | } |
| 843 | | else |
| 844 | | { |
| 845 | | return YES; |
| 846 | | } |
| 847 | | } |
| 848 | | |
| 849 | | @end |
trunk/src/osd/modules/debugger/osx/debugview.mm
| r0 | r250165 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Vas Crabb |
| 3 | //============================================================ |
| 4 | // |
| 5 | // debugview.m - MacOS X Cocoa debug window handling |
| 6 | // |
| 7 | //============================================================ |
| 8 | |
| 9 | #import "debugview.h" |
| 10 | |
| 11 | #include "debug/debugcpu.h" |
| 12 | |
| 13 | #include "modules/lib/osdobj_common.h" |
| 14 | |
| 15 | #include <string.h> |
| 16 | |
| 17 | |
| 18 | static NSColor *DefaultForeground; |
| 19 | static NSColor *ChangedForeground; |
| 20 | static NSColor *InvalidForeground; |
| 21 | static NSColor *CommentForeground; |
| 22 | static NSColor *DisabledChangedForeground; |
| 23 | static NSColor *DisabledInvalidForeground; |
| 24 | static NSColor *DisabledCommentForeground; |
| 25 | |
| 26 | static NSColor *DefaultBackground; |
| 27 | static NSColor *VisitedBackground; |
| 28 | static NSColor *AncillaryBackground; |
| 29 | static NSColor *SelectedBackground; |
| 30 | static NSColor *CurrentBackground; |
| 31 | static NSColor *SelectedCurrentBackground; |
| 32 | static NSColor *InactiveSelectedBackground; |
| 33 | static NSColor *InactiveSelectedCurrentBackground; |
| 34 | |
| 35 | static NSCharacterSet *NonWhiteCharacters; |
| 36 | |
| 37 | |
| 38 | static void debugwin_view_update(debug_view &view, void *osdprivate) |
| 39 | { |
| 40 | NSAutoreleasePool *const pool = [[NSAutoreleasePool alloc] init]; |
| 41 | [(MAMEDebugView *)osdprivate update]; |
| 42 | [pool release]; |
| 43 | } |
| 44 | |
| 45 | |
| 46 | @implementation MAMEDebugView |
| 47 | |
| 48 | + (void)initialize { |
| 49 | DefaultForeground = [[NSColor colorWithCalibratedWhite:0.0 alpha:1.0] retain]; |
| 50 | ChangedForeground = [[NSColor colorWithCalibratedRed:0.875 green:0.0 blue:0.0 alpha:1.0] retain]; |
| 51 | InvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:1.0 alpha:1.0] retain]; |
| 52 | CommentForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.375 blue:0.0 alpha:1.0] retain]; |
| 53 | DisabledChangedForeground = [[NSColor colorWithCalibratedRed:0.5 green:0.125 blue:0.125 alpha:1.0] retain]; |
| 54 | DisabledInvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.5 alpha:1.0] retain]; |
| 55 | DisabledCommentForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.25 blue:0.0 alpha:1.0] retain]; |
| 56 | |
| 57 | DefaultBackground = [[NSColor colorWithCalibratedWhite:1.0 alpha:1.0] retain]; |
| 58 | VisitedBackground = [[NSColor colorWithCalibratedRed:0.75 green:1.0 blue:0.75 alpha:1.0] retain]; |
| 59 | AncillaryBackground = [[NSColor colorWithCalibratedWhite:0.75 alpha:1.0] retain]; |
| 60 | SelectedBackground = [[NSColor colorWithCalibratedRed:0.75 green:0.875 blue:1.0 alpha:1.0] retain]; |
| 61 | CurrentBackground = [[NSColor colorWithCalibratedRed:1.0 green:0.75 blue:0.75 alpha:1.0] retain]; |
| 62 | SelectedCurrentBackground = [[NSColor colorWithCalibratedRed:0.875 green:0.625 blue:0.875 alpha:1.0] retain]; |
| 63 | InactiveSelectedBackground = [[NSColor colorWithCalibratedWhite:0.875 alpha:1.0] retain]; |
| 64 | InactiveSelectedCurrentBackground = [[NSColor colorWithCalibratedRed:0.875 green:0.5 blue:0.625 alpha:1.0] retain]; |
| 65 | |
| 66 | NonWhiteCharacters = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet]; |
| 67 | } |
| 68 | |
| 69 | |
| 70 | - (NSColor *)foregroundForAttribute:(UINT8)attrib { |
| 71 | if (attrib & DCA_COMMENT) |
| 72 | return (attrib & DCA_DISABLED) ? DisabledCommentForeground : CommentForeground; |
| 73 | else if (attrib & DCA_INVALID) |
| 74 | return (attrib & DCA_DISABLED) ? DisabledInvalidForeground : InvalidForeground; |
| 75 | else if (attrib & DCA_CHANGED) |
| 76 | return (attrib & DCA_DISABLED) ? DisabledChangedForeground : ChangedForeground; |
| 77 | else |
| 78 | return DefaultForeground; |
| 79 | } |
| 80 | |
| 81 | |
| 82 | - (NSColor *)backgroundForAttribute:(UINT8)attrib { |
| 83 | BOOL const active = [[self window] isKeyWindow] && ([[self window] firstResponder] == self); |
| 84 | if ((attrib & DCA_SELECTED) && (attrib & DCA_CURRENT)) |
| 85 | return active ? SelectedCurrentBackground : InactiveSelectedCurrentBackground; |
| 86 | else if (attrib & DCA_CURRENT) |
| 87 | return CurrentBackground; |
| 88 | else if (attrib & DCA_SELECTED) |
| 89 | return active ? SelectedBackground : InactiveSelectedBackground; |
| 90 | else if (attrib & DCA_ANCILLARY) |
| 91 | return AncillaryBackground; |
| 92 | else if (attrib & DCA_VISITED) |
| 93 | return VisitedBackground; |
| 94 | else |
| 95 | return DefaultBackground; |
| 96 | } |
| 97 | |
| 98 | |
| 99 | - (debug_view_xy)convertLocation:(NSPoint)location { |
| 100 | debug_view_xy position; |
| 101 | |
| 102 | position.y = lround(floor(location.y / fontHeight)); |
| 103 | if (position.y < 0) |
| 104 | position.y = 0; |
| 105 | else if (position.y >= totalHeight) |
| 106 | position.y = totalHeight - 1; |
| 107 | |
| 108 | debug_view_xy const origin = view->visible_position(); |
| 109 | debug_view_xy const size = view->visible_size(); |
| 110 | debug_view_char const *data = view->viewdata(); |
| 111 | if (!data || (position.y < origin.y) || (position.y >= origin.y + size.y)) |
| 112 | { |
| 113 | // y coordinate outside visible area, x will be a guess |
| 114 | position.x = lround(floor((location.x - [textContainer lineFragmentPadding]) / fontWidth)); |
| 115 | } |
| 116 | else |
| 117 | { |
| 118 | data += ((position.y - view->visible_position().y) * view->visible_size().x); |
| 119 | int attr = -1; |
| 120 | NSUInteger start = 0, length = 0; |
| 121 | for (UINT32 col = origin.x; col < origin.x + size.x; col++) |
| 122 | { |
| 123 | [[text mutableString] appendFormat:@"%c", data[col - origin.x].byte]; |
| 124 | if ((start < length) && (attr != data[col - origin.x].attrib)) |
| 125 | { |
| 126 | NSRange const run = NSMakeRange(start, length - start); |
| 127 | [text addAttribute:NSFontAttributeName |
| 128 | value:font |
| 129 | range:NSMakeRange(0, length)]; |
| 130 | [text addAttribute:NSForegroundColorAttributeName |
| 131 | value:[self foregroundForAttribute:attr] |
| 132 | range:run]; |
| 133 | start = length; |
| 134 | } |
| 135 | attr = data[col - origin.x].attrib; |
| 136 | length = [text length]; |
| 137 | } |
| 138 | if (start < length) |
| 139 | { |
| 140 | NSRange const run = NSMakeRange(start, length - start); |
| 141 | [text addAttribute:NSFontAttributeName |
| 142 | value:font |
| 143 | range:NSMakeRange(0, length)]; |
| 144 | [text addAttribute:NSForegroundColorAttributeName |
| 145 | value:[self foregroundForAttribute:attr] |
| 146 | range:run]; |
| 147 | } |
| 148 | CGFloat fraction; |
| 149 | NSUInteger const glyph = [layoutManager glyphIndexForPoint:NSMakePoint(location.x, fontHeight / 2) |
| 150 | inTextContainer:textContainer |
| 151 | fractionOfDistanceThroughGlyph:&fraction]; |
| 152 | position.x = [layoutManager characterIndexForGlyphAtIndex:glyph]; // FIXME: assumes 1:1 character mapping |
| 153 | [text deleteCharactersInRange:NSMakeRange(0, length)]; |
| 154 | } |
| 155 | if (position.x < 0) |
| 156 | position.x = 0; |
| 157 | else if (position.x >= totalWidth) |
| 158 | position.x = totalWidth - 1; |
| 159 | |
| 160 | return position; |
| 161 | } |
| 162 | |
| 163 | |
| 164 | - (void)convertBounds:(NSRect)b toFirstAffectedLine:(INT32 *)f count:(INT32 *)c { |
| 165 | *f = lround(floor(b.origin.y / fontHeight)); |
| 166 | *c = lround(ceil((b.origin.y + b.size.height) / fontHeight)) - *f; |
| 167 | } |
| 168 | |
| 169 | |
| 170 | - (void)recomputeVisible { |
| 171 | // this gets all the lines that are at least partially visible |
| 172 | debug_view_xy origin(0, 0), size(totalWidth, totalHeight); |
| 173 | [self convertBounds:[self visibleRect] toFirstAffectedLine:&origin.y count:&size.y]; |
| 174 | size.y = MIN(size.y, totalHeight - origin.y); |
| 175 | |
| 176 | // tell the underlying view how much real estate is available |
| 177 | view->set_visible_size(size); |
| 178 | view->set_visible_position(origin); |
| 179 | originTop = origin.y; |
| 180 | } |
| 181 | |
| 182 | |
| 183 | - (void)typeCharacterAndScrollToCursor:(char)ch { |
| 184 | debug_view_xy const oldPos = view->cursor_position(); |
| 185 | view->process_char(ch); |
| 186 | if (view->cursor_supported() && view->cursor_visible()) |
| 187 | { |
| 188 | debug_view_xy const newPos = view->cursor_position(); |
| 189 | if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y)) |
| 190 | { |
| 191 | // FIXME - use proper font metrics |
| 192 | [self scrollRectToVisible:NSMakeRect((newPos.x * fontWidth) + [textContainer lineFragmentPadding], |
| 193 | newPos.y * fontHeight, |
| 194 | fontWidth, |
| 195 | fontHeight)]; |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | |
| 201 | - (void)adjustSizeAndRecomputeVisible { |
| 202 | NSSize const clip = [[[self enclosingScrollView] contentView] bounds].size; |
| 203 | NSSize content = NSMakeSize((fontWidth * totalWidth) + (2 * [textContainer lineFragmentPadding]), |
| 204 | fontHeight * totalHeight); |
| 205 | if (wholeLineScroll) |
| 206 | content.height += (fontHeight * 2) - 1; |
| 207 | [self setFrameSize:NSMakeSize(ceil(MAX(clip.width, content.width)), |
| 208 | ceil(MAX(clip.height, content.height)))]; |
| 209 | [self recomputeVisible]; |
| 210 | } |
| 211 | |
| 212 | |
| 213 | + (NSFont *)defaultFontForMachine:(running_machine &)m { |
| 214 | osd_options const &options = downcast<osd_options const &>(m.options()); |
| 215 | char const *const face = options.debugger_font(); |
| 216 | float const size = options.debugger_font_size(); |
| 217 | |
| 218 | NSFont *const result = (('\0' != *face) && (0 != strcmp(OSDOPTVAL_AUTO, face))) |
| 219 | ? [NSFont fontWithName:[NSString stringWithUTF8String:face] size:MAX(0, size)] |
| 220 | : nil; |
| 221 | |
| 222 | return (nil != result) ? result : [NSFont userFixedPitchFontOfSize:MAX(0, size)]; |
| 223 | } |
| 224 | |
| 225 | |
| 226 | - (id)initWithFrame:(NSRect)f type:(debug_view_type)t machine:(running_machine &)m wholeLineScroll:(BOOL)w { |
| 227 | if (!(self = [super initWithFrame:f])) |
| 228 | return nil; |
| 229 | type = t; |
| 230 | machine = &m; |
| 231 | view = machine->debug_view().alloc_view((debug_view_type)type, debugwin_view_update, self); |
| 232 | if (view == nil) { |
| 233 | [self release]; |
| 234 | return nil; |
| 235 | } |
| 236 | wholeLineScroll = w; |
| 237 | debug_view_xy const size = view->total_size(); |
| 238 | totalWidth = size.x; |
| 239 | totalHeight = size.y; |
| 240 | originTop = 0; |
| 241 | |
| 242 | text = [[NSTextStorage alloc] init]; |
| 243 | textContainer = [[NSTextContainer alloc] init]; |
| 244 | layoutManager = [[NSLayoutManager alloc] init]; |
| 245 | [layoutManager addTextContainer:textContainer]; |
| 246 | [textContainer release]; |
| 247 | [text addLayoutManager:layoutManager]; |
| 248 | [layoutManager release]; |
| 249 | |
| 250 | [self setFont:[[self class] defaultFontForMachine:m]]; |
| 251 | |
| 252 | NSMenu *contextMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Context"]; |
| 253 | [self addContextMenuItemsToMenu:contextMenu]; |
| 254 | [self setMenu:contextMenu]; |
| 255 | [contextMenu release]; |
| 256 | |
| 257 | return self; |
| 258 | } |
| 259 | |
| 260 | |
| 261 | - (void)dealloc { |
| 262 | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 263 | if (view != NULL) machine->debug_view().free_view(*view); |
| 264 | if (font != nil) [font release]; |
| 265 | if (text != nil) [text release]; |
| 266 | [super dealloc]; |
| 267 | } |
| 268 | |
| 269 | |
| 270 | - (void)update { |
| 271 | // resize our frame if the total size has changed |
| 272 | debug_view_xy const newSize = view->total_size(); |
| 273 | BOOL const resized = (newSize.x != totalWidth) || (newSize.y != totalHeight); |
| 274 | if (resized) |
| 275 | { |
| 276 | NSScrollView *const scroller = [self enclosingScrollView]; |
| 277 | if (scroller) |
| 278 | { |
| 279 | NSSize const clip = [[scroller contentView] bounds].size; |
| 280 | NSSize content = NSMakeSize((fontWidth * newSize.x) + (2 * [textContainer lineFragmentPadding]), |
| 281 | fontHeight * newSize.y); |
| 282 | if (wholeLineScroll) |
| 283 | content.height += (fontHeight * 2) - 1; |
| 284 | [self setFrameSize:NSMakeSize(ceil(MAX(clip.width, content.width)), |
| 285 | ceil(MAX(clip.height, content.height)))]; |
| 286 | } |
| 287 | totalWidth = newSize.x; |
| 288 | totalHeight = newSize.y; |
| 289 | } |
| 290 | |
| 291 | // scroll the view if we're being told to |
| 292 | debug_view_xy const newOrigin = view->visible_position(); |
| 293 | if (newOrigin.y != originTop) |
| 294 | { |
| 295 | NSRect const visible = [self visibleRect]; |
| 296 | NSPoint scroll = NSMakePoint(visible.origin.x, newOrigin.y * fontHeight); |
| 297 | [self scrollPoint:scroll]; |
| 298 | originTop = newOrigin.y; |
| 299 | } |
| 300 | |
| 301 | // mark as dirty |
| 302 | [self setNeedsDisplay:YES]; |
| 303 | } |
| 304 | |
| 305 | |
| 306 | - (NSSize)maximumFrameSize { |
| 307 | debug_view_xy const max = view->total_size(); |
| 308 | return NSMakeSize(ceil((max.x * fontWidth) + (2 * [textContainer lineFragmentPadding])), |
| 309 | ceil((max.y + (wholeLineScroll ? 1 : 0)) * fontHeight)); |
| 310 | } |
| 311 | |
| 312 | |
| 313 | - (NSFont *)font { |
| 314 | return [[font retain] autorelease]; |
| 315 | } |
| 316 | |
| 317 | |
| 318 | - (void)setFont:(NSFont *)f { |
| 319 | [font autorelease]; |
| 320 | font = [f retain]; |
| 321 | fontWidth = [font maximumAdvancement].width; |
| 322 | fontHeight = ceil([font ascender] - [font descender]); |
| 323 | fontAscent = [font ascender]; |
| 324 | [[self enclosingScrollView] setLineScroll:fontHeight]; |
| 325 | totalWidth = totalHeight = 0; |
| 326 | [self update]; |
| 327 | } |
| 328 | |
| 329 | |
| 330 | - (BOOL)cursorSupported { |
| 331 | return view->cursor_supported(); |
| 332 | } |
| 333 | |
| 334 | |
| 335 | - (BOOL)cursorVisible { |
| 336 | return view->cursor_visible(); |
| 337 | } |
| 338 | |
| 339 | |
| 340 | - (debug_view_xy)cursorPosition { |
| 341 | return view->cursor_position(); |
| 342 | } |
| 343 | |
| 344 | |
| 345 | - (IBAction)copyVisible:(id)sender { |
| 346 | debug_view_xy const size = view->visible_size(); |
| 347 | debug_view_char const *data = view->viewdata(); |
| 348 | if (!data) |
| 349 | { |
| 350 | NSBeep(); |
| 351 | return; |
| 352 | } |
| 353 | |
| 354 | for (UINT32 row = 0; row < size.y; row++, data += size.x) |
| 355 | { |
| 356 | // add content for the line and set colours |
| 357 | int attr = -1; |
| 358 | NSUInteger start = [text length], length = start; |
| 359 | for (UINT32 col = 0; col < size.x; col++) |
| 360 | { |
| 361 | [[text mutableString] appendFormat:@"%c", data[col].byte]; |
| 362 | if ((start < length) && (attr != (data[col].attrib & ~DCA_SELECTED))) |
| 363 | { |
| 364 | NSRange const run = NSMakeRange(start, length - start); |
| 365 | [text addAttribute:NSForegroundColorAttributeName |
| 366 | value:[self foregroundForAttribute:attr] |
| 367 | range:run]; |
| 368 | [text addAttribute:NSBackgroundColorAttributeName |
| 369 | value:[self backgroundForAttribute:attr] |
| 370 | range:run]; |
| 371 | start = length; |
| 372 | } |
| 373 | attr = data[col].attrib & ~DCA_SELECTED; |
| 374 | length = [text length]; |
| 375 | } |
| 376 | |
| 377 | // clean up trailing whitespace |
| 378 | NSRange trim = [[text string] rangeOfCharacterFromSet:NonWhiteCharacters |
| 379 | options:NSBackwardsSearch |
| 380 | range:NSMakeRange(start, length - start)]; |
| 381 | if (trim.location != NSNotFound) |
| 382 | trim = [[text string] rangeOfComposedCharacterSequenceAtIndex:(trim.location + trim.length - 1)]; |
| 383 | else if (start > 0) |
| 384 | trim = [[text string] rangeOfComposedCharacterSequenceAtIndex:(start - 1)]; |
| 385 | else |
| 386 | trim = NSMakeRange(start, 0); |
| 387 | trim.location += trim.length; |
| 388 | trim.length = length - trim.location; |
| 389 | [text deleteCharactersInRange:trim]; |
| 390 | |
| 391 | // add the line ending and set colours |
| 392 | [[text mutableString] appendString:@"\n"]; |
| 393 | NSRange const run = NSMakeRange(start, [text length] - start); |
| 394 | [text addAttribute:NSForegroundColorAttributeName |
| 395 | value:[self foregroundForAttribute:attr] |
| 396 | range:run]; |
| 397 | [text addAttribute:NSBackgroundColorAttributeName |
| 398 | value:[self backgroundForAttribute:attr] |
| 399 | range:run]; |
| 400 | } |
| 401 | |
| 402 | // set the font and send it to the pasteboard |
| 403 | NSRange const run = NSMakeRange(0, [text length]); |
| 404 | [text addAttribute:NSFontAttributeName value:font range:run]; |
| 405 | NSPasteboard *const board = [NSPasteboard generalPasteboard]; |
| 406 | [board declareTypes:[NSArray arrayWithObject:NSRTFPboardType] owner:nil]; |
| 407 | [board setData:[text RTFFromRange:run documentAttributes:[NSDictionary dictionary]] forType:NSRTFPboardType]; |
| 408 | [text deleteCharactersInRange:run]; |
| 409 | } |
| 410 | |
| 411 | |
| 412 | - (IBAction)paste:(id)sender { |
| 413 | NSPasteboard *const board = [NSPasteboard generalPasteboard]; |
| 414 | NSString *const avail = [board availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; |
| 415 | if (avail == nil) |
| 416 | { |
| 417 | NSBeep(); |
| 418 | return; |
| 419 | } |
| 420 | |
| 421 | NSData *const data = [[board stringForType:avail] dataUsingEncoding:NSASCIIStringEncoding |
| 422 | allowLossyConversion:YES]; |
| 423 | char const *const bytes = (char const *)[data bytes]; |
| 424 | debug_view_xy const oldPos = view->cursor_position(); |
| 425 | for (NSUInteger i = 0, l = [data length]; i < l; i++) |
| 426 | view->process_char(bytes[i]); |
| 427 | if (view->cursor_supported() && view->cursor_visible()) |
| 428 | { |
| 429 | debug_view_xy const newPos = view->cursor_position(); |
| 430 | if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y)) |
| 431 | { |
| 432 | // FIXME - use proper font metrics |
| 433 | [self scrollRectToVisible:NSMakeRect((newPos.x * fontWidth) + [textContainer lineFragmentPadding], |
| 434 | newPos.y * fontHeight, |
| 435 | fontWidth, |
| 436 | fontHeight)]; |
| 437 | } |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | |
| 442 | - (void)viewBoundsDidChange:(NSNotification *)notification { |
| 443 | NSView *const changed = [notification object]; |
| 444 | if (changed == [[self enclosingScrollView] contentView]) |
| 445 | [self adjustSizeAndRecomputeVisible]; |
| 446 | } |
| 447 | |
| 448 | |
| 449 | - (void)viewFrameDidChange:(NSNotification *)notification { |
| 450 | NSView *const changed = [notification object]; |
| 451 | if (changed == [[self enclosingScrollView] contentView]) |
| 452 | [self adjustSizeAndRecomputeVisible]; |
| 453 | } |
| 454 | |
| 455 | |
| 456 | - (void)windowDidBecomeKey:(NSNotification *)notification { |
| 457 | NSWindow *const win = [notification object]; |
| 458 | if ((win == [self window]) && ([win firstResponder] == self) && view->cursor_supported()) |
| 459 | [self setNeedsDisplay:YES]; |
| 460 | } |
| 461 | |
| 462 | |
| 463 | - (void)windowDidResignKey:(NSNotification *)notification { |
| 464 | NSWindow *const win = [notification object]; |
| 465 | if ((win == [self window]) && ([win firstResponder] == self) && view->cursor_supported()) |
| 466 | [self setNeedsDisplay:YES]; |
| 467 | } |
| 468 | |
| 469 | |
| 470 | - (void)addContextMenuItemsToMenu:(NSMenu *)menu { |
| 471 | NSMenuItem *item; |
| 472 | |
| 473 | item = [menu addItemWithTitle:@"Copy Visible" |
| 474 | action:@selector(copyVisible:) |
| 475 | keyEquivalent:@""]; |
| 476 | [item setTarget:self]; |
| 477 | |
| 478 | item = [menu addItemWithTitle:@"Paste" |
| 479 | action:@selector(paste:) |
| 480 | keyEquivalent:@""]; |
| 481 | [item setTarget:self]; |
| 482 | } |
| 483 | |
| 484 | |
| 485 | - (BOOL)acceptsFirstResponder { |
| 486 | return view->cursor_supported(); |
| 487 | } |
| 488 | |
| 489 | |
| 490 | - (BOOL)becomeFirstResponder { |
| 491 | if (view->cursor_supported()) |
| 492 | { |
| 493 | debug_view_xy pos; |
| 494 | view->set_cursor_visible(true); |
| 495 | pos = view->cursor_position(); |
| 496 | [self scrollRectToVisible:NSMakeRect((pos.x * fontWidth) + [textContainer lineFragmentPadding], |
| 497 | pos.y * fontHeight, |
| 498 | fontWidth, |
| 499 | fontHeight)]; // FIXME: metrics |
| 500 | [self setNeedsDisplay:YES]; |
| 501 | return [super becomeFirstResponder]; |
| 502 | } |
| 503 | else |
| 504 | { |
| 505 | return NO; |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | |
| 510 | - (BOOL)resignFirstResponder { |
| 511 | if (view->cursor_supported()) |
| 512 | [self setNeedsDisplay:YES]; |
| 513 | return [super resignFirstResponder]; |
| 514 | } |
| 515 | |
| 516 | |
| 517 | - (void)viewDidMoveToSuperview { |
| 518 | [super viewDidMoveToSuperview]; |
| 519 | |
| 520 | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 521 | name:NSViewBoundsDidChangeNotification |
| 522 | object:nil]; |
| 523 | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 524 | name:NSViewFrameDidChangeNotification |
| 525 | object:nil]; |
| 526 | |
| 527 | NSScrollView *const scroller = [self enclosingScrollView]; |
| 528 | if (scroller != nil) |
| 529 | { |
| 530 | [scroller setLineScroll:fontHeight]; |
| 531 | [[scroller contentView] setPostsBoundsChangedNotifications:YES]; |
| 532 | [[scroller contentView] setPostsFrameChangedNotifications:YES]; |
| 533 | [[NSNotificationCenter defaultCenter] addObserver:self |
| 534 | selector:@selector(viewBoundsDidChange:) |
| 535 | name:NSViewBoundsDidChangeNotification |
| 536 | object:[scroller contentView]]; |
| 537 | [[NSNotificationCenter defaultCenter] addObserver:self |
| 538 | selector:@selector(viewFrameDidChange:) |
| 539 | name:NSViewFrameDidChangeNotification |
| 540 | object:[scroller contentView]]; |
| 541 | [self adjustSizeAndRecomputeVisible]; |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | |
| 546 | - (void)viewDidMoveToWindow { |
| 547 | [super viewDidMoveToWindow]; |
| 548 | |
| 549 | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 550 | name:NSWindowDidBecomeKeyNotification |
| 551 | object:nil]; |
| 552 | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 553 | name:NSWindowDidResignKeyNotification |
| 554 | object:nil]; |
| 555 | |
| 556 | if ([self window] != nil) |
| 557 | { |
| 558 | [[NSNotificationCenter defaultCenter] addObserver:self |
| 559 | selector:@selector(windowDidBecomeKey:) |
| 560 | name:NSWindowDidBecomeKeyNotification |
| 561 | object:[self window]]; |
| 562 | [[NSNotificationCenter defaultCenter] addObserver:self |
| 563 | selector:@selector(windowDidResignKey:) |
| 564 | name:NSWindowDidResignKeyNotification |
| 565 | object:[self window]]; |
| 566 | [self setNeedsDisplay:YES]; |
| 567 | } |
| 568 | } |
| 569 | |
| 570 | |
| 571 | - (BOOL)isFlipped { |
| 572 | return YES; |
| 573 | } |
| 574 | |
| 575 | |
| 576 | - (BOOL)isOpaque { |
| 577 | return YES; |
| 578 | } |
| 579 | |
| 580 | |
| 581 | - (NSRect)adjustScroll:(NSRect)proposedVisibleRect { |
| 582 | if (wholeLineScroll) |
| 583 | { |
| 584 | CGFloat const clamp = [self bounds].size.height - fontHeight - proposedVisibleRect.size.height; |
| 585 | proposedVisibleRect.origin.y = MIN(proposedVisibleRect.origin.y, MAX(clamp, 0)); |
| 586 | proposedVisibleRect.origin.y -= fmod(proposedVisibleRect.origin.y, fontHeight); |
| 587 | } |
| 588 | return proposedVisibleRect; |
| 589 | } |
| 590 | |
| 591 | |
| 592 | - (void)drawRect:(NSRect)dirtyRect { |
| 593 | // work out what's available |
| 594 | [self recomputeVisible]; |
| 595 | debug_view_xy const origin = view->visible_position(); |
| 596 | debug_view_xy const size = view->visible_size(); |
| 597 | |
| 598 | // work out how much we need to draw |
| 599 | INT32 row, clip; |
| 600 | [self convertBounds:dirtyRect toFirstAffectedLine:&row count:&clip]; |
| 601 | clip += row; |
| 602 | row = MAX(row, origin.y); |
| 603 | clip = MIN(clip, origin.y + size.y); |
| 604 | |
| 605 | // this gets the text for the whole visible area |
| 606 | debug_view_char const *data = view->viewdata(); |
| 607 | if (!data) |
| 608 | return; |
| 609 | |
| 610 | // clear any space above the available content |
| 611 | data += ((row - origin.y) * size.x); |
| 612 | if (dirtyRect.origin.y < (row * fontHeight)) |
| 613 | { |
| 614 | [DefaultBackground set]; |
| 615 | [NSBezierPath fillRect:NSMakeRect(0, |
| 616 | dirtyRect.origin.y, |
| 617 | [self bounds].size.width, |
| 618 | (row * fontHeight) - dirtyRect.origin.y)]; |
| 619 | } |
| 620 | |
| 621 | // render entire lines to get character alignment right |
| 622 | for ( ; row < clip; row++, data += size.x) |
| 623 | { |
| 624 | int attr = -1; |
| 625 | NSUInteger start = 0, length = 0; |
| 626 | for (UINT32 col = origin.x; col < origin.x + size.x; col++) |
| 627 | { |
| 628 | [[text mutableString] appendFormat:@"%c", data[col - origin.x].byte]; |
| 629 | if ((start < length) && (attr != data[col - origin.x].attrib)) |
| 630 | { |
| 631 | NSRange const run = NSMakeRange(start, length - start); |
| 632 | [text addAttribute:NSFontAttributeName |
| 633 | value:font |
| 634 | range:NSMakeRange(0, length)]; |
| 635 | [text addAttribute:NSForegroundColorAttributeName |
| 636 | value:[self foregroundForAttribute:attr] |
| 637 | range:run]; |
| 638 | NSRange const glyphs = [layoutManager glyphRangeForCharacterRange:run |
| 639 | actualCharacterRange:NULL]; |
| 640 | NSRect box = [layoutManager boundingRectForGlyphRange:glyphs |
| 641 | inTextContainer:textContainer]; |
| 642 | if (start == 0) |
| 643 | { |
| 644 | box.size.width += box.origin.x; |
| 645 | box.origin.x = 0; |
| 646 | } |
| 647 | [[self backgroundForAttribute:attr] set]; |
| 648 | [NSBezierPath fillRect:NSMakeRect(box.origin.x, |
| 649 | row * fontHeight, |
| 650 | box.size.width, |
| 651 | fontHeight)]; |
| 652 | start = length; |
| 653 | } |
| 654 | attr = data[col - origin.x].attrib; |
| 655 | length = [text length]; |
| 656 | } |
| 657 | NSRange const run = NSMakeRange(start, length - start); |
| 658 | [text addAttribute:NSFontAttributeName |
| 659 | value:font |
| 660 | range:NSMakeRange(0, length)]; |
| 661 | [text addAttribute:NSForegroundColorAttributeName |
| 662 | value:[self foregroundForAttribute:attr] |
| 663 | range:run]; |
| 664 | NSRange const glyphs = [layoutManager glyphRangeForCharacterRange:run |
| 665 | actualCharacterRange:NULL]; |
| 666 | NSRect box = [layoutManager boundingRectForGlyphRange:glyphs |
| 667 | inTextContainer:textContainer]; |
| 668 | if (start == 0) |
| 669 | box.origin.x = 0; |
| 670 | box.size.width = MAX([self bounds].size.width - box.origin.x, 0); |
| 671 | [[self backgroundForAttribute:attr] set]; |
| 672 | [NSBezierPath fillRect:NSMakeRect(box.origin.x, |
| 673 | row * fontHeight, |
| 674 | box.size.width, |
| 675 | fontHeight)]; |
| 676 | [layoutManager drawGlyphsForGlyphRange:[layoutManager glyphRangeForTextContainer:textContainer] |
| 677 | atPoint:NSMakePoint(0, row * fontHeight)]; |
| 678 | [text deleteCharactersInRange:NSMakeRange(0, length)]; |
| 679 | } |
| 680 | |
| 681 | // clear any space below the available content |
| 682 | if ((dirtyRect.origin.y + dirtyRect.size.height) > (row * fontHeight)) |
| 683 | { |
| 684 | [DefaultBackground set]; |
| 685 | [NSBezierPath fillRect:NSMakeRect(0, |
| 686 | row * fontHeight, |
| 687 | [self bounds].size.width, |
| 688 | (dirtyRect.origin.y + dirtyRect.size.height) - (row * fontHeight))]; |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | |
| 693 | - (void)mouseDown:(NSEvent *)event { |
| 694 | NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; |
| 695 | NSUInteger const modifiers = [event modifierFlags]; |
| 696 | view->process_click(((modifiers & NSCommandKeyMask) && [[self window] isMainWindow]) ? DCK_RIGHT_CLICK |
| 697 | : (modifiers & NSAlternateKeyMask) ? DCK_MIDDLE_CLICK |
| 698 | : DCK_LEFT_CLICK, |
| 699 | [self convertLocation:location]); |
| 700 | } |
| 701 | |
| 702 | |
| 703 | - (void)mouseDragged:(NSEvent *)event { |
| 704 | [self autoscroll:event]; |
| 705 | NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; |
| 706 | NSUInteger const modifiers = [event modifierFlags]; |
| 707 | if (view->cursor_supported() |
| 708 | && !(modifiers & NSAlternateKeyMask) |
| 709 | && (!(modifiers & NSCommandKeyMask) || ![[self window] isMainWindow])) |
| 710 | { |
| 711 | view->set_cursor_position([self convertLocation:location]); |
| 712 | view->set_cursor_visible(true); |
| 713 | [self setNeedsDisplay:YES]; |
| 714 | } |
| 715 | } |
| 716 | |
| 717 | |
| 718 | - (void)rightMouseDown:(NSEvent *)event { |
| 719 | NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; |
| 720 | if (view->cursor_supported()) |
| 721 | { |
| 722 | view->set_cursor_position([self convertLocation:location]); |
| 723 | view->set_cursor_visible(true); |
| 724 | [self setNeedsDisplay:YES]; |
| 725 | } |
| 726 | [super rightMouseDown:event]; |
| 727 | } |
| 728 | |
| 729 | |
| 730 | - (void)keyDown:(NSEvent *)event { |
| 731 | NSUInteger modifiers = [event modifierFlags]; |
| 732 | NSString *str = [event charactersIgnoringModifiers]; |
| 733 | |
| 734 | if ([str length] == 1) |
| 735 | { |
| 736 | if (modifiers & NSNumericPadKeyMask) |
| 737 | { |
| 738 | switch ([str characterAtIndex:0]) |
| 739 | { |
| 740 | case NSUpArrowFunctionKey: |
| 741 | if (modifiers & NSCommandKeyMask) |
| 742 | view->process_char(DCH_CTRLHOME); |
| 743 | else |
| 744 | view->process_char(DCH_UP); |
| 745 | return; |
| 746 | case NSDownArrowFunctionKey: |
| 747 | if (modifiers & NSCommandKeyMask) |
| 748 | view->process_char(DCH_CTRLEND); |
| 749 | else |
| 750 | view->process_char(DCH_DOWN); |
| 751 | return; |
| 752 | case NSLeftArrowFunctionKey: |
| 753 | if (modifiers & NSCommandKeyMask) |
| 754 | [self typeCharacterAndScrollToCursor:DCH_HOME]; |
| 755 | else if (modifiers & NSAlternateKeyMask) |
| 756 | [self typeCharacterAndScrollToCursor:DCH_CTRLLEFT]; |
| 757 | else |
| 758 | [self typeCharacterAndScrollToCursor:DCH_LEFT]; |
| 759 | return; |
| 760 | case NSRightArrowFunctionKey: |
| 761 | if (modifiers & NSCommandKeyMask) |
| 762 | [self typeCharacterAndScrollToCursor:DCH_END]; |
| 763 | else if (modifiers & NSAlternateKeyMask) |
| 764 | [self typeCharacterAndScrollToCursor:DCH_CTRLRIGHT]; |
| 765 | else |
| 766 | [self typeCharacterAndScrollToCursor:DCH_RIGHT]; |
| 767 | return; |
| 768 | default: |
| 769 | [self interpretKeyEvents:[NSArray arrayWithObject:event]]; |
| 770 | return; |
| 771 | } |
| 772 | } |
| 773 | else if (modifiers & NSFunctionKeyMask) |
| 774 | { |
| 775 | switch ([str characterAtIndex:0]) |
| 776 | { |
| 777 | case NSPageUpFunctionKey: |
| 778 | if (modifiers & NSAlternateKeyMask) |
| 779 | { |
| 780 | view->process_char(DCH_PUP); |
| 781 | return; |
| 782 | } |
| 783 | case NSPageDownFunctionKey: |
| 784 | if (modifiers & NSAlternateKeyMask) |
| 785 | { |
| 786 | view->process_char(DCH_PDOWN); |
| 787 | return; |
| 788 | } |
| 789 | default: |
| 790 | ; |
| 791 | } |
| 792 | [super keyDown:event]; |
| 793 | return; |
| 794 | } |
| 795 | } |
| 796 | [self interpretKeyEvents:[NSArray arrayWithObject:event]]; |
| 797 | } |
| 798 | |
| 799 | |
| 800 | - (void)insertTab:(id)sender { |
| 801 | if ([[self window] firstResponder] == self) |
| 802 | [[self window] selectNextKeyView:self]; |
| 803 | } |
| 804 | |
| 805 | |
| 806 | - (void)insertBacktab:(id)sender { |
| 807 | if ([[self window] firstResponder] == self) |
| 808 | [[self window] selectPreviousKeyView:self]; |
| 809 | } |
| 810 | |
| 811 | |
| 812 | - (void)insertNewline:(id)sender { |
| 813 | debug_cpu_get_visible_cpu(*machine)->debug()->single_step(); |
| 814 | } |
| 815 | |
| 816 | |
| 817 | - (void)insertText:(id)string { |
| 818 | NSUInteger len; |
| 819 | NSRange found; |
| 820 | if ([string isKindOfClass:[NSAttributedString class]]) |
| 821 | string = [string string]; |
| 822 | for (len = [string length], found = NSMakeRange(0, 0); |
| 823 | found.location < len; |
| 824 | found.location += found.length) { |
| 825 | found = [string rangeOfComposedCharacterSequenceAtIndex:found.location]; |
| 826 | if (found.length == 1) { |
| 827 | unichar ch = [string characterAtIndex:found.location]; |
| 828 | if ((ch >= 32) && (ch < 127)) |
| 829 | [self typeCharacterAndScrollToCursor:ch]; |
| 830 | } |
| 831 | } |
| 832 | } |
| 833 | |
| 834 | |
| 835 | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 836 | SEL action = [item action]; |
| 837 | |
| 838 | if (action == @selector(paste:)) |
| 839 | { |
| 840 | NSPasteboard *const board = [NSPasteboard generalPasteboard]; |
| 841 | return [board availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] != nil; |
| 842 | } |
| 843 | else |
| 844 | { |
| 845 | return YES; |
| 846 | } |
| 847 | } |
| 848 | |
| 849 | @end |
trunk/src/osd/modules/debugger/osx/debugwindowhandler.m
| r250164 | r250165 | |
| 1 | | // license:BSD-3-Clause |
| 2 | | // copyright-holders:Vas Crabb |
| 3 | | //============================================================ |
| 4 | | // |
| 5 | | // debugwindowhandler.m - MacOS X Cocoa debug window handling |
| 6 | | // |
| 7 | | //============================================================ |
| 8 | | |
| 9 | | #import "debugwindowhandler.h" |
| 10 | | |
| 11 | | #import "debugconsole.h" |
| 12 | | #import "debugcommandhistory.h" |
| 13 | | #import "debugview.h" |
| 14 | | |
| 15 | | |
| 16 | | //============================================================ |
| 17 | | // NOTIFICATIONS |
| 18 | | //============================================================ |
| 19 | | |
| 20 | | NSString *const MAMEHideDebuggerNotification = @"MAMEHideDebuggerNotification"; |
| 21 | | NSString *const MAMEShowDebuggerNotification = @"MAMEShowDebuggerNotification"; |
| 22 | | NSString *const MAMEAuxiliaryDebugWindowWillCloseNotification = @"MAMEAuxiliaryDebugWindowWillCloseNotification"; |
| 23 | | |
| 24 | | |
| 25 | | //============================================================ |
| 26 | | // MAMEDebugWindowHandler class |
| 27 | | //============================================================ |
| 28 | | |
| 29 | | @implementation MAMEDebugWindowHandler |
| 30 | | |
| 31 | | + (void)addCommonActionItems:(NSMenu *)menu { |
| 32 | | NSMenuItem *runParentItem = [menu addItemWithTitle:@"Run" |
| 33 | | action:@selector(debugRun:) |
| 34 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF5FunctionKey]]; |
| 35 | | NSMenu *runMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Run"]; |
| 36 | | [runParentItem setSubmenu:runMenu]; |
| 37 | | [runMenu release]; |
| 38 | | [runParentItem setKeyEquivalentModifierMask:0]; |
| 39 | | [[runMenu addItemWithTitle:@"and Hide Debugger" |
| 40 | | action:@selector(debugRunAndHide:) |
| 41 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF12FunctionKey]] |
| 42 | | setKeyEquivalentModifierMask:0]; |
| 43 | | [[runMenu addItemWithTitle:@"to Next CPU" |
| 44 | | action:@selector(debugRunToNextCPU:) |
| 45 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF6FunctionKey]] |
| 46 | | setKeyEquivalentModifierMask:0]; |
| 47 | | [[runMenu addItemWithTitle:@"until Next Interrupt on Current CPU" |
| 48 | | action:@selector(debugRunToNextInterrupt:) |
| 49 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF7FunctionKey]] |
| 50 | | setKeyEquivalentModifierMask:0]; |
| 51 | | [[runMenu addItemWithTitle:@"until Next VBLANK" |
| 52 | | action:@selector(debugRunToNextVBLANK:) |
| 53 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF8FunctionKey]] |
| 54 | | setKeyEquivalentModifierMask:0]; |
| 55 | | |
| 56 | | NSMenuItem *stepParentItem = [menu addItemWithTitle:@"Step" action:NULL keyEquivalent:@""]; |
| 57 | | NSMenu *stepMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Step"]; |
| 58 | | [stepParentItem setSubmenu:stepMenu]; |
| 59 | | [stepMenu release]; |
| 60 | | [[stepMenu addItemWithTitle:@"Into" |
| 61 | | action:@selector(debugStepInto:) |
| 62 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF11FunctionKey]] |
| 63 | | setKeyEquivalentModifierMask:0]; |
| 64 | | [[stepMenu addItemWithTitle:@"Over" |
| 65 | | action:@selector(debugStepOver:) |
| 66 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]] |
| 67 | | setKeyEquivalentModifierMask:0]; |
| 68 | | [[stepMenu addItemWithTitle:@"Out" |
| 69 | | action:@selector(debugStepOut:) |
| 70 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]] |
| 71 | | setKeyEquivalentModifierMask:NSShiftKeyMask]; |
| 72 | | |
| 73 | | NSMenuItem *resetParentItem = [menu addItemWithTitle:@"Reset" action:NULL keyEquivalent:@""]; |
| 74 | | NSMenu *resetMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Reset"]; |
| 75 | | [resetParentItem setSubmenu:resetMenu]; |
| 76 | | [resetMenu release]; |
| 77 | | [[resetMenu addItemWithTitle:@"Soft" |
| 78 | | action:@selector(debugSoftReset:) |
| 79 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]] |
| 80 | | setKeyEquivalentModifierMask:0]; |
| 81 | | [[resetMenu addItemWithTitle:@"Hard" |
| 82 | | action:@selector(debugHardReset:) |
| 83 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]] |
| 84 | | setKeyEquivalentModifierMask:NSShiftKeyMask]; |
| 85 | | |
| 86 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 87 | | |
| 88 | | NSMenuItem *newParentItem = [menu addItemWithTitle:@"New" action:NULL keyEquivalent:@""]; |
| 89 | | NSMenu *newMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"New"]; |
| 90 | | [newParentItem setSubmenu:newMenu]; |
| 91 | | [newMenu release]; |
| 92 | | [newMenu addItemWithTitle:@"Memory Window" |
| 93 | | action:@selector(debugNewMemoryWindow:) |
| 94 | | keyEquivalent:@"d"]; |
| 95 | | [newMenu addItemWithTitle:@"Disassembly Window" |
| 96 | | action:@selector(debugNewDisassemblyWindow:) |
| 97 | | keyEquivalent:@"a"]; |
| 98 | | [newMenu addItemWithTitle:@"Error Log Window" |
| 99 | | action:@selector(debugNewErrorLogWindow:) |
| 100 | | keyEquivalent:@"l"]; |
| 101 | | [newMenu addItemWithTitle:@"(Break|Watch)points Window" |
| 102 | | action:@selector(debugNewPointsWindow:) |
| 103 | | keyEquivalent:@"b"]; |
| 104 | | [newMenu addItemWithTitle:@"Devices Window" |
| 105 | | action:@selector(debugNewDevicesWindow:) |
| 106 | | keyEquivalent:@"D"]; |
| 107 | | |
| 108 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 109 | | |
| 110 | | [menu addItemWithTitle:@"Close Window" action:@selector(performClose:) keyEquivalent:@"w"]; |
| 111 | | [menu addItemWithTitle:@"Quit" action:@selector(debugExit:) keyEquivalent:@"q"]; |
| 112 | | } |
| 113 | | |
| 114 | | |
| 115 | | + (NSPopUpButton *)newActionButtonWithFrame:(NSRect)frame { |
| 116 | | NSPopUpButton *actionButton = [[NSPopUpButton alloc] initWithFrame:frame pullsDown:YES]; |
| 117 | | [actionButton setTitle:@""]; |
| 118 | | [actionButton addItemWithTitle:@""]; |
| 119 | | [actionButton setBezelStyle:NSShadowlessSquareBezelStyle]; |
| 120 | | [actionButton setFocusRingType:NSFocusRingTypeNone]; |
| 121 | | [[actionButton cell] setArrowPosition:NSPopUpArrowAtCenter]; |
| 122 | | [[self class] addCommonActionItems:[actionButton menu]]; |
| 123 | | return actionButton; |
| 124 | | } |
| 125 | | |
| 126 | | |
| 127 | | + (device_debug::breakpoint *)findBreakpointAtAddress:(offs_t)address forDevice:(device_t &)device { |
| 128 | | device_debug *const cpuinfo = device.debug(); |
| 129 | | device_debug::breakpoint *bp = cpuinfo->breakpoint_first(); |
| 130 | | while ((bp != NULL) && (address != bp->address())) bp = bp->next(); |
| 131 | | return bp; |
| 132 | | } |
| 133 | | |
| 134 | | |
| 135 | | - (id)initWithMachine:(running_machine &)m title:(NSString *)t { |
| 136 | | if (!(self = [super init])) |
| 137 | | return nil; |
| 138 | | |
| 139 | | window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 320, 240) |
| 140 | | styleMask:(NSTitledWindowMask | |
| 141 | | NSClosableWindowMask | |
| 142 | | NSMiniaturizableWindowMask | |
| 143 | | NSResizableWindowMask) |
| 144 | | backing:NSBackingStoreBuffered |
| 145 | | defer:YES]; |
| 146 | | [window setReleasedWhenClosed:NO]; |
| 147 | | [window setDelegate:self]; |
| 148 | | [window setTitle:t]; |
| 149 | | [window setContentMinSize:NSMakeSize(320, 240)]; |
| 150 | | |
| 151 | | [[NSNotificationCenter defaultCenter] addObserver:self |
| 152 | | selector:@selector(showDebugger:) |
| 153 | | name:MAMEShowDebuggerNotification |
| 154 | | object:nil]; |
| 155 | | [[NSNotificationCenter defaultCenter] addObserver:self |
| 156 | | selector:@selector(hideDebugger:) |
| 157 | | name:MAMEHideDebuggerNotification |
| 158 | | object:nil]; |
| 159 | | |
| 160 | | machine = &m; |
| 161 | | |
| 162 | | return self; |
| 163 | | } |
| 164 | | |
| 165 | | |
| 166 | | - (void)dealloc { |
| 167 | | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 168 | | |
| 169 | | if (window != nil) |
| 170 | | { |
| 171 | | [window orderOut:self]; |
| 172 | | [window release]; |
| 173 | | } |
| 174 | | |
| 175 | | [super dealloc]; |
| 176 | | } |
| 177 | | |
| 178 | | |
| 179 | | - (void)activate { |
| 180 | | [window makeKeyAndOrderFront:self]; |
| 181 | | } |
| 182 | | |
| 183 | | |
| 184 | | - (IBAction)debugRun:(id)sender { |
| 185 | | debug_cpu_get_visible_cpu(*machine)->debug()->go(); |
| 186 | | } |
| 187 | | |
| 188 | | |
| 189 | | - (IBAction)debugRunAndHide:(id)sender { |
| 190 | | [[NSNotificationCenter defaultCenter] postNotificationName:MAMEHideDebuggerNotification object:self]; |
| 191 | | debug_cpu_get_visible_cpu(*machine)->debug()->go(); |
| 192 | | } |
| 193 | | |
| 194 | | |
| 195 | | - (IBAction)debugRunToNextCPU:(id)sender { |
| 196 | | debug_cpu_get_visible_cpu(*machine)->debug()->go_next_device(); |
| 197 | | } |
| 198 | | |
| 199 | | |
| 200 | | - (IBAction)debugRunToNextInterrupt:(id)sender { |
| 201 | | debug_cpu_get_visible_cpu(*machine)->debug()->go_interrupt(); |
| 202 | | } |
| 203 | | |
| 204 | | |
| 205 | | - (IBAction)debugRunToNextVBLANK:(id)sender { |
| 206 | | debug_cpu_get_visible_cpu(*machine)->debug()->go_vblank(); |
| 207 | | } |
| 208 | | |
| 209 | | |
| 210 | | - (IBAction)debugStepInto:(id)sender { |
| 211 | | debug_cpu_get_visible_cpu(*machine)->debug()->single_step(); |
| 212 | | } |
| 213 | | |
| 214 | | |
| 215 | | - (IBAction)debugStepOver:(id)sender { |
| 216 | | debug_cpu_get_visible_cpu(*machine)->debug()->single_step_over(); |
| 217 | | } |
| 218 | | |
| 219 | | |
| 220 | | - (IBAction)debugStepOut:(id)sender { |
| 221 | | debug_cpu_get_visible_cpu(*machine)->debug()->single_step_out(); |
| 222 | | } |
| 223 | | |
| 224 | | |
| 225 | | - (IBAction)debugSoftReset:(id)sender { |
| 226 | | machine->schedule_soft_reset(); |
| 227 | | debug_cpu_get_visible_cpu(*machine)->debug()->go(); |
| 228 | | } |
| 229 | | |
| 230 | | |
| 231 | | - (IBAction)debugHardReset:(id)sender { |
| 232 | | machine->schedule_hard_reset(); |
| 233 | | } |
| 234 | | |
| 235 | | |
| 236 | | - (IBAction)debugExit:(id)sender { |
| 237 | | machine->schedule_exit(); |
| 238 | | } |
| 239 | | |
| 240 | | |
| 241 | | - (void)showDebugger:(NSNotification *)notification { |
| 242 | | running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue]; |
| 243 | | if (m == machine) |
| 244 | | { |
| 245 | | if (![window isVisible] && ![window isMiniaturized]) |
| 246 | | [window orderFront:self]; |
| 247 | | } |
| 248 | | } |
| 249 | | |
| 250 | | |
| 251 | | - (void)hideDebugger:(NSNotification *)notification { |
| 252 | | running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue]; |
| 253 | | if (m == machine) |
| 254 | | [window orderOut:self]; |
| 255 | | } |
| 256 | | |
| 257 | | @end |
| 258 | | |
| 259 | | |
| 260 | | //============================================================ |
| 261 | | // MAMEAuxiliaryDebugWindowHandler class |
| 262 | | //============================================================ |
| 263 | | |
| 264 | | @implementation MAMEAuxiliaryDebugWindowHandler |
| 265 | | |
| 266 | | + (void)cascadeWindow:(NSWindow *)window { |
| 267 | | static NSPoint lastPosition = { 0, 0 }; |
| 268 | | if (NSEqualPoints(lastPosition, NSZeroPoint)) { |
| 269 | | NSRect available = [[NSScreen mainScreen] visibleFrame]; |
| 270 | | lastPosition = NSMakePoint(available.origin.x + 12, available.origin.y + available.size.height - 8); |
| 271 | | } |
| 272 | | lastPosition = [window cascadeTopLeftFromPoint:lastPosition]; |
| 273 | | } |
| 274 | | |
| 275 | | |
| 276 | | - (id)initWithMachine:(running_machine &)m title:(NSString *)t console:(MAMEDebugConsole *)c { |
| 277 | | if (!(self = [super initWithMachine:m title:t])) |
| 278 | | return nil; |
| 279 | | console = c; |
| 280 | | return self; |
| 281 | | } |
| 282 | | |
| 283 | | |
| 284 | | - (void)dealloc { |
| 285 | | [super dealloc]; |
| 286 | | } |
| 287 | | |
| 288 | | |
| 289 | | - (IBAction)debugNewMemoryWindow:(id)sender { |
| 290 | | [console debugNewMemoryWindow:sender]; |
| 291 | | } |
| 292 | | |
| 293 | | |
| 294 | | - (IBAction)debugNewDisassemblyWindow:(id)sender { |
| 295 | | [console debugNewDisassemblyWindow:sender]; |
| 296 | | } |
| 297 | | |
| 298 | | |
| 299 | | - (IBAction)debugNewErrorLogWindow:(id)sender { |
| 300 | | [console debugNewErrorLogWindow:sender]; |
| 301 | | } |
| 302 | | |
| 303 | | |
| 304 | | - (IBAction)debugNewPointsWindow:(id)sender { |
| 305 | | [console debugNewPointsWindow:sender]; |
| 306 | | } |
| 307 | | |
| 308 | | |
| 309 | | - (IBAction)debugNewDevicesWindow:(id)sender { |
| 310 | | [console debugNewDevicesWindow:sender]; |
| 311 | | } |
| 312 | | |
| 313 | | |
| 314 | | - (void)windowWillClose:(NSNotification *)notification { |
| 315 | | [[NSNotificationCenter defaultCenter] postNotificationName:MAMEAuxiliaryDebugWindowWillCloseNotification |
| 316 | | object:self]; |
| 317 | | } |
| 318 | | |
| 319 | | - (void)cascadeWindowWithDesiredSize:(NSSize)desired forView:(NSView *)view { |
| 320 | | // convert desired size to an adjustment and apply it to the current window frame |
| 321 | | NSSize const current = [view frame].size; |
| 322 | | desired.width -= current.width; |
| 323 | | desired.height -= current.height; |
| 324 | | NSRect windowFrame = [window frame]; |
| 325 | | windowFrame.size.width += desired.width; |
| 326 | | windowFrame.size.height += desired.height; |
| 327 | | |
| 328 | | // limit the size to the minimum size |
| 329 | | NSSize const minimum = [window minSize]; |
| 330 | | windowFrame.size.width = MAX(windowFrame.size.width, minimum.width); |
| 331 | | windowFrame.size.height = MAX(windowFrame.size.height, minimum.height); |
| 332 | | |
| 333 | | // limit the size to the main screen size |
| 334 | | NSRect const available = [[NSScreen mainScreen] visibleFrame]; |
| 335 | | windowFrame.size.width = MIN(windowFrame.size.width, available.size.width); |
| 336 | | windowFrame.size.height = MIN(windowFrame.size.height, available.size.height); |
| 337 | | |
| 338 | | // arbitrary additional height limit |
| 339 | | windowFrame.size.height = MIN(windowFrame.size.height, 320); |
| 340 | | |
| 341 | | // place it in the bottom right corner and apply |
| 342 | | windowFrame.origin.x = available.origin.x + available.size.width - windowFrame.size.width; |
| 343 | | windowFrame.origin.y = available.origin.y; |
| 344 | | [window setFrame:windowFrame display:YES]; |
| 345 | | [[self class] cascadeWindow:window]; |
| 346 | | } |
| 347 | | |
| 348 | | @end |
| 349 | | |
| 350 | | |
| 351 | | //============================================================ |
| 352 | | // MAMEExpreesionAuxiliaryDebugWindowHandler class |
| 353 | | //============================================================ |
| 354 | | |
| 355 | | @implementation MAMEExpressionAuxiliaryDebugWindowHandler |
| 356 | | |
| 357 | | - (id)initWithMachine:(running_machine &)m title:(NSString *)t console:(MAMEDebugConsole *)c { |
| 358 | | if (!(self = [super initWithMachine:m title:t console:c])) |
| 359 | | return nil; |
| 360 | | history = [[MAMEDebugCommandHistory alloc] init]; |
| 361 | | return self; |
| 362 | | } |
| 363 | | |
| 364 | | |
| 365 | | - (void)dealloc { |
| 366 | | if (history != nil) |
| 367 | | [history release]; |
| 368 | | [super dealloc]; |
| 369 | | } |
| 370 | | |
| 371 | | |
| 372 | | - (id <MAMEDebugViewExpressionSupport>)documentView { |
| 373 | | return nil; |
| 374 | | } |
| 375 | | |
| 376 | | |
| 377 | | - (NSString *)expression { |
| 378 | | return [[self documentView] expression]; |
| 379 | | } |
| 380 | | |
| 381 | | - (void)setExpression:(NSString *)expression { |
| 382 | | [history add:expression]; |
| 383 | | [[self documentView] setExpression:expression]; |
| 384 | | [expressionField setStringValue:expression]; |
| 385 | | [expressionField selectText:self]; |
| 386 | | } |
| 387 | | |
| 388 | | |
| 389 | | - (IBAction)doExpression:(id)sender { |
| 390 | | NSString *expr = [sender stringValue]; |
| 391 | | if ([expr length] > 0) { |
| 392 | | [history add:expr]; |
| 393 | | [[self documentView] setExpression:expr]; |
| 394 | | } else { |
| 395 | | [sender setStringValue:[[self documentView] expression]]; |
| 396 | | [history reset]; |
| 397 | | } |
| 398 | | [sender selectText:self]; |
| 399 | | } |
| 400 | | |
| 401 | | |
| 402 | | - (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor |
| 403 | | { |
| 404 | | if (control == expressionField) |
| 405 | | [history edit]; |
| 406 | | |
| 407 | | return YES; |
| 408 | | } |
| 409 | | |
| 410 | | |
| 411 | | - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command { |
| 412 | | if (control == expressionField) { |
| 413 | | if (command == @selector(cancelOperation:)) { |
| 414 | | [history reset]; |
| 415 | | [expressionField setStringValue:[[self documentView] expression]]; |
| 416 | | [expressionField selectText:self]; |
| 417 | | return YES; |
| 418 | | } else if (command == @selector(moveUp:)) { |
| 419 | | NSString *hist = [history previous:[expressionField stringValue]]; |
| 420 | | if (hist != nil) { |
| 421 | | [expressionField setStringValue:hist]; |
| 422 | | [expressionField selectText:self]; |
| 423 | | } |
| 424 | | return YES; |
| 425 | | } else if (command == @selector(moveDown:)) { |
| 426 | | NSString *hist = [history next:[expressionField stringValue]]; |
| 427 | | if (hist != nil) { |
| 428 | | [expressionField setStringValue:hist]; |
| 429 | | [expressionField selectText:self]; |
| 430 | | } |
| 431 | | return YES; |
| 432 | | } |
| 433 | | } |
| 434 | | return NO; |
| 435 | | } |
| 436 | | |
| 437 | | @end |
trunk/src/osd/modules/debugger/osx/debugwindowhandler.mm
| r0 | r250165 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Vas Crabb |
| 3 | //============================================================ |
| 4 | // |
| 5 | // debugwindowhandler.m - MacOS X Cocoa debug window handling |
| 6 | // |
| 7 | //============================================================ |
| 8 | |
| 9 | #import "debugwindowhandler.h" |
| 10 | |
| 11 | #import "debugconsole.h" |
| 12 | #import "debugcommandhistory.h" |
| 13 | #import "debugview.h" |
| 14 | |
| 15 | |
| 16 | //============================================================ |
| 17 | // NOTIFICATIONS |
| 18 | //============================================================ |
| 19 | |
| 20 | NSString *const MAMEHideDebuggerNotification = @"MAMEHideDebuggerNotification"; |
| 21 | NSString *const MAMEShowDebuggerNotification = @"MAMEShowDebuggerNotification"; |
| 22 | NSString *const MAMEAuxiliaryDebugWindowWillCloseNotification = @"MAMEAuxiliaryDebugWindowWillCloseNotification"; |
| 23 | |
| 24 | |
| 25 | //============================================================ |
| 26 | // MAMEDebugWindowHandler class |
| 27 | //============================================================ |
| 28 | |
| 29 | @implementation MAMEDebugWindowHandler |
| 30 | |
| 31 | + (void)addCommonActionItems:(NSMenu *)menu { |
| 32 | NSMenuItem *runParentItem = [menu addItemWithTitle:@"Run" |
| 33 | action:@selector(debugRun:) |
| 34 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF5FunctionKey]]; |
| 35 | NSMenu *runMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Run"]; |
| 36 | [runParentItem setSubmenu:runMenu]; |
| 37 | [runMenu release]; |
| 38 | [runParentItem setKeyEquivalentModifierMask:0]; |
| 39 | [[runMenu addItemWithTitle:@"and Hide Debugger" |
| 40 | action:@selector(debugRunAndHide:) |
| 41 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF12FunctionKey]] |
| 42 | setKeyEquivalentModifierMask:0]; |
| 43 | [[runMenu addItemWithTitle:@"to Next CPU" |
| 44 | action:@selector(debugRunToNextCPU:) |
| 45 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF6FunctionKey]] |
| 46 | setKeyEquivalentModifierMask:0]; |
| 47 | [[runMenu addItemWithTitle:@"until Next Interrupt on Current CPU" |
| 48 | action:@selector(debugRunToNextInterrupt:) |
| 49 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF7FunctionKey]] |
| 50 | setKeyEquivalentModifierMask:0]; |
| 51 | [[runMenu addItemWithTitle:@"until Next VBLANK" |
| 52 | action:@selector(debugRunToNextVBLANK:) |
| 53 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF8FunctionKey]] |
| 54 | setKeyEquivalentModifierMask:0]; |
| 55 | |
| 56 | NSMenuItem *stepParentItem = [menu addItemWithTitle:@"Step" action:NULL keyEquivalent:@""]; |
| 57 | NSMenu *stepMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Step"]; |
| 58 | [stepParentItem setSubmenu:stepMenu]; |
| 59 | [stepMenu release]; |
| 60 | [[stepMenu addItemWithTitle:@"Into" |
| 61 | action:@selector(debugStepInto:) |
| 62 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF11FunctionKey]] |
| 63 | setKeyEquivalentModifierMask:0]; |
| 64 | [[stepMenu addItemWithTitle:@"Over" |
| 65 | action:@selector(debugStepOver:) |
| 66 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]] |
| 67 | setKeyEquivalentModifierMask:0]; |
| 68 | [[stepMenu addItemWithTitle:@"Out" |
| 69 | action:@selector(debugStepOut:) |
| 70 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]] |
| 71 | setKeyEquivalentModifierMask:NSShiftKeyMask]; |
| 72 | |
| 73 | NSMenuItem *resetParentItem = [menu addItemWithTitle:@"Reset" action:NULL keyEquivalent:@""]; |
| 74 | NSMenu *resetMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Reset"]; |
| 75 | [resetParentItem setSubmenu:resetMenu]; |
| 76 | [resetMenu release]; |
| 77 | [[resetMenu addItemWithTitle:@"Soft" |
| 78 | action:@selector(debugSoftReset:) |
| 79 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]] |
| 80 | setKeyEquivalentModifierMask:0]; |
| 81 | [[resetMenu addItemWithTitle:@"Hard" |
| 82 | action:@selector(debugHardReset:) |
| 83 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]] |
| 84 | setKeyEquivalentModifierMask:NSShiftKeyMask]; |
| 85 | |
| 86 | [menu addItem:[NSMenuItem separatorItem]]; |
| 87 | |
| 88 | NSMenuItem *newParentItem = [menu addItemWithTitle:@"New" action:NULL keyEquivalent:@""]; |
| 89 | NSMenu *newMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"New"]; |
| 90 | [newParentItem setSubmenu:newMenu]; |
| 91 | [newMenu release]; |
| 92 | [newMenu addItemWithTitle:@"Memory Window" |
| 93 | action:@selector(debugNewMemoryWindow:) |
| 94 | keyEquivalent:@"d"]; |
| 95 | [newMenu addItemWithTitle:@"Disassembly Window" |
| 96 | action:@selector(debugNewDisassemblyWindow:) |
| 97 | keyEquivalent:@"a"]; |
| 98 | [newMenu addItemWithTitle:@"Error Log Window" |
| 99 | action:@selector(debugNewErrorLogWindow:) |
| 100 | keyEquivalent:@"l"]; |
| 101 | [newMenu addItemWithTitle:@"(Break|Watch)points Window" |
| 102 | action:@selector(debugNewPointsWindow:) |
| 103 | keyEquivalent:@"b"]; |
| 104 | [newMenu addItemWithTitle:@"Devices Window" |
| 105 | action:@selector(debugNewDevicesWindow:) |
| 106 | keyEquivalent:@"D"]; |
| 107 | |
| 108 | [menu addItem:[NSMenuItem separatorItem]]; |
| 109 | |
| 110 | [menu addItemWithTitle:@"Close Window" action:@selector(performClose:) keyEquivalent:@"w"]; |
| 111 | [menu addItemWithTitle:@"Quit" action:@selector(debugExit:) keyEquivalent:@"q"]; |
| 112 | } |
| 113 | |
| 114 | |
| 115 | + (NSPopUpButton *)newActionButtonWithFrame:(NSRect)frame { |
| 116 | NSPopUpButton *actionButton = [[NSPopUpButton alloc] initWithFrame:frame pullsDown:YES]; |
| 117 | [actionButton setTitle:@""]; |
| 118 | [actionButton addItemWithTitle:@""]; |
| 119 | [actionButton setBezelStyle:NSShadowlessSquareBezelStyle]; |
| 120 | [actionButton setFocusRingType:NSFocusRingTypeNone]; |
| 121 | [[actionButton cell] setArrowPosition:NSPopUpArrowAtCenter]; |
| 122 | [[self class] addCommonActionItems:[actionButton menu]]; |
| 123 | return actionButton; |
| 124 | } |
| 125 | |
| 126 | |
| 127 | + (device_debug::breakpoint *)findBreakpointAtAddress:(offs_t)address forDevice:(device_t &)device { |
| 128 | device_debug *const cpuinfo = device.debug(); |
| 129 | device_debug::breakpoint *bp = cpuinfo->breakpoint_first(); |
| 130 | while ((bp != NULL) && (address != bp->address())) bp = bp->next(); |
| 131 | return bp; |
| 132 | } |
| 133 | |
| 134 | |
| 135 | - (id)initWithMachine:(running_machine &)m title:(NSString *)t { |
| 136 | if (!(self = [super init])) |
| 137 | return nil; |
| 138 | |
| 139 | window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 320, 240) |
| 140 | styleMask:(NSTitledWindowMask | |
| 141 | NSClosableWindowMask | |
| 142 | NSMiniaturizableWindowMask | |
| 143 | NSResizableWindowMask) |
| 144 | backing:NSBackingStoreBuffered |
| 145 | defer:YES]; |
| 146 | [window setReleasedWhenClosed:NO]; |
| 147 | [window setDelegate:self]; |
| 148 | [window setTitle:t]; |
| 149 | [window setContentMinSize:NSMakeSize(320, 240)]; |
| 150 | |
| 151 | [[NSNotificationCenter defaultCenter] addObserver:self |
| 152 | selector:@selector(showDebugger:) |
| 153 | name:MAMEShowDebuggerNotification |
| 154 | object:nil]; |
| 155 | [[NSNotificationCenter defaultCenter] addObserver:self |
| 156 | selector:@selector(hideDebugger:) |
| 157 | name:MAMEHideDebuggerNotification |
| 158 | object:nil]; |
| 159 | |
| 160 | machine = &m; |
| 161 | |
| 162 | return self; |
| 163 | } |
| 164 | |
| 165 | |
| 166 | - (void)dealloc { |
| 167 | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 168 | |
| 169 | if (window != nil) |
| 170 | { |
| 171 | [window orderOut:self]; |
| 172 | [window release]; |
| 173 | } |
| 174 | |
| 175 | [super dealloc]; |
| 176 | } |
| 177 | |
| 178 | |
| 179 | - (void)activate { |
| 180 | [window makeKeyAndOrderFront:self]; |
| 181 | } |
| 182 | |
| 183 | |
| 184 | - (IBAction)debugRun:(id)sender { |
| 185 | debug_cpu_get_visible_cpu(*machine)->debug()->go(); |
| 186 | } |
| 187 | |
| 188 | |
| 189 | - (IBAction)debugRunAndHide:(id)sender { |
| 190 | [[NSNotificationCenter defaultCenter] postNotificationName:MAMEHideDebuggerNotification object:self]; |
| 191 | debug_cpu_get_visible_cpu(*machine)->debug()->go(); |
| 192 | } |
| 193 | |
| 194 | |
| 195 | - (IBAction)debugRunToNextCPU:(id)sender { |
| 196 | debug_cpu_get_visible_cpu(*machine)->debug()->go_next_device(); |
| 197 | } |
| 198 | |
| 199 | |
| 200 | - (IBAction)debugRunToNextInterrupt:(id)sender { |
| 201 | debug_cpu_get_visible_cpu(*machine)->debug()->go_interrupt(); |
| 202 | } |
| 203 | |
| 204 | |
| 205 | - (IBAction)debugRunToNextVBLANK:(id)sender { |
| 206 | debug_cpu_get_visible_cpu(*machine)->debug()->go_vblank(); |
| 207 | } |
| 208 | |
| 209 | |
| 210 | - (IBAction)debugStepInto:(id)sender { |
| 211 | debug_cpu_get_visible_cpu(*machine)->debug()->single_step(); |
| 212 | } |
| 213 | |
| 214 | |
| 215 | - (IBAction)debugStepOver:(id)sender { |
| 216 | debug_cpu_get_visible_cpu(*machine)->debug()->single_step_over(); |
| 217 | } |
| 218 | |
| 219 | |
| 220 | - (IBAction)debugStepOut:(id)sender { |
| 221 | debug_cpu_get_visible_cpu(*machine)->debug()->single_step_out(); |
| 222 | } |
| 223 | |
| 224 | |
| 225 | - (IBAction)debugSoftReset:(id)sender { |
| 226 | machine->schedule_soft_reset(); |
| 227 | debug_cpu_get_visible_cpu(*machine)->debug()->go(); |
| 228 | } |
| 229 | |
| 230 | |
| 231 | - (IBAction)debugHardReset:(id)sender { |
| 232 | machine->schedule_hard_reset(); |
| 233 | } |
| 234 | |
| 235 | |
| 236 | - (IBAction)debugExit:(id)sender { |
| 237 | machine->schedule_exit(); |
| 238 | } |
| 239 | |
| 240 | |
| 241 | - (void)showDebugger:(NSNotification *)notification { |
| 242 | running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue]; |
| 243 | if (m == machine) |
| 244 | { |
| 245 | if (![window isVisible] && ![window isMiniaturized]) |
| 246 | [window orderFront:self]; |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | |
| 251 | - (void)hideDebugger:(NSNotification *)notification { |
| 252 | running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue]; |
| 253 | if (m == machine) |
| 254 | [window orderOut:self]; |
| 255 | } |
| 256 | |
| 257 | @end |
| 258 | |
| 259 | |
| 260 | //============================================================ |
| 261 | // MAMEAuxiliaryDebugWindowHandler class |
| 262 | //============================================================ |
| 263 | |
| 264 | @implementation MAMEAuxiliaryDebugWindowHandler |
| 265 | |
| 266 | + (void)cascadeWindow:(NSWindow *)window { |
| 267 | static NSPoint lastPosition = { 0, 0 }; |
| 268 | if (NSEqualPoints(lastPosition, NSZeroPoint)) { |
| 269 | NSRect available = [[NSScreen mainScreen] visibleFrame]; |
| 270 | lastPosition = NSMakePoint(available.origin.x + 12, available.origin.y + available.size.height - 8); |
| 271 | } |
| 272 | lastPosition = [window cascadeTopLeftFromPoint:lastPosition]; |
| 273 | } |
| 274 | |
| 275 | |
| 276 | - (id)initWithMachine:(running_machine &)m title:(NSString *)t console:(MAMEDebugConsole *)c { |
| 277 | if (!(self = [super initWithMachine:m title:t])) |
| 278 | return nil; |
| 279 | console = c; |
| 280 | return self; |
| 281 | } |
| 282 | |
| 283 | |
| 284 | - (void)dealloc { |
| 285 | [super dealloc]; |
| 286 | } |
| 287 | |
| 288 | |
| 289 | - (IBAction)debugNewMemoryWindow:(id)sender { |
| 290 | [console debugNewMemoryWindow:sender]; |
| 291 | } |
| 292 | |
| 293 | |
| 294 | - (IBAction)debugNewDisassemblyWindow:(id)sender { |
| 295 | [console debugNewDisassemblyWindow:sender]; |
| 296 | } |
| 297 | |
| 298 | |
| 299 | - (IBAction)debugNewErrorLogWindow:(id)sender { |
| 300 | [console debugNewErrorLogWindow:sender]; |
| 301 | } |
| 302 | |
| 303 | |
| 304 | - (IBAction)debugNewPointsWindow:(id)sender { |
| 305 | [console debugNewPointsWindow:sender]; |
| 306 | } |
| 307 | |
| 308 | |
| 309 | - (IBAction)debugNewDevicesWindow:(id)sender { |
| 310 | [console debugNewDevicesWindow:sender]; |
| 311 | } |
| 312 | |
| 313 | |
| 314 | - (void)windowWillClose:(NSNotification *)notification { |
| 315 | [[NSNotificationCenter defaultCenter] postNotificationName:MAMEAuxiliaryDebugWindowWillCloseNotification |
| 316 | object:self]; |
| 317 | } |
| 318 | |
| 319 | - (void)cascadeWindowWithDesiredSize:(NSSize)desired forView:(NSView *)view { |
| 320 | // convert desired size to an adjustment and apply it to the current window frame |
| 321 | NSSize const current = [view frame].size; |
| 322 | desired.width -= current.width; |
| 323 | desired.height -= current.height; |
| 324 | NSRect windowFrame = [window frame]; |
| 325 | windowFrame.size.width += desired.width; |
| 326 | windowFrame.size.height += desired.height; |
| 327 | |
| 328 | // limit the size to the minimum size |
| 329 | NSSize const minimum = [window minSize]; |
| 330 | windowFrame.size.width = MAX(windowFrame.size.width, minimum.width); |
| 331 | windowFrame.size.height = MAX(windowFrame.size.height, minimum.height); |
| 332 | |
| 333 | // limit the size to the main screen size |
| 334 | NSRect const available = [[NSScreen mainScreen] visibleFrame]; |
| 335 | windowFrame.size.width = MIN(windowFrame.size.width, available.size.width); |
| 336 | windowFrame.size.height = MIN(windowFrame.size.height, available.size.height); |
| 337 | |
| 338 | // arbitrary additional height limit |
| 339 | windowFrame.size.height = MIN(windowFrame.size.height, 320); |
| 340 | |
| 341 | // place it in the bottom right corner and apply |
| 342 | windowFrame.origin.x = available.origin.x + available.size.width - windowFrame.size.width; |
| 343 | windowFrame.origin.y = available.origin.y; |
| 344 | [window setFrame:windowFrame display:YES]; |
| 345 | [[self class] cascadeWindow:window]; |
| 346 | } |
| 347 | |
| 348 | @end |
| 349 | |
| 350 | |
| 351 | //============================================================ |
| 352 | // MAMEExpreesionAuxiliaryDebugWindowHandler class |
| 353 | //============================================================ |
| 354 | |
| 355 | @implementation MAMEExpressionAuxiliaryDebugWindowHandler |
| 356 | |
| 357 | - (id)initWithMachine:(running_machine &)m title:(NSString *)t console:(MAMEDebugConsole *)c { |
| 358 | if (!(self = [super initWithMachine:m title:t console:c])) |
| 359 | return nil; |
| 360 | history = [[MAMEDebugCommandHistory alloc] init]; |
| 361 | return self; |
| 362 | } |
| 363 | |
| 364 | |
| 365 | - (void)dealloc { |
| 366 | if (history != nil) |
| 367 | [history release]; |
| 368 | [super dealloc]; |
| 369 | } |
| 370 | |
| 371 | |
| 372 | - (id <MAMEDebugViewExpressionSupport>)documentView { |
| 373 | return nil; |
| 374 | } |
| 375 | |
| 376 | |
| 377 | - (NSString *)expression { |
| 378 | return [[self documentView] expression]; |
| 379 | } |
| 380 | |
| 381 | - (void)setExpression:(NSString *)expression { |
| 382 | [history add:expression]; |
| 383 | [[self documentView] setExpression:expression]; |
| 384 | [expressionField setStringValue:expression]; |
| 385 | [expressionField selectText:self]; |
| 386 | } |
| 387 | |
| 388 | |
| 389 | - (IBAction)doExpression:(id)sender { |
| 390 | NSString *expr = [sender stringValue]; |
| 391 | if ([expr length] > 0) { |
| 392 | [history add:expr]; |
| 393 | [[self documentView] setExpression:expr]; |
| 394 | } else { |
| 395 | [sender setStringValue:[[self documentView] expression]]; |
| 396 | [history reset]; |
| 397 | } |
| 398 | [sender selectText:self]; |
| 399 | } |
| 400 | |
| 401 | |
| 402 | - (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor |
| 403 | { |
| 404 | if (control == expressionField) |
| 405 | [history edit]; |
| 406 | |
| 407 | return YES; |
| 408 | } |
| 409 | |
| 410 | |
| 411 | - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command { |
| 412 | if (control == expressionField) { |
| 413 | if (command == @selector(cancelOperation:)) { |
| 414 | [history reset]; |
| 415 | [expressionField setStringValue:[[self documentView] expression]]; |
| 416 | [expressionField selectText:self]; |
| 417 | return YES; |
| 418 | } else if (command == @selector(moveUp:)) { |
| 419 | NSString *hist = [history previous:[expressionField stringValue]]; |
| 420 | if (hist != nil) { |
| 421 | [expressionField setStringValue:hist]; |
| 422 | [expressionField selectText:self]; |
| 423 | } |
| 424 | return YES; |
| 425 | } else if (command == @selector(moveDown:)) { |
| 426 | NSString *hist = [history next:[expressionField stringValue]]; |
| 427 | if (hist != nil) { |
| 428 | [expressionField setStringValue:hist]; |
| 429 | [expressionField selectText:self]; |
| 430 | } |
| 431 | return YES; |
| 432 | } |
| 433 | } |
| 434 | return NO; |
| 435 | } |
| 436 | |
| 437 | @end |
trunk/src/osd/modules/debugger/osx/deviceinfoviewer.m
| r250164 | r250165 | |
| 1 | | // license:BSD-3-Clause |
| 2 | | // copyright-holders:Vas Crabb |
| 3 | | //============================================================ |
| 4 | | // |
| 5 | | // deviceinfoviewer.m - MacOS X Cocoa debug window handling |
| 6 | | // |
| 7 | | //============================================================ |
| 8 | | |
| 9 | | #import "deviceinfoviewer.h" |
| 10 | | |
| 11 | | |
| 12 | | @interface MAMEDeviceInfoView : NSView |
| 13 | | { |
| 14 | | CGFloat minWidth; |
| 15 | | } |
| 16 | | |
| 17 | | - (id)initWithFrame:(NSRect)frame; |
| 18 | | |
| 19 | | - (void)setMinWidth:(CGFloat)aWidth; |
| 20 | | |
| 21 | | @end |
| 22 | | |
| 23 | | |
| 24 | | @implementation MAMEDeviceInfoView |
| 25 | | |
| 26 | | - (id)initWithFrame:(NSRect)frame { |
| 27 | | if (!(self = [super initWithFrame:frame])) |
| 28 | | return nil; |
| 29 | | minWidth = 0; |
| 30 | | return self; |
| 31 | | } |
| 32 | | |
| 33 | | - (void)setMinWidth:(CGFloat)aWidth { |
| 34 | | minWidth = aWidth; |
| 35 | | } |
| 36 | | |
| 37 | | - (BOOL)isFlipped { |
| 38 | | return YES; |
| 39 | | } |
| 40 | | |
| 41 | | - (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize { |
| 42 | | NSSize const newBoundsSize = [[self superview] bounds].size; |
| 43 | | [self setFrameSize:NSMakeSize(MAX(newBoundsSize.width, minWidth), [self frame].size.height)]; |
| 44 | | } |
| 45 | | |
| 46 | | @end |
| 47 | | |
| 48 | | |
| 49 | | @implementation MAMEDeviceInfoViewer |
| 50 | | |
| 51 | | - (NSTextField *)makeLabel:(NSString *)text { |
| 52 | | NSTextField *const result = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 14)]; |
| 53 | | [result setAutoresizingMask:(NSViewMaxYMargin | NSViewMaxXMargin)]; |
| 54 | | [[result cell] setControlSize:NSSmallControlSize]; |
| 55 | | [result setEditable:NO]; |
| 56 | | [result setSelectable:NO]; |
| 57 | | [result setBezeled:NO]; |
| 58 | | [result setBordered:NO]; |
| 59 | | [result setDrawsBackground:NO]; |
| 60 | | [result setAlignment:NSRightTextAlignment]; |
| 61 | | [result setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; |
| 62 | | [result setStringValue:text]; |
| 63 | | [result sizeToFit]; |
| 64 | | return result; |
| 65 | | } |
| 66 | | |
| 67 | | |
| 68 | | - (NSTextField *)makeField:(NSString *)text { |
| 69 | | NSTextField *const result = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 14)]; |
| 70 | | [result setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)]; |
| 71 | | [[result cell] setControlSize:NSSmallControlSize]; |
| 72 | | [result setEditable:NO]; |
| 73 | | [result setSelectable:YES]; |
| 74 | | [result setBezeled:NO]; |
| 75 | | [result setBordered:NO]; |
| 76 | | [result setDrawsBackground:NO]; |
| 77 | | [result setAlignment:NSLeftTextAlignment]; |
| 78 | | [result setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; |
| 79 | | [result setStringValue:text]; |
| 80 | | [result sizeToFit]; |
| 81 | | return result; |
| 82 | | } |
| 83 | | |
| 84 | | |
| 85 | | - (NSBox *)makeBox:(NSString *)t toFit:(NSView *)v { |
| 86 | | NSBox *const result = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, [v frame].size.width - 34, 32)]; |
| 87 | | [result setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)]; |
| 88 | | [result setTitle:t]; |
| 89 | | [result setBoxType:NSBoxPrimary]; |
| 90 | | [result setBorderType:NSLineBorder]; |
| 91 | | [result setContentViewMargins:NSMakeSize(0, 0)]; |
| 92 | | [result setAutoresizesSubviews:YES]; |
| 93 | | return result; |
| 94 | | } |
| 95 | | |
| 96 | | |
| 97 | | - (void)addLabel:(NSString *)l withWidth:(CGFloat)w andField:(NSString *)f toView:(NSView *)v { |
| 98 | | NSTextField *const label = [self makeLabel:l]; |
| 99 | | NSTextField *const field = [self makeField:f]; |
| 100 | | CGFloat const height = MAX([label frame].size.height, [field frame].size.height); |
| 101 | | NSSize space = [v bounds].size; |
| 102 | | space.width = MAX(space.width, [field frame].size.width + w + 52); |
| 103 | | space.height += height + 8; |
| 104 | | [label setFrame:NSMakeRect(25, space.height - height - 20, w, height)]; |
| 105 | | [field setFrame:NSMakeRect(w + 27, space.height - height - 20, space.width - w - 52, height)]; |
| 106 | | [v setFrameSize:space]; |
| 107 | | [v addSubview:label]; |
| 108 | | [v addSubview:field]; |
| 109 | | [label release]; |
| 110 | | [field release]; |
| 111 | | } |
| 112 | | |
| 113 | | |
| 114 | | - (void)addField:(NSString *)f toBox:(NSBox *)b { |
| 115 | | NSTextField *const field = [self makeField:f]; |
| 116 | | [field setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; |
| 117 | | NSSize space = [b frame].size; |
| 118 | | space.width = MAX(space.width, [field frame].size.width + 32); |
| 119 | | space.height += [field frame].size.height + 8; |
| 120 | | [field setFrame:NSMakeRect(15, 14, space.width - 32, [field frame].size.height)]; |
| 121 | | [b setFrameSize:space]; |
| 122 | | [[b contentView] addSubview:field]; |
| 123 | | [field release]; |
| 124 | | } |
| 125 | | |
| 126 | | |
| 127 | | - (void)addBox:(NSBox *)b toView:(NSView *)v { |
| 128 | | NSSize space = [v frame].size; |
| 129 | | space.width = MAX(space.width, [b frame].size.width + 34); |
| 130 | | space.height += [b frame].size.height + 4; |
| 131 | | [b setFrameOrigin:NSMakePoint(17, space.height - [b frame].size.height - 16)]; |
| 132 | | [v setFrameSize:space]; |
| 133 | | [v addSubview:b]; |
| 134 | | } |
| 135 | | |
| 136 | | |
| 137 | | - (id)initWithDevice:(device_t &)d machine:(running_machine &)m console:(MAMEDebugConsole *)c { |
| 138 | | MAMEDeviceInfoView *contentView; |
| 139 | | NSScrollView *contentScroll; |
| 140 | | |
| 141 | | if (!(self = [super initWithMachine:m |
| 142 | | title:[NSString stringWithFormat:@"Device %s", d.tag()] |
| 143 | | console:c])) |
| 144 | | { |
| 145 | | return nil; |
| 146 | | } |
| 147 | | device = &d; |
| 148 | | |
| 149 | | // Create a view to hold everything |
| 150 | | contentView = [[MAMEDeviceInfoView alloc] initWithFrame:NSMakeRect(0, 0, 320, 32)]; |
| 151 | | [contentView setAutoresizesSubviews:YES]; |
| 152 | | |
| 153 | | // add the stuff that's always present |
| 154 | | [self addLabel:@"Tag:" |
| 155 | | withWidth:100 |
| 156 | | andField:[NSString stringWithUTF8String:device->tag()] |
| 157 | | toView:contentView]; |
| 158 | | [self addLabel:@"Name:" |
| 159 | | withWidth:100 |
| 160 | | andField:[NSString stringWithUTF8String:device->name()] |
| 161 | | toView:contentView]; |
| 162 | | [self addLabel:@"Shortname:" |
| 163 | | withWidth:100 |
| 164 | | andField:[NSString stringWithUTF8String:device->shortname()] |
| 165 | | toView:contentView]; |
| 166 | | |
| 167 | | // add interfaces if present |
| 168 | | device_interface *interface = device->first_interface(); |
| 169 | | if (interface != NULL) |
| 170 | | { |
| 171 | | NSBox *const interfacesBox = [self makeBox:@"Interfaces" toFit:contentView]; |
| 172 | | while (interface != NULL) |
| 173 | | { |
| 174 | | [self addField:[NSString stringWithUTF8String:interface->interface_type()] |
| 175 | | toBox:interfacesBox]; |
| 176 | | interface = interface->interface_next(); |
| 177 | | } |
| 178 | | [self addBox:interfacesBox toView:contentView]; |
| 179 | | [interfacesBox release]; |
| 180 | | } |
| 181 | | |
| 182 | | // add memory maps if present |
| 183 | | device_memory_interface *memory; |
| 184 | | if (device->interface(memory)) |
| 185 | | { |
| 186 | | NSBox *memoryBox = nil; |
| 187 | | for (address_spacenum i = AS_0; i < ADDRESS_SPACES; i++) |
| 188 | | { |
| 189 | | if (memory->has_space(i)) |
| 190 | | { |
| 191 | | if (memoryBox == nil) |
| 192 | | memoryBox = [self makeBox:@"Memory maps" toFit:contentView]; |
| 193 | | [self addField:[NSString stringWithUTF8String:memory->space_config(i)->name()] |
| 194 | | toBox:memoryBox]; |
| 195 | | } |
| 196 | | } |
| 197 | | if (memoryBox != nil) |
| 198 | | { |
| 199 | | [self addBox:memoryBox toView:contentView]; |
| 200 | | [memoryBox release]; |
| 201 | | } |
| 202 | | } |
| 203 | | |
| 204 | | // lock minimum content size |
| 205 | | [contentView setMinWidth:[contentView frame].size.width]; |
| 206 | | [contentView setAutoresizingMask:NSViewWidthSizable]; |
| 207 | | |
| 208 | | // create a scroll view for holding everything |
| 209 | | NSSize desired = [NSScrollView frameSizeForContentSize:[contentView frame].size |
| 210 | | hasHorizontalScroller:YES |
| 211 | | hasVerticalScroller:YES |
| 212 | | borderType:NSNoBorder]; |
| 213 | | [window setContentSize:desired]; |
| 214 | | contentScroll = [[NSScrollView alloc] initWithFrame:[[window contentView] bounds]]; |
| 215 | | [contentScroll setDrawsBackground:NO]; |
| 216 | | [contentScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 217 | | [contentScroll setHasHorizontalScroller:YES]; |
| 218 | | [contentScroll setHasVerticalScroller:YES]; |
| 219 | | [contentScroll setAutohidesScrollers:YES]; |
| 220 | | [contentScroll setBorderType:NSNoBorder]; |
| 221 | | [contentScroll setDocumentView:contentView]; |
| 222 | | [contentView release]; |
| 223 | | [[window contentView] addSubview:contentScroll]; |
| 224 | | [contentScroll release]; |
| 225 | | |
| 226 | | // calculate the optimal size for everything |
| 227 | | [self cascadeWindowWithDesiredSize:[contentScroll frame].size forView:contentScroll]; |
| 228 | | |
| 229 | | // don't forget the result |
| 230 | | return self; |
| 231 | | } |
| 232 | | |
| 233 | | @end |
trunk/src/osd/modules/debugger/osx/deviceinfoviewer.mm
| r0 | r250165 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Vas Crabb |
| 3 | //============================================================ |
| 4 | // |
| 5 | // deviceinfoviewer.m - MacOS X Cocoa debug window handling |
| 6 | // |
| 7 | //============================================================ |
| 8 | |
| 9 | #import "deviceinfoviewer.h" |
| 10 | |
| 11 | |
| 12 | @interface MAMEDeviceInfoView : NSView |
| 13 | { |
| 14 | CGFloat minWidth; |
| 15 | } |
| 16 | |
| 17 | - (id)initWithFrame:(NSRect)frame; |
| 18 | |
| 19 | - (void)setMinWidth:(CGFloat)aWidth; |
| 20 | |
| 21 | @end |
| 22 | |
| 23 | |
| 24 | @implementation MAMEDeviceInfoView |
| 25 | |
| 26 | - (id)initWithFrame:(NSRect)frame { |
| 27 | if (!(self = [super initWithFrame:frame])) |
| 28 | return nil; |
| 29 | minWidth = 0; |
| 30 | return self; |
| 31 | } |
| 32 | |
| 33 | - (void)setMinWidth:(CGFloat)aWidth { |
| 34 | minWidth = aWidth; |
| 35 | } |
| 36 | |
| 37 | - (BOOL)isFlipped { |
| 38 | return YES; |
| 39 | } |
| 40 | |
| 41 | - (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize { |
| 42 | NSSize const newBoundsSize = [[self superview] bounds].size; |
| 43 | [self setFrameSize:NSMakeSize(MAX(newBoundsSize.width, minWidth), [self frame].size.height)]; |
| 44 | } |
| 45 | |
| 46 | @end |
| 47 | |
| 48 | |
| 49 | @implementation MAMEDeviceInfoViewer |
| 50 | |
| 51 | - (NSTextField *)makeLabel:(NSString *)text { |
| 52 | NSTextField *const result = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 14)]; |
| 53 | [result setAutoresizingMask:(NSViewMaxYMargin | NSViewMaxXMargin)]; |
| 54 | [[result cell] setControlSize:NSSmallControlSize]; |
| 55 | [result setEditable:NO]; |
| 56 | [result setSelectable:NO]; |
| 57 | [result setBezeled:NO]; |
| 58 | [result setBordered:NO]; |
| 59 | [result setDrawsBackground:NO]; |
| 60 | [result setAlignment:NSRightTextAlignment]; |
| 61 | [result setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; |
| 62 | [result setStringValue:text]; |
| 63 | [result sizeToFit]; |
| 64 | return result; |
| 65 | } |
| 66 | |
| 67 | |
| 68 | - (NSTextField *)makeField:(NSString *)text { |
| 69 | NSTextField *const result = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 14)]; |
| 70 | [result setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)]; |
| 71 | [[result cell] setControlSize:NSSmallControlSize]; |
| 72 | [result setEditable:NO]; |
| 73 | [result setSelectable:YES]; |
| 74 | [result setBezeled:NO]; |
| 75 | [result setBordered:NO]; |
| 76 | [result setDrawsBackground:NO]; |
| 77 | [result setAlignment:NSLeftTextAlignment]; |
| 78 | [result setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; |
| 79 | [result setStringValue:text]; |
| 80 | [result sizeToFit]; |
| 81 | return result; |
| 82 | } |
| 83 | |
| 84 | |
| 85 | - (NSBox *)makeBox:(NSString *)t toFit:(NSView *)v { |
| 86 | NSBox *const result = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, [v frame].size.width - 34, 32)]; |
| 87 | [result setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)]; |
| 88 | [result setTitle:t]; |
| 89 | [result setBoxType:NSBoxPrimary]; |
| 90 | [result setBorderType:NSLineBorder]; |
| 91 | [result setContentViewMargins:NSMakeSize(0, 0)]; |
| 92 | [result setAutoresizesSubviews:YES]; |
| 93 | return result; |
| 94 | } |
| 95 | |
| 96 | |
| 97 | - (void)addLabel:(NSString *)l withWidth:(CGFloat)w andField:(NSString *)f toView:(NSView *)v { |
| 98 | NSTextField *const label = [self makeLabel:l]; |
| 99 | NSTextField *const field = [self makeField:f]; |
| 100 | CGFloat const height = MAX([label frame].size.height, [field frame].size.height); |
| 101 | NSSize space = [v bounds].size; |
| 102 | space.width = MAX(space.width, [field frame].size.width + w + 52); |
| 103 | space.height += height + 8; |
| 104 | [label setFrame:NSMakeRect(25, space.height - height - 20, w, height)]; |
| 105 | [field setFrame:NSMakeRect(w + 27, space.height - height - 20, space.width - w - 52, height)]; |
| 106 | [v setFrameSize:space]; |
| 107 | [v addSubview:label]; |
| 108 | [v addSubview:field]; |
| 109 | [label release]; |
| 110 | [field release]; |
| 111 | } |
| 112 | |
| 113 | |
| 114 | - (void)addField:(NSString *)f toBox:(NSBox *)b { |
| 115 | NSTextField *const field = [self makeField:f]; |
| 116 | [field setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; |
| 117 | NSSize space = [b frame].size; |
| 118 | space.width = MAX(space.width, [field frame].size.width + 32); |
| 119 | space.height += [field frame].size.height + 8; |
| 120 | [field setFrame:NSMakeRect(15, 14, space.width - 32, [field frame].size.height)]; |
| 121 | [b setFrameSize:space]; |
| 122 | [[b contentView] addSubview:field]; |
| 123 | [field release]; |
| 124 | } |
| 125 | |
| 126 | |
| 127 | - (void)addBox:(NSBox *)b toView:(NSView *)v { |
| 128 | NSSize space = [v frame].size; |
| 129 | space.width = MAX(space.width, [b frame].size.width + 34); |
| 130 | space.height += [b frame].size.height + 4; |
| 131 | [b setFrameOrigin:NSMakePoint(17, space.height - [b frame].size.height - 16)]; |
| 132 | [v setFrameSize:space]; |
| 133 | [v addSubview:b]; |
| 134 | } |
| 135 | |
| 136 | |
| 137 | - (id)initWithDevice:(device_t &)d machine:(running_machine &)m console:(MAMEDebugConsole *)c { |
| 138 | MAMEDeviceInfoView *contentView; |
| 139 | NSScrollView *contentScroll; |
| 140 | |
| 141 | if (!(self = [super initWithMachine:m |
| 142 | title:[NSString stringWithFormat:@"Device %s", d.tag()] |
| 143 | console:c])) |
| 144 | { |
| 145 | return nil; |
| 146 | } |
| 147 | device = &d; |
| 148 | |
| 149 | // Create a view to hold everything |
| 150 | contentView = [[MAMEDeviceInfoView alloc] initWithFrame:NSMakeRect(0, 0, 320, 32)]; |
| 151 | [contentView setAutoresizesSubviews:YES]; |
| 152 | |
| 153 | // add the stuff that's always present |
| 154 | [self addLabel:@"Tag:" |
| 155 | withWidth:100 |
| 156 | andField:[NSString stringWithUTF8String:device->tag()] |
| 157 | toView:contentView]; |
| 158 | [self addLabel:@"Name:" |
| 159 | withWidth:100 |
| 160 | andField:[NSString stringWithUTF8String:device->name()] |
| 161 | toView:contentView]; |
| 162 | [self addLabel:@"Shortname:" |
| 163 | withWidth:100 |
| 164 | andField:[NSString stringWithUTF8String:device->shortname()] |
| 165 | toView:contentView]; |
| 166 | |
| 167 | // add interfaces if present |
| 168 | device_interface *interface = device->first_interface(); |
| 169 | if (interface != NULL) |
| 170 | { |
| 171 | NSBox *const interfacesBox = [self makeBox:@"Interfaces" toFit:contentView]; |
| 172 | while (interface != NULL) |
| 173 | { |
| 174 | [self addField:[NSString stringWithUTF8String:interface->interface_type()] |
| 175 | toBox:interfacesBox]; |
| 176 | interface = interface->interface_next(); |
| 177 | } |
| 178 | [self addBox:interfacesBox toView:contentView]; |
| 179 | [interfacesBox release]; |
| 180 | } |
| 181 | |
| 182 | // add memory maps if present |
| 183 | device_memory_interface *memory; |
| 184 | if (device->interface(memory)) |
| 185 | { |
| 186 | NSBox *memoryBox = nil; |
| 187 | for (address_spacenum i = AS_0; i < ADDRESS_SPACES; i++) |
| 188 | { |
| 189 | if (memory->has_space(i)) |
| 190 | { |
| 191 | if (memoryBox == nil) |
| 192 | memoryBox = [self makeBox:@"Memory maps" toFit:contentView]; |
| 193 | [self addField:[NSString stringWithUTF8String:memory->space_config(i)->name()] |
| 194 | toBox:memoryBox]; |
| 195 | } |
| 196 | } |
| 197 | if (memoryBox != nil) |
| 198 | { |
| 199 | [self addBox:memoryBox toView:contentView]; |
| 200 | [memoryBox release]; |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | // lock minimum content size |
| 205 | [contentView setMinWidth:[contentView frame].size.width]; |
| 206 | [contentView setAutoresizingMask:NSViewWidthSizable]; |
| 207 | |
| 208 | // create a scroll view for holding everything |
| 209 | NSSize desired = [NSScrollView frameSizeForContentSize:[contentView frame].size |
| 210 | hasHorizontalScroller:YES |
| 211 | hasVerticalScroller:YES |
| 212 | borderType:NSNoBorder]; |
| 213 | [window setContentSize:desired]; |
| 214 | contentScroll = [[NSScrollView alloc] initWithFrame:[[window contentView] bounds]]; |
| 215 | [contentScroll setDrawsBackground:NO]; |
| 216 | [contentScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 217 | [contentScroll setHasHorizontalScroller:YES]; |
| 218 | [contentScroll setHasVerticalScroller:YES]; |
| 219 | [contentScroll setAutohidesScrollers:YES]; |
| 220 | [contentScroll setBorderType:NSNoBorder]; |
| 221 | [contentScroll setDocumentView:contentView]; |
| 222 | [contentView release]; |
| 223 | [[window contentView] addSubview:contentScroll]; |
| 224 | [contentScroll release]; |
| 225 | |
| 226 | // calculate the optimal size for everything |
| 227 | [self cascadeWindowWithDesiredSize:[contentScroll frame].size forView:contentScroll]; |
| 228 | |
| 229 | // don't forget the result |
| 230 | return self; |
| 231 | } |
| 232 | |
| 233 | @end |
trunk/src/osd/modules/debugger/osx/disassemblyview.m
| r250164 | r250165 | |
| 1 | | // license:BSD-3-Clause |
| 2 | | // copyright-holders:Vas Crabb |
| 3 | | //============================================================ |
| 4 | | // |
| 5 | | // disassemblyview.m - MacOS X Cocoa debug window handling |
| 6 | | // |
| 7 | | //============================================================ |
| 8 | | |
| 9 | | #import "disassemblyview.h" |
| 10 | | |
| 11 | | #include "debug/debugvw.h" |
| 12 | | |
| 13 | | |
| 14 | | @implementation MAMEDisassemblyView |
| 15 | | |
| 16 | | - (id)initWithFrame:(NSRect)f machine:(running_machine &)m { |
| 17 | | if (!(self = [super initWithFrame:f type:DVT_DISASSEMBLY machine:m wholeLineScroll:NO])) |
| 18 | | return nil; |
| 19 | | return self; |
| 20 | | } |
| 21 | | |
| 22 | | |
| 23 | | - (void)dealloc { |
| 24 | | [super dealloc]; |
| 25 | | } |
| 26 | | |
| 27 | | |
| 28 | | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 29 | | SEL const action = [item action]; |
| 30 | | |
| 31 | | if (action == @selector(showRightColumn:)) |
| 32 | | { |
| 33 | | [item setState:((downcast<debug_view_disasm *>(view)->right_column() == [item tag]) ? NSOnState : NSOffState)]; |
| 34 | | return YES; |
| 35 | | } |
| 36 | | else |
| 37 | | { |
| 38 | | return [super validateMenuItem:item]; |
| 39 | | } |
| 40 | | } |
| 41 | | |
| 42 | | |
| 43 | | - (NSSize)maximumFrameSize { |
| 44 | | debug_view_xy max(0, 0); |
| 45 | | debug_view_source const *source = view->source(); |
| 46 | | for (debug_view_source const *source = view->first_source(); source != NULL; source = source->next()) |
| 47 | | { |
| 48 | | view->set_source(*source); |
| 49 | | debug_view_xy const current = view->total_size(); |
| 50 | | max.x = MAX(max.x, current.x); |
| 51 | | max.y = MAX(max.y, current.y); |
| 52 | | } |
| 53 | | view->set_source(*source); |
| 54 | | return NSMakeSize(ceil((max.x * fontWidth) + (2 * [textContainer lineFragmentPadding])), |
| 55 | | ceil(max.y * fontHeight)); |
| 56 | | } |
| 57 | | |
| 58 | | |
| 59 | | - (void)addContextMenuItemsToMenu:(NSMenu *)menu { |
| 60 | | NSMenuItem *item; |
| 61 | | |
| 62 | | [super addContextMenuItemsToMenu:menu]; |
| 63 | | |
| 64 | | if ([menu numberOfItems] > 0) |
| 65 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 66 | | |
| 67 | | item = [menu addItemWithTitle:@"Toggle Breakpoint" |
| 68 | | action:@selector(debugToggleBreakpoint:) |
| 69 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey]]; |
| 70 | | [item setKeyEquivalentModifierMask:0]; |
| 71 | | |
| 72 | | item = [menu addItemWithTitle:@"Disable Breakpoint" |
| 73 | | action:@selector(debugToggleBreakpointEnable:) |
| 74 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey]]; |
| 75 | | [item setKeyEquivalentModifierMask:NSShiftKeyMask]; |
| 76 | | |
| 77 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 78 | | |
| 79 | | item = [menu addItemWithTitle:@"Run to Cursor" |
| 80 | | action:@selector(debugRunToCursor:) |
| 81 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF4FunctionKey]]; |
| 82 | | [item setKeyEquivalentModifierMask:0]; |
| 83 | | |
| 84 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 85 | | |
| 86 | | item = [menu addItemWithTitle:@"Raw Opcodes" |
| 87 | | action:@selector(showRightColumn:) |
| 88 | | keyEquivalent:@"r"]; |
| 89 | | [item setTarget:self]; |
| 90 | | [item setTag:DASM_RIGHTCOL_RAW]; |
| 91 | | |
| 92 | | item = [menu addItemWithTitle:@"Encrypted Opcodes" |
| 93 | | action:@selector(showRightColumn:) |
| 94 | | keyEquivalent:@"e"]; |
| 95 | | [item setTarget:self]; |
| 96 | | [item setTag:DASM_RIGHTCOL_ENCRYPTED]; |
| 97 | | |
| 98 | | item = [menu addItemWithTitle:@"Comments" |
| 99 | | action:@selector(showRightColumn:) |
| 100 | | keyEquivalent:@"n"]; |
| 101 | | [item setTarget:self]; |
| 102 | | [item setTag:DASM_RIGHTCOL_COMMENTS]; |
| 103 | | } |
| 104 | | |
| 105 | | |
| 106 | | - (NSString *)selectedSubviewName { |
| 107 | | const debug_view_source *source = view->source(); |
| 108 | | if (source != NULL) |
| 109 | | return [NSString stringWithUTF8String:source->name()]; |
| 110 | | else |
| 111 | | return @""; |
| 112 | | } |
| 113 | | |
| 114 | | |
| 115 | | - (int)selectedSubviewIndex { |
| 116 | | const debug_view_source *source = view->source(); |
| 117 | | if (source != NULL) |
| 118 | | return view->source_list().indexof(*source); |
| 119 | | else |
| 120 | | return -1; |
| 121 | | } |
| 122 | | |
| 123 | | |
| 124 | | - (void)selectSubviewAtIndex:(int)index { |
| 125 | | const int selected = view->source_list().indexof(*view->source()); |
| 126 | | if (selected != index) { |
| 127 | | view->set_source(*view->source_list().find(index)); |
| 128 | | if ([[self window] firstResponder] != self) |
| 129 | | view->set_cursor_visible(false); |
| 130 | | } |
| 131 | | } |
| 132 | | |
| 133 | | |
| 134 | | - (BOOL)selectSubviewForDevice:(device_t *)device { |
| 135 | | debug_view_source const *const source = view->source_for_device(device); |
| 136 | | if (source != NULL) |
| 137 | | { |
| 138 | | if (view->source() != source) |
| 139 | | { |
| 140 | | view->set_source(*source); |
| 141 | | if ([[self window] firstResponder] != self) |
| 142 | | view->set_cursor_visible(false); |
| 143 | | } |
| 144 | | return YES; |
| 145 | | } |
| 146 | | else |
| 147 | | { |
| 148 | | return NO; |
| 149 | | } |
| 150 | | } |
| 151 | | |
| 152 | | |
| 153 | | - (BOOL)selectSubviewForSpace:(address_space *)space { |
| 154 | | if (space == NULL) return NO; |
| 155 | | debug_view_disasm_source const *source = downcast<debug_view_disasm_source const *>(view->first_source()); |
| 156 | | while ((source != NULL) && (&source->space() != space)) |
| 157 | | source = downcast<debug_view_disasm_source *>(source->next()); |
| 158 | | if (source != NULL) |
| 159 | | { |
| 160 | | if (view->source() != source) |
| 161 | | { |
| 162 | | view->set_source(*source); |
| 163 | | if ([[self window] firstResponder] != self) |
| 164 | | view->set_cursor_visible(false); |
| 165 | | } |
| 166 | | return YES; |
| 167 | | } |
| 168 | | else |
| 169 | | { |
| 170 | | return NO; |
| 171 | | } |
| 172 | | } |
| 173 | | |
| 174 | | |
| 175 | | - (NSString *)expression { |
| 176 | | return [NSString stringWithUTF8String:downcast<debug_view_disasm *>(view)->expression()]; |
| 177 | | } |
| 178 | | |
| 179 | | |
| 180 | | - (void)setExpression:(NSString *)exp { |
| 181 | | downcast<debug_view_disasm *>(view)->set_expression([exp UTF8String]); |
| 182 | | } |
| 183 | | |
| 184 | | |
| 185 | | - (debug_view_disasm_source const *)source { |
| 186 | | return downcast<debug_view_disasm_source const *>(view->source()); |
| 187 | | } |
| 188 | | |
| 189 | | |
| 190 | | - (offs_t)selectedAddress { |
| 191 | | return downcast<debug_view_disasm *>(view)->selected_address(); |
| 192 | | } |
| 193 | | |
| 194 | | |
| 195 | | - (IBAction)showRightColumn:(id)sender { |
| 196 | | downcast<debug_view_disasm *>(view)->set_right_column((disasm_right_column) [sender tag]); |
| 197 | | } |
| 198 | | |
| 199 | | |
| 200 | | - (void)insertActionItemsInMenu:(NSMenu *)menu atIndex:(NSInteger)index { |
| 201 | | NSMenuItem *breakItem = [menu insertItemWithTitle:@"Toggle Breakpoint at Cursor" |
| 202 | | action:@selector(debugToggleBreakpoint:) |
| 203 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey] |
| 204 | | atIndex:index++]; |
| 205 | | [breakItem setKeyEquivalentModifierMask:0]; |
| 206 | | |
| 207 | | NSMenuItem *disableItem = [menu insertItemWithTitle:@"Disable Breakpoint at Cursor" |
| 208 | | action:@selector(debugToggleBreakpointEnable:) |
| 209 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey] |
| 210 | | atIndex:index++]; |
| 211 | | [disableItem setKeyEquivalentModifierMask:NSShiftKeyMask]; |
| 212 | | |
| 213 | | NSMenu *runMenu = [[menu itemWithTitle:@"Run"] submenu]; |
| 214 | | NSMenuItem *runItem; |
| 215 | | if (runMenu != nil) { |
| 216 | | runItem = [runMenu addItemWithTitle:@"to Cursor" |
| 217 | | action:@selector(debugRunToCursor:) |
| 218 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF4FunctionKey]]; |
| 219 | | } else { |
| 220 | | runItem = [menu insertItemWithTitle:@"Run to Cursor" |
| 221 | | action:@selector(debugRunToCursor:) |
| 222 | | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF4FunctionKey] |
| 223 | | atIndex:index++]; |
| 224 | | } |
| 225 | | [runItem setKeyEquivalentModifierMask:0]; |
| 226 | | |
| 227 | | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 228 | | |
| 229 | | NSMenuItem *rawItem = [menu insertItemWithTitle:@"Show Raw Opcodes" |
| 230 | | action:@selector(showRightColumn:) |
| 231 | | keyEquivalent:@"r" |
| 232 | | atIndex:index++]; |
| 233 | | [rawItem setTarget:self]; |
| 234 | | [rawItem setTag:DASM_RIGHTCOL_RAW]; |
| 235 | | |
| 236 | | NSMenuItem *encItem = [menu insertItemWithTitle:@"Show Encrypted Opcodes" |
| 237 | | action:@selector(showRightColumn:) |
| 238 | | keyEquivalent:@"e" |
| 239 | | atIndex:index++]; |
| 240 | | [encItem setTarget:self]; |
| 241 | | [encItem setTag:DASM_RIGHTCOL_ENCRYPTED]; |
| 242 | | |
| 243 | | NSMenuItem *commentsItem = [menu insertItemWithTitle:@"Show Comments" |
| 244 | | action:@selector(showRightColumn:) |
| 245 | | keyEquivalent:@"n" |
| 246 | | atIndex:index++]; |
| 247 | | [commentsItem setTarget:self]; |
| 248 | | [commentsItem setTag:DASM_RIGHTCOL_COMMENTS]; |
| 249 | | |
| 250 | | if (index < [menu numberOfItems]) |
| 251 | | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 252 | | } |
| 253 | | |
| 254 | | |
| 255 | | - (void)insertSubviewItemsInMenu:(NSMenu *)menu atIndex:(NSInteger)index { |
| 256 | | for (const debug_view_source *source = view->source_list().first(); source != NULL; source = source->next()) |
| 257 | | { |
| 258 | | [[menu insertItemWithTitle:[NSString stringWithUTF8String:source->name()] |
| 259 | | action:NULL |
| 260 | | keyEquivalent:@"" |
| 261 | | atIndex:index++] setTag:view->source_list().indexof(*source)]; |
| 262 | | } |
| 263 | | if (index < [menu numberOfItems]) |
| 264 | | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 265 | | } |
| 266 | | |
| 267 | | @end |
trunk/src/osd/modules/debugger/osx/disassemblyview.mm
| r0 | r250165 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Vas Crabb |
| 3 | //============================================================ |
| 4 | // |
| 5 | // disassemblyview.m - MacOS X Cocoa debug window handling |
| 6 | // |
| 7 | //============================================================ |
| 8 | |
| 9 | #import "disassemblyview.h" |
| 10 | |
| 11 | #include "debug/debugvw.h" |
| 12 | |
| 13 | |
| 14 | @implementation MAMEDisassemblyView |
| 15 | |
| 16 | - (id)initWithFrame:(NSRect)f machine:(running_machine &)m { |
| 17 | if (!(self = [super initWithFrame:f type:DVT_DISASSEMBLY machine:m wholeLineScroll:NO])) |
| 18 | return nil; |
| 19 | return self; |
| 20 | } |
| 21 | |
| 22 | |
| 23 | - (void)dealloc { |
| 24 | [super dealloc]; |
| 25 | } |
| 26 | |
| 27 | |
| 28 | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 29 | SEL const action = [item action]; |
| 30 | |
| 31 | if (action == @selector(showRightColumn:)) |
| 32 | { |
| 33 | [item setState:((downcast<debug_view_disasm *>(view)->right_column() == [item tag]) ? NSOnState : NSOffState)]; |
| 34 | return YES; |
| 35 | } |
| 36 | else |
| 37 | { |
| 38 | return [super validateMenuItem:item]; |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | |
| 43 | - (NSSize)maximumFrameSize { |
| 44 | debug_view_xy max(0, 0); |
| 45 | debug_view_source const *source = view->source(); |
| 46 | for (debug_view_source const *source = view->first_source(); source != NULL; source = source->next()) |
| 47 | { |
| 48 | view->set_source(*source); |
| 49 | debug_view_xy const current = view->total_size(); |
| 50 | max.x = MAX(max.x, current.x); |
| 51 | max.y = MAX(max.y, current.y); |
| 52 | } |
| 53 | view->set_source(*source); |
| 54 | return NSMakeSize(ceil((max.x * fontWidth) + (2 * [textContainer lineFragmentPadding])), |
| 55 | ceil(max.y * fontHeight)); |
| 56 | } |
| 57 | |
| 58 | |
| 59 | - (void)addContextMenuItemsToMenu:(NSMenu *)menu { |
| 60 | NSMenuItem *item; |
| 61 | |
| 62 | [super addContextMenuItemsToMenu:menu]; |
| 63 | |
| 64 | if ([menu numberOfItems] > 0) |
| 65 | [menu addItem:[NSMenuItem separatorItem]]; |
| 66 | |
| 67 | item = [menu addItemWithTitle:@"Toggle Breakpoint" |
| 68 | action:@selector(debugToggleBreakpoint:) |
| 69 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey]]; |
| 70 | [item setKeyEquivalentModifierMask:0]; |
| 71 | |
| 72 | item = [menu addItemWithTitle:@"Disable Breakpoint" |
| 73 | action:@selector(debugToggleBreakpointEnable:) |
| 74 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey]]; |
| 75 | [item setKeyEquivalentModifierMask:NSShiftKeyMask]; |
| 76 | |
| 77 | [menu addItem:[NSMenuItem separatorItem]]; |
| 78 | |
| 79 | item = [menu addItemWithTitle:@"Run to Cursor" |
| 80 | action:@selector(debugRunToCursor:) |
| 81 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF4FunctionKey]]; |
| 82 | [item setKeyEquivalentModifierMask:0]; |
| 83 | |
| 84 | [menu addItem:[NSMenuItem separatorItem]]; |
| 85 | |
| 86 | item = [menu addItemWithTitle:@"Raw Opcodes" |
| 87 | action:@selector(showRightColumn:) |
| 88 | keyEquivalent:@"r"]; |
| 89 | [item setTarget:self]; |
| 90 | [item setTag:DASM_RIGHTCOL_RAW]; |
| 91 | |
| 92 | item = [menu addItemWithTitle:@"Encrypted Opcodes" |
| 93 | action:@selector(showRightColumn:) |
| 94 | keyEquivalent:@"e"]; |
| 95 | [item setTarget:self]; |
| 96 | [item setTag:DASM_RIGHTCOL_ENCRYPTED]; |
| 97 | |
| 98 | item = [menu addItemWithTitle:@"Comments" |
| 99 | action:@selector(showRightColumn:) |
| 100 | keyEquivalent:@"n"]; |
| 101 | [item setTarget:self]; |
| 102 | [item setTag:DASM_RIGHTCOL_COMMENTS]; |
| 103 | } |
| 104 | |
| 105 | |
| 106 | - (NSString *)selectedSubviewName { |
| 107 | const debug_view_source *source = view->source(); |
| 108 | if (source != NULL) |
| 109 | return [NSString stringWithUTF8String:source->name()]; |
| 110 | else |
| 111 | return @""; |
| 112 | } |
| 113 | |
| 114 | |
| 115 | - (int)selectedSubviewIndex { |
| 116 | const debug_view_source *source = view->source(); |
| 117 | if (source != NULL) |
| 118 | return view->source_list().indexof(*source); |
| 119 | else |
| 120 | return -1; |
| 121 | } |
| 122 | |
| 123 | |
| 124 | - (void)selectSubviewAtIndex:(int)index { |
| 125 | const int selected = view->source_list().indexof(*view->source()); |
| 126 | if (selected != index) { |
| 127 | view->set_source(*view->source_list().find(index)); |
| 128 | if ([[self window] firstResponder] != self) |
| 129 | view->set_cursor_visible(false); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | |
| 134 | - (BOOL)selectSubviewForDevice:(device_t *)device { |
| 135 | debug_view_source const *const source = view->source_for_device(device); |
| 136 | if (source != NULL) |
| 137 | { |
| 138 | if (view->source() != source) |
| 139 | { |
| 140 | view->set_source(*source); |
| 141 | if ([[self window] firstResponder] != self) |
| 142 | view->set_cursor_visible(false); |
| 143 | } |
| 144 | return YES; |
| 145 | } |
| 146 | else |
| 147 | { |
| 148 | return NO; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | |
| 153 | - (BOOL)selectSubviewForSpace:(address_space *)space { |
| 154 | if (space == NULL) return NO; |
| 155 | debug_view_disasm_source const *source = downcast<debug_view_disasm_source const *>(view->first_source()); |
| 156 | while ((source != NULL) && (&source->space() != space)) |
| 157 | source = downcast<debug_view_disasm_source *>(source->next()); |
| 158 | if (source != NULL) |
| 159 | { |
| 160 | if (view->source() != source) |
| 161 | { |
| 162 | view->set_source(*source); |
| 163 | if ([[self window] firstResponder] != self) |
| 164 | view->set_cursor_visible(false); |
| 165 | } |
| 166 | return YES; |
| 167 | } |
| 168 | else |
| 169 | { |
| 170 | return NO; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | |
| 175 | - (NSString *)expression { |
| 176 | return [NSString stringWithUTF8String:downcast<debug_view_disasm *>(view)->expression()]; |
| 177 | } |
| 178 | |
| 179 | |
| 180 | - (void)setExpression:(NSString *)exp { |
| 181 | downcast<debug_view_disasm *>(view)->set_expression([exp UTF8String]); |
| 182 | } |
| 183 | |
| 184 | |
| 185 | - (debug_view_disasm_source const *)source { |
| 186 | return downcast<debug_view_disasm_source const *>(view->source()); |
| 187 | } |
| 188 | |
| 189 | |
| 190 | - (offs_t)selectedAddress { |
| 191 | return downcast<debug_view_disasm *>(view)->selected_address(); |
| 192 | } |
| 193 | |
| 194 | |
| 195 | - (IBAction)showRightColumn:(id)sender { |
| 196 | downcast<debug_view_disasm *>(view)->set_right_column((disasm_right_column) [sender tag]); |
| 197 | } |
| 198 | |
| 199 | |
| 200 | - (void)insertActionItemsInMenu:(NSMenu *)menu atIndex:(NSInteger)index { |
| 201 | NSMenuItem *breakItem = [menu insertItemWithTitle:@"Toggle Breakpoint at Cursor" |
| 202 | action:@selector(debugToggleBreakpoint:) |
| 203 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey] |
| 204 | atIndex:index++]; |
| 205 | [breakItem setKeyEquivalentModifierMask:0]; |
| 206 | |
| 207 | NSMenuItem *disableItem = [menu insertItemWithTitle:@"Disable Breakpoint at Cursor" |
| 208 | action:@selector(debugToggleBreakpointEnable:) |
| 209 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey] |
| 210 | atIndex:index++]; |
| 211 | [disableItem setKeyEquivalentModifierMask:NSShiftKeyMask]; |
| 212 | |
| 213 | NSMenu *runMenu = [[menu itemWithTitle:@"Run"] submenu]; |
| 214 | NSMenuItem *runItem; |
| 215 | if (runMenu != nil) { |
| 216 | runItem = [runMenu addItemWithTitle:@"to Cursor" |
| 217 | action:@selector(debugRunToCursor:) |
| 218 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF4FunctionKey]]; |
| 219 | } else { |
| 220 | runItem = [menu insertItemWithTitle:@"Run to Cursor" |
| 221 | action:@selector(debugRunToCursor:) |
| 222 | keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF4FunctionKey] |
| 223 | atIndex:index++]; |
| 224 | } |
| 225 | [runItem setKeyEquivalentModifierMask:0]; |
| 226 | |
| 227 | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 228 | |
| 229 | NSMenuItem *rawItem = [menu insertItemWithTitle:@"Show Raw Opcodes" |
| 230 | action:@selector(showRightColumn:) |
| 231 | keyEquivalent:@"r" |
| 232 | atIndex:index++]; |
| 233 | [rawItem setTarget:self]; |
| 234 | [rawItem setTag:DASM_RIGHTCOL_RAW]; |
| 235 | |
| 236 | NSMenuItem *encItem = [menu insertItemWithTitle:@"Show Encrypted Opcodes" |
| 237 | action:@selector(showRightColumn:) |
| 238 | keyEquivalent:@"e" |
| 239 | atIndex:index++]; |
| 240 | [encItem setTarget:self]; |
| 241 | [encItem setTag:DASM_RIGHTCOL_ENCRYPTED]; |
| 242 | |
| 243 | NSMenuItem *commentsItem = [menu insertItemWithTitle:@"Show Comments" |
| 244 | action:@selector(showRightColumn:) |
| 245 | keyEquivalent:@"n" |
| 246 | atIndex:index++]; |
| 247 | [commentsItem setTarget:self]; |
| 248 | [commentsItem setTag:DASM_RIGHTCOL_COMMENTS]; |
| 249 | |
| 250 | if (index < [menu numberOfItems]) |
| 251 | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 252 | } |
| 253 | |
| 254 | |
| 255 | - (void)insertSubviewItemsInMenu:(NSMenu *)menu atIndex:(NSInteger)index { |
| 256 | for (const debug_view_source *source = view->source_list().first(); source != NULL; source = source->next()) |
| 257 | { |
| 258 | [[menu insertItemWithTitle:[NSString stringWithUTF8String:source->name()] |
| 259 | action:NULL |
| 260 | keyEquivalent:@"" |
| 261 | atIndex:index++] setTag:view->source_list().indexof(*source)]; |
| 262 | } |
| 263 | if (index < [menu numberOfItems]) |
| 264 | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 265 | } |
| 266 | |
| 267 | @end |
trunk/src/osd/modules/debugger/osx/disassemblyviewer.m
| r250164 | r250165 | |
| 1 | | // license:BSD-3-Clause |
| 2 | | // copyright-holders:Vas Crabb |
| 3 | | //============================================================ |
| 4 | | // |
| 5 | | // disassemblyviewer.m - MacOS X Cocoa debug window handling |
| 6 | | // |
| 7 | | //============================================================ |
| 8 | | |
| 9 | | #import "disassemblyviewer.h" |
| 10 | | |
| 11 | | #import "debugconsole.h" |
| 12 | | #import "debugview.h" |
| 13 | | #import "disassemblyview.h" |
| 14 | | |
| 15 | | #include "debugger.h" |
| 16 | | #include "debug/debugcon.h" |
| 17 | | #include "debug/debugcpu.h" |
| 18 | | |
| 19 | | |
| 20 | | @implementation MAMEDisassemblyViewer |
| 21 | | |
| 22 | | - (id)initWithMachine:(running_machine &)m console:(MAMEDebugConsole *)c { |
| 23 | | NSScrollView *dasmScroll; |
| 24 | | NSView *expressionContainer; |
| 25 | | NSPopUpButton *actionButton; |
| 26 | | NSRect expressionFrame; |
| 27 | | |
| 28 | | if (!(self = [super initWithMachine:m title:@"Disassembly" console:c])) |
| 29 | | return nil; |
| 30 | | NSRect const contentBounds = [[window contentView] bounds]; |
| 31 | | NSFont *const defaultFont = [[MAMEDebugView class] defaultFontForMachine:m]; |
| 32 | | |
| 33 | | // create the expression field |
| 34 | | expressionField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; |
| 35 | | [expressionField setAutoresizingMask:(NSViewWidthSizable | NSViewMaxXMargin | NSViewMinYMargin)]; |
| 36 | | [expressionField setFont:defaultFont]; |
| 37 | | [expressionField setFocusRingType:NSFocusRingTypeNone]; |
| 38 | | [expressionField setTarget:self]; |
| 39 | | [expressionField setAction:@selector(doExpression:)]; |
| 40 | | [expressionField setDelegate:self]; |
| 41 | | [expressionField sizeToFit]; |
| 42 | | |
| 43 | | // create the subview popup |
| 44 | | subviewButton = [[NSPopUpButton alloc] initWithFrame:NSOffsetRect(expressionFrame, |
| 45 | | expressionFrame.size.width, |
| 46 | | 0)]; |
| 47 | | [subviewButton setAutoresizingMask:(NSViewWidthSizable | NSViewMinXMargin | NSViewMinYMargin)]; |
| 48 | | [subviewButton setBezelStyle:NSShadowlessSquareBezelStyle]; |
| 49 | | [subviewButton setFocusRingType:NSFocusRingTypeNone]; |
| 50 | | [subviewButton setFont:defaultFont]; |
| 51 | | [subviewButton setTarget:self]; |
| 52 | | [subviewButton setAction:@selector(changeSubview:)]; |
| 53 | | [[subviewButton cell] setArrowPosition:NSPopUpArrowAtBottom]; |
| 54 | | [subviewButton sizeToFit]; |
| 55 | | |
| 56 | | // adjust sizes to make it fit nicely |
| 57 | | expressionFrame = [expressionField frame]; |
| 58 | | expressionFrame.size.height = MAX(expressionFrame.size.height, [subviewButton frame].size.height); |
| 59 | | expressionFrame.size.width = (contentBounds.size.width - expressionFrame.size.height) / 2; |
| 60 | | [expressionField setFrame:expressionFrame]; |
| 61 | | expressionFrame.origin.x = expressionFrame.size.width; |
| 62 | | expressionFrame.size.width = contentBounds.size.width - expressionFrame.size.height - expressionFrame.origin.x; |
| 63 | | [subviewButton setFrame:expressionFrame]; |
| 64 | | |
| 65 | | // create a container for the expression field and subview popup |
| 66 | | expressionFrame = NSMakeRect(expressionFrame.size.height, |
| 67 | | contentBounds.size.height - expressionFrame.size.height, |
| 68 | | contentBounds.size.width - expressionFrame.size.height, |
| 69 | | expressionFrame.size.height); |
| 70 | | expressionContainer = [[NSView alloc] initWithFrame:expressionFrame]; |
| 71 | | [expressionContainer setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; |
| 72 | | [expressionContainer addSubview:expressionField]; |
| 73 | | [expressionField release]; |
| 74 | | [expressionContainer addSubview:subviewButton]; |
| 75 | | [subviewButton release]; |
| 76 | | [[window contentView] addSubview:expressionContainer]; |
| 77 | | [expressionContainer release]; |
| 78 | | |
| 79 | | // create the disassembly view |
| 80 | | dasmView = [[MAMEDisassemblyView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; |
| 81 | | [dasmView insertSubviewItemsInMenu:[subviewButton menu] atIndex:0]; |
| 82 | | dasmScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, |
| 83 | | 0, |
| 84 | | contentBounds.size.width, |
| 85 | | expressionFrame.origin.y)]; |
| 86 | | [dasmScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 87 | | [dasmScroll setHasHorizontalScroller:YES]; |
| 88 | | [dasmScroll setHasVerticalScroller:YES]; |
| 89 | | [dasmScroll setAutohidesScrollers:YES]; |
| 90 | | [dasmScroll setBorderType:NSNoBorder]; |
| 91 | | [dasmScroll setDocumentView:dasmView]; |
| 92 | | [dasmView release]; |
| 93 | | [[window contentView] addSubview:dasmScroll]; |
| 94 | | [dasmScroll release]; |
| 95 | | |
| 96 | | // create the action popup |
| 97 | | actionButton = [[self class] newActionButtonWithFrame:NSMakeRect(0, |
| 98 | | expressionFrame.origin.y, |
| 99 | | expressionFrame.size.height, |
| 100 | | expressionFrame.size.height)]; |
| 101 | | [actionButton setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)]; |
| 102 | | [actionButton setFont:[NSFont systemFontOfSize:[defaultFont pointSize]]]; |
| 103 | | [dasmView insertActionItemsInMenu:[actionButton menu] atIndex:1]; |
| 104 | | [[window contentView] addSubview:actionButton]; |
| 105 | | [actionButton release]; |
| 106 | | |
| 107 | | // set default state |
| 108 | | [dasmView selectSubviewForDevice:debug_cpu_get_visible_cpu(*machine)]; |
| 109 | | [dasmView setExpression:@"curpc"]; |
| 110 | | [expressionField setStringValue:@"curpc"]; |
| 111 | | [expressionField selectText:self]; |
| 112 | | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[dasmView selectedSubviewIndex]]]; |
| 113 | | [window makeFirstResponder:expressionField]; |
| 114 | | [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; |
| 115 | | |
| 116 | | // calculate the optimal size for everything |
| 117 | | NSSize const desired = [NSScrollView frameSizeForContentSize:[dasmView maximumFrameSize] |
| 118 | | hasHorizontalScroller:YES |
| 119 | | hasVerticalScroller:YES |
| 120 | | borderType:[dasmScroll borderType]]; |
| 121 | | [self cascadeWindowWithDesiredSize:desired forView:dasmScroll]; |
| 122 | | |
| 123 | | // don't forget the result |
| 124 | | return self; |
| 125 | | } |
| 126 | | |
| 127 | | |
| 128 | | - (void)dealloc { |
| 129 | | [super dealloc]; |
| 130 | | } |
| 131 | | |
| 132 | | |
| 133 | | - (id <MAMEDebugViewExpressionSupport>)documentView { |
| 134 | | return dasmView; |
| 135 | | } |
| 136 | | |
| 137 | | |
| 138 | | - (IBAction)debugNewMemoryWindow:(id)sender { |
| 139 | | debug_view_disasm_source const *source = [dasmView source]; |
| 140 | | [console debugNewMemoryWindowForSpace:&source->space() |
| 141 | | device:&source->device() |
| 142 | | expression:nil]; |
| 143 | | } |
| 144 | | |
| 145 | | |
| 146 | | - (IBAction)debugNewDisassemblyWindow:(id)sender { |
| 147 | | debug_view_disasm_source const *source = [dasmView source]; |
| 148 | | [console debugNewDisassemblyWindowForSpace:&source->space() |
| 149 | | device:&source->device() |
| 150 | | expression:[dasmView expression]]; |
| 151 | | } |
| 152 | | |
| 153 | | |
| 154 | | - (BOOL)selectSubviewForDevice:(device_t *)device { |
| 155 | | BOOL const result = [dasmView selectSubviewForDevice:device]; |
| 156 | | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[dasmView selectedSubviewIndex]]]; |
| 157 | | [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; |
| 158 | | return result; |
| 159 | | } |
| 160 | | |
| 161 | | |
| 162 | | - (BOOL)selectSubviewForSpace:(address_space *)space { |
| 163 | | BOOL const result = [dasmView selectSubviewForSpace:space]; |
| 164 | | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[dasmView selectedSubviewIndex]]]; |
| 165 | | [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; |
| 166 | | return result; |
| 167 | | } |
| 168 | | |
| 169 | | |
| 170 | | - (IBAction)debugToggleBreakpoint:(id)sender { |
| 171 | | if ([dasmView cursorVisible]) |
| 172 | | { |
| 173 | | device_t &device = [dasmView source]->device(); |
| 174 | | offs_t const address = [dasmView selectedAddress]; |
| 175 | | device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:address forDevice:device]; |
| 176 | | |
| 177 | | // if it doesn't exist, add a new one |
| 178 | | if (bp == NULL) |
| 179 | | { |
| 180 | | UINT32 const bpnum = device.debug()->breakpoint_set(address, NULL, NULL); |
| 181 | | debug_console_printf(*machine, "Breakpoint %X set\n", bpnum); |
| 182 | | } |
| 183 | | else |
| 184 | | { |
| 185 | | int const bpnum = bp->index(); |
| 186 | | device.debug()->breakpoint_clear(bpnum); |
| 187 | | debug_console_printf(*machine, "Breakpoint %X cleared\n", (UINT32)bpnum); |
| 188 | | } |
| 189 | | |
| 190 | | // fail to do this and the display doesn't update |
| 191 | | machine->debug_view().update_all(); |
| 192 | | debugger_refresh_display(*machine); |
| 193 | | } |
| 194 | | } |
| 195 | | |
| 196 | | |
| 197 | | - (IBAction)debugToggleBreakpointEnable:(id)sender { |
| 198 | | if ([dasmView cursorVisible]) |
| 199 | | { |
| 200 | | device_t &device = [dasmView source]->device(); |
| 201 | | offs_t const address = [dasmView selectedAddress]; |
| 202 | | device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:address forDevice:device]; |
| 203 | | if (bp != NULL) |
| 204 | | { |
| 205 | | device.debug()->breakpoint_enable(bp->index(), !bp->enabled()); |
| 206 | | debug_console_printf(*machine, |
| 207 | | "Breakpoint %X %s\n", |
| 208 | | (UINT32)bp->index(), |
| 209 | | bp->enabled() ? "enabled" : "disabled"); |
| 210 | | machine->debug_view().update_all(); |
| 211 | | debugger_refresh_display(*machine); |
| 212 | | } |
| 213 | | } |
| 214 | | } |
| 215 | | |
| 216 | | |
| 217 | | - (IBAction)debugRunToCursor:(id)sender { |
| 218 | | if ([dasmView cursorVisible]) |
| 219 | | [dasmView source]->device().debug()->go([dasmView selectedAddress]); |
| 220 | | } |
| 221 | | |
| 222 | | |
| 223 | | - (IBAction)changeSubview:(id)sender { |
| 224 | | [dasmView selectSubviewAtIndex:[[sender selectedItem] tag]]; |
| 225 | | [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; |
| 226 | | } |
| 227 | | |
| 228 | | |
| 229 | | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 230 | | SEL const action = [item action]; |
| 231 | | BOOL const inContextMenu = ([item menu] == [dasmView menu]); |
| 232 | | BOOL const haveCursor = [dasmView cursorVisible]; |
| 233 | | |
| 234 | | device_debug::breakpoint *breakpoint = NULL; |
| 235 | | if (haveCursor) |
| 236 | | { |
| 237 | | breakpoint = [[self class] findBreakpointAtAddress:[dasmView selectedAddress] |
| 238 | | forDevice:[dasmView source]->device()]; |
| 239 | | } |
| 240 | | |
| 241 | | if (action == @selector(debugToggleBreakpoint:)) |
| 242 | | { |
| 243 | | if (haveCursor) |
| 244 | | { |
| 245 | | if (breakpoint != NULL) |
| 246 | | { |
| 247 | | if (inContextMenu) |
| 248 | | [item setTitle:@"Clear Breakpoint"]; |
| 249 | | else |
| 250 | | [item setTitle:@"Clear Breakpoint at Cursor"]; |
| 251 | | } |
| 252 | | else |
| 253 | | { |
| 254 | | if (inContextMenu) |
| 255 | | [item setTitle:@"Set Breakpoint"]; |
| 256 | | else |
| 257 | | [item setTitle:@"Set Breakpoint at Cursor"]; |
| 258 | | } |
| 259 | | } |
| 260 | | else |
| 261 | | { |
| 262 | | if (inContextMenu) |
| 263 | | [item setTitle:@"Toggle Breakpoint"]; |
| 264 | | else |
| 265 | | [item setTitle:@"Toggle Breakpoint at Cursor"]; |
| 266 | | } |
| 267 | | return haveCursor; |
| 268 | | } |
| 269 | | else if (action == @selector(debugToggleBreakpointEnable:)) |
| 270 | | { |
| 271 | | if ((breakpoint != NULL) && !breakpoint->enabled()) |
| 272 | | { |
| 273 | | if (inContextMenu) |
| 274 | | [item setTitle:@"Enable Breakpoint"]; |
| 275 | | else |
| 276 | | [item setTitle:@"Enable Breakpoint at Cursor"]; |
| 277 | | } |
| 278 | | else |
| 279 | | { |
| 280 | | if (inContextMenu) |
| 281 | | [item setTitle:@"Disable Breakpoint"]; |
| 282 | | else |
| 283 | | [item setTitle:@"Disable Breakpoint at Cursor"]; |
| 284 | | } |
| 285 | | return breakpoint != NULL; |
| 286 | | } |
| 287 | | else |
| 288 | | { |
| 289 | | return YES; |
| 290 | | } |
| 291 | | } |
| 292 | | |
| 293 | | @end |
trunk/src/osd/modules/debugger/osx/disassemblyviewer.mm
| r0 | r250165 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Vas Crabb |
| 3 | //============================================================ |
| 4 | // |
| 5 | // disassemblyviewer.m - MacOS X Cocoa debug window handling |
| 6 | // |
| 7 | //============================================================ |
| 8 | |
| 9 | #import "disassemblyviewer.h" |
| 10 | |
| 11 | #import "debugconsole.h" |
| 12 | #import "debugview.h" |
| 13 | #import "disassemblyview.h" |
| 14 | |
| 15 | #include "debugger.h" |
| 16 | #include "debug/debugcon.h" |
| 17 | #include "debug/debugcpu.h" |
| 18 | |
| 19 | |
| 20 | @implementation MAMEDisassemblyViewer |
| 21 | |
| 22 | - (id)initWithMachine:(running_machine &)m console:(MAMEDebugConsole *)c { |
| 23 | NSScrollView *dasmScroll; |
| 24 | NSView *expressionContainer; |
| 25 | NSPopUpButton *actionButton; |
| 26 | NSRect expressionFrame; |
| 27 | |
| 28 | if (!(self = [super initWithMachine:m title:@"Disassembly" console:c])) |
| 29 | return nil; |
| 30 | NSRect const contentBounds = [[window contentView] bounds]; |
| 31 | NSFont *const defaultFont = [[MAMEDebugView class] defaultFontForMachine:m]; |
| 32 | |
| 33 | // create the expression field |
| 34 | expressionField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; |
| 35 | [expressionField setAutoresizingMask:(NSViewWidthSizable | NSViewMaxXMargin | NSViewMinYMargin)]; |
| 36 | [expressionField setFont:defaultFont]; |
| 37 | [expressionField setFocusRingType:NSFocusRingTypeNone]; |
| 38 | [expressionField setTarget:self]; |
| 39 | [expressionField setAction:@selector(doExpression:)]; |
| 40 | [expressionField setDelegate:self]; |
| 41 | [expressionField sizeToFit]; |
| 42 | |
| 43 | // create the subview popup |
| 44 | subviewButton = [[NSPopUpButton alloc] initWithFrame:NSOffsetRect(expressionFrame, |
| 45 | expressionFrame.size.width, |
| 46 | 0)]; |
| 47 | [subviewButton setAutoresizingMask:(NSViewWidthSizable | NSViewMinXMargin | NSViewMinYMargin)]; |
| 48 | [subviewButton setBezelStyle:NSShadowlessSquareBezelStyle]; |
| 49 | [subviewButton setFocusRingType:NSFocusRingTypeNone]; |
| 50 | [subviewButton setFont:defaultFont]; |
| 51 | [subviewButton setTarget:self]; |
| 52 | [subviewButton setAction:@selector(changeSubview:)]; |
| 53 | [[subviewButton cell] setArrowPosition:NSPopUpArrowAtBottom]; |
| 54 | [subviewButton sizeToFit]; |
| 55 | |
| 56 | // adjust sizes to make it fit nicely |
| 57 | expressionFrame = [expressionField frame]; |
| 58 | expressionFrame.size.height = MAX(expressionFrame.size.height, [subviewButton frame].size.height); |
| 59 | expressionFrame.size.width = (contentBounds.size.width - expressionFrame.size.height) / 2; |
| 60 | [expressionField setFrame:expressionFrame]; |
| 61 | expressionFrame.origin.x = expressionFrame.size.width; |
| 62 | expressionFrame.size.width = contentBounds.size.width - expressionFrame.size.height - expressionFrame.origin.x; |
| 63 | [subviewButton setFrame:expressionFrame]; |
| 64 | |
| 65 | // create a container for the expression field and subview popup |
| 66 | expressionFrame = NSMakeRect(expressionFrame.size.height, |
| 67 | contentBounds.size.height - expressionFrame.size.height, |
| 68 | contentBounds.size.width - expressionFrame.size.height, |
| 69 | expressionFrame.size.height); |
| 70 | expressionContainer = [[NSView alloc] initWithFrame:expressionFrame]; |
| 71 | [expressionContainer setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; |
| 72 | [expressionContainer addSubview:expressionField]; |
| 73 | [expressionField release]; |
| 74 | [expressionContainer addSubview:subviewButton]; |
| 75 | [subviewButton release]; |
| 76 | [[window contentView] addSubview:expressionContainer]; |
| 77 | [expressionContainer release]; |
| 78 | |
| 79 | // create the disassembly view |
| 80 | dasmView = [[MAMEDisassemblyView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; |
| 81 | [dasmView insertSubviewItemsInMenu:[subviewButton menu] atIndex:0]; |
| 82 | dasmScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, |
| 83 | 0, |
| 84 | contentBounds.size.width, |
| 85 | expressionFrame.origin.y)]; |
| 86 | [dasmScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 87 | [dasmScroll setHasHorizontalScroller:YES]; |
| 88 | [dasmScroll setHasVerticalScroller:YES]; |
| 89 | [dasmScroll setAutohidesScrollers:YES]; |
| 90 | [dasmScroll setBorderType:NSNoBorder]; |
| 91 | [dasmScroll setDocumentView:dasmView]; |
| 92 | [dasmView release]; |
| 93 | [[window contentView] addSubview:dasmScroll]; |
| 94 | [dasmScroll release]; |
| 95 | |
| 96 | // create the action popup |
| 97 | actionButton = [[self class] newActionButtonWithFrame:NSMakeRect(0, |
| 98 | expressionFrame.origin.y, |
| 99 | expressionFrame.size.height, |
| 100 | expressionFrame.size.height)]; |
| 101 | [actionButton setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)]; |
| 102 | [actionButton setFont:[NSFont systemFontOfSize:[defaultFont pointSize]]]; |
| 103 | [dasmView insertActionItemsInMenu:[actionButton menu] atIndex:1]; |
| 104 | [[window contentView] addSubview:actionButton]; |
| 105 | [actionButton release]; |
| 106 | |
| 107 | // set default state |
| 108 | [dasmView selectSubviewForDevice:debug_cpu_get_visible_cpu(*machine)]; |
| 109 | [dasmView setExpression:@"curpc"]; |
| 110 | [expressionField setStringValue:@"curpc"]; |
| 111 | [expressionField selectText:self]; |
| 112 | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[dasmView selectedSubviewIndex]]]; |
| 113 | [window makeFirstResponder:expressionField]; |
| 114 | [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; |
| 115 | |
| 116 | // calculate the optimal size for everything |
| 117 | NSSize const desired = [NSScrollView frameSizeForContentSize:[dasmView maximumFrameSize] |
| 118 | hasHorizontalScroller:YES |
| 119 | hasVerticalScroller:YES |
| 120 | borderType:[dasmScroll borderType]]; |
| 121 | [self cascadeWindowWithDesiredSize:desired forView:dasmScroll]; |
| 122 | |
| 123 | // don't forget the result |
| 124 | return self; |
| 125 | } |
| 126 | |
| 127 | |
| 128 | - (void)dealloc { |
| 129 | [super dealloc]; |
| 130 | } |
| 131 | |
| 132 | |
| 133 | - (id <MAMEDebugViewExpressionSupport>)documentView { |
| 134 | return dasmView; |
| 135 | } |
| 136 | |
| 137 | |
| 138 | - (IBAction)debugNewMemoryWindow:(id)sender { |
| 139 | debug_view_disasm_source const *source = [dasmView source]; |
| 140 | [console debugNewMemoryWindowForSpace:&source->space() |
| 141 | device:&source->device() |
| 142 | expression:nil]; |
| 143 | } |
| 144 | |
| 145 | |
| 146 | - (IBAction)debugNewDisassemblyWindow:(id)sender { |
| 147 | debug_view_disasm_source const *source = [dasmView source]; |
| 148 | [console debugNewDisassemblyWindowForSpace:&source->space() |
| 149 | device:&source->device() |
| 150 | expression:[dasmView expression]]; |
| 151 | } |
| 152 | |
| 153 | |
| 154 | - (BOOL)selectSubviewForDevice:(device_t *)device { |
| 155 | BOOL const result = [dasmView selectSubviewForDevice:device]; |
| 156 | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[dasmView selectedSubviewIndex]]]; |
| 157 | [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; |
| 158 | return result; |
| 159 | } |
| 160 | |
| 161 | |
| 162 | - (BOOL)selectSubviewForSpace:(address_space *)space { |
| 163 | BOOL const result = [dasmView selectSubviewForSpace:space]; |
| 164 | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[dasmView selectedSubviewIndex]]]; |
| 165 | [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; |
| 166 | return result; |
| 167 | } |
| 168 | |
| 169 | |
| 170 | - (IBAction)debugToggleBreakpoint:(id)sender { |
| 171 | if ([dasmView cursorVisible]) |
| 172 | { |
| 173 | device_t &device = [dasmView source]->device(); |
| 174 | offs_t const address = [dasmView selectedAddress]; |
| 175 | device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:address forDevice:device]; |
| 176 | |
| 177 | // if it doesn't exist, add a new one |
| 178 | if (bp == NULL) |
| 179 | { |
| 180 | UINT32 const bpnum = device.debug()->breakpoint_set(address, NULL, NULL); |
| 181 | debug_console_printf(*machine, "Breakpoint %X set\n", bpnum); |
| 182 | } |
| 183 | else |
| 184 | { |
| 185 | int const bpnum = bp->index(); |
| 186 | device.debug()->breakpoint_clear(bpnum); |
| 187 | debug_console_printf(*machine, "Breakpoint %X cleared\n", (UINT32)bpnum); |
| 188 | } |
| 189 | |
| 190 | // fail to do this and the display doesn't update |
| 191 | machine->debug_view().update_all(); |
| 192 | debugger_refresh_display(*machine); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | |
| 197 | - (IBAction)debugToggleBreakpointEnable:(id)sender { |
| 198 | if ([dasmView cursorVisible]) |
| 199 | { |
| 200 | device_t &device = [dasmView source]->device(); |
| 201 | offs_t const address = [dasmView selectedAddress]; |
| 202 | device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:address forDevice:device]; |
| 203 | if (bp != NULL) |
| 204 | { |
| 205 | device.debug()->breakpoint_enable(bp->index(), !bp->enabled()); |
| 206 | debug_console_printf(*machine, |
| 207 | "Breakpoint %X %s\n", |
| 208 | (UINT32)bp->index(), |
| 209 | bp->enabled() ? "enabled" : "disabled"); |
| 210 | machine->debug_view().update_all(); |
| 211 | debugger_refresh_display(*machine); |
| 212 | } |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | |
| 217 | - (IBAction)debugRunToCursor:(id)sender { |
| 218 | if ([dasmView cursorVisible]) |
| 219 | [dasmView source]->device().debug()->go([dasmView selectedAddress]); |
| 220 | } |
| 221 | |
| 222 | |
| 223 | - (IBAction)changeSubview:(id)sender { |
| 224 | [dasmView selectSubviewAtIndex:[[sender selectedItem] tag]]; |
| 225 | [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; |
| 226 | } |
| 227 | |
| 228 | |
| 229 | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 230 | SEL const action = [item action]; |
| 231 | BOOL const inContextMenu = ([item menu] == [dasmView menu]); |
| 232 | BOOL const haveCursor = [dasmView cursorVisible]; |
| 233 | |
| 234 | device_debug::breakpoint *breakpoint = NULL; |
| 235 | if (haveCursor) |
| 236 | { |
| 237 | breakpoint = [[self class] findBreakpointAtAddress:[dasmView selectedAddress] |
| 238 | forDevice:[dasmView source]->device()]; |
| 239 | } |
| 240 | |
| 241 | if (action == @selector(debugToggleBreakpoint:)) |
| 242 | { |
| 243 | if (haveCursor) |
| 244 | { |
| 245 | if (breakpoint != NULL) |
| 246 | { |
| 247 | if (inContextMenu) |
| 248 | [item setTitle:@"Clear Breakpoint"]; |
| 249 | else |
| 250 | [item setTitle:@"Clear Breakpoint at Cursor"]; |
| 251 | } |
| 252 | else |
| 253 | { |
| 254 | if (inContextMenu) |
| 255 | [item setTitle:@"Set Breakpoint"]; |
| 256 | else |
| 257 | [item setTitle:@"Set Breakpoint at Cursor"]; |
| 258 | } |
| 259 | } |
| 260 | else |
| 261 | { |
| 262 | if (inContextMenu) |
| 263 | [item setTitle:@"Toggle Breakpoint"]; |
| 264 | else |
| 265 | [item setTitle:@"Toggle Breakpoint at Cursor"]; |
| 266 | } |
| 267 | return haveCursor; |
| 268 | } |
| 269 | else if (action == @selector(debugToggleBreakpointEnable:)) |
| 270 | { |
| 271 | if ((breakpoint != NULL) && !breakpoint->enabled()) |
| 272 | { |
| 273 | if (inContextMenu) |
| 274 | [item setTitle:@"Enable Breakpoint"]; |
| 275 | else |
| 276 | [item setTitle:@"Enable Breakpoint at Cursor"]; |
| 277 | } |
| 278 | else |
| 279 | { |
| 280 | if (inContextMenu) |
| 281 | [item setTitle:@"Disable Breakpoint"]; |
| 282 | else |
| 283 | [item setTitle:@"Disable Breakpoint at Cursor"]; |
| 284 | } |
| 285 | return breakpoint != NULL; |
| 286 | } |
| 287 | else |
| 288 | { |
| 289 | return YES; |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | @end |
trunk/src/osd/modules/debugger/osx/memoryview.m
| r250164 | r250165 | |
| 1 | | // license:BSD-3-Clause |
| 2 | | // copyright-holders:Vas Crabb |
| 3 | | //============================================================ |
| 4 | | // |
| 5 | | // memoryview.m - MacOS X Cocoa debug window handling |
| 6 | | // |
| 7 | | //============================================================ |
| 8 | | |
| 9 | | #import "memoryview.h" |
| 10 | | |
| 11 | | #include "debug/debugcpu.h" |
| 12 | | #include "debug/debugvw.h" |
| 13 | | |
| 14 | | |
| 15 | | @implementation MAMEMemoryView |
| 16 | | |
| 17 | | - (id)initWithFrame:(NSRect)f machine:(running_machine &)m { |
| 18 | | if (!(self = [super initWithFrame:f type:DVT_MEMORY machine:m wholeLineScroll:NO])) |
| 19 | | return nil; |
| 20 | | return self; |
| 21 | | } |
| 22 | | |
| 23 | | |
| 24 | | - (void)dealloc { |
| 25 | | [super dealloc]; |
| 26 | | } |
| 27 | | |
| 28 | | |
| 29 | | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 30 | | SEL action = [item action]; |
| 31 | | NSInteger tag = [item tag]; |
| 32 | | debug_view_memory *memview = downcast<debug_view_memory *>(view); |
| 33 | | |
| 34 | | if (action == @selector(showChunkSize:)) |
| 35 | | { |
| 36 | | [item setState:((tag == memview->bytes_per_chunk()) ? NSOnState : NSOffState)]; |
| 37 | | return YES; |
| 38 | | } |
| 39 | | else if (action == @selector(showPhysicalAddresses:)) |
| 40 | | { |
| 41 | | [item setState:((tag == memview->physical()) ? NSOnState : NSOffState)]; |
| 42 | | return YES; |
| 43 | | } |
| 44 | | else if (action == @selector(showReverseView:)) |
| 45 | | { |
| 46 | | [item setState:((tag == memview->reverse()) ? NSOnState : NSOffState)]; |
| 47 | | return YES; |
| 48 | | } |
| 49 | | else if (action == @selector(showReverseViewToggle:)) |
| 50 | | { |
| 51 | | [item setState:(memview->reverse() ? NSOnState : NSOffState)]; |
| 52 | | return YES; |
| 53 | | } |
| 54 | | else if (action == @selector(changeBytesPerLine:)) |
| 55 | | { |
| 56 | | return (memview->chunks_per_row() + [item tag]) > 0; |
| 57 | | } |
| 58 | | else |
| 59 | | { |
| 60 | | return [super validateMenuItem:item]; |
| 61 | | } |
| 62 | | } |
| 63 | | |
| 64 | | |
| 65 | | - (NSSize)maximumFrameSize { |
| 66 | | debug_view_xy max(0, 0); |
| 67 | | debug_view_source const *source = view->source(); |
| 68 | | for (debug_view_source const *source = view->first_source(); source != NULL; source = source->next()) |
| 69 | | { |
| 70 | | view->set_source(*source); |
| 71 | | debug_view_xy const current = view->total_size(); |
| 72 | | max.x = MAX(max.x, current.x); |
| 73 | | max.y = MAX(max.y, current.y); |
| 74 | | } |
| 75 | | view->set_source(*source); |
| 76 | | return NSMakeSize(ceil((max.x * fontWidth) + (2 * [textContainer lineFragmentPadding])), |
| 77 | | ceil(max.y * fontHeight)); |
| 78 | | } |
| 79 | | |
| 80 | | |
| 81 | | - (void)addContextMenuItemsToMenu:(NSMenu *)menu { |
| 82 | | [super addContextMenuItemsToMenu:menu]; |
| 83 | | if ([menu numberOfItems] > 0) |
| 84 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 85 | | [self insertActionItemsInMenu:menu atIndex:[menu numberOfItems]]; |
| 86 | | } |
| 87 | | |
| 88 | | |
| 89 | | - (NSString *)selectedSubviewName { |
| 90 | | debug_view_source const *source = view->source(); |
| 91 | | if (source != NULL) |
| 92 | | return [NSString stringWithUTF8String:source->name()]; |
| 93 | | else |
| 94 | | return @""; |
| 95 | | } |
| 96 | | |
| 97 | | |
| 98 | | - (int)selectedSubviewIndex { |
| 99 | | debug_view_source const *source = view->source(); |
| 100 | | if (source != NULL) |
| 101 | | return view->source_list().indexof(*source); |
| 102 | | else |
| 103 | | return -1; |
| 104 | | } |
| 105 | | |
| 106 | | |
| 107 | | - (void)selectSubviewAtIndex:(int)index { |
| 108 | | int const selected = view->source_list().indexof(*view->source()); |
| 109 | | if (selected != index) { |
| 110 | | view->set_source(*view->source_list().find(index)); |
| 111 | | if ([[self window] firstResponder] != self) |
| 112 | | view->set_cursor_visible(false); |
| 113 | | } |
| 114 | | } |
| 115 | | |
| 116 | | |
| 117 | | - (BOOL)selectSubviewForDevice:(device_t *)device { |
| 118 | | debug_view_source const *const source = view->source_for_device(device); |
| 119 | | if (source != NULL) |
| 120 | | { |
| 121 | | if (view->source() != source) |
| 122 | | { |
| 123 | | view->set_source(*source); |
| 124 | | if ([[self window] firstResponder] != self) |
| 125 | | view->set_cursor_visible(false); |
| 126 | | } |
| 127 | | return YES; |
| 128 | | } |
| 129 | | else |
| 130 | | { |
| 131 | | return NO; |
| 132 | | } |
| 133 | | } |
| 134 | | |
| 135 | | |
| 136 | | - (BOOL)selectSubviewForSpace:(address_space *)space { |
| 137 | | if (space == NULL) return NO; |
| 138 | | debug_view_memory_source const *source = downcast<debug_view_memory_source const *>(view->first_source()); |
| 139 | | while ((source != NULL) && (source->space() != space)) |
| 140 | | source = downcast<debug_view_memory_source *>(source->next()); |
| 141 | | if (source != NULL) |
| 142 | | { |
| 143 | | if (view->source() != source) |
| 144 | | { |
| 145 | | view->set_source(*source); |
| 146 | | if ([[self window] firstResponder] != self) |
| 147 | | view->set_cursor_visible(false); |
| 148 | | } |
| 149 | | return YES; |
| 150 | | } |
| 151 | | else |
| 152 | | { |
| 153 | | return NO; |
| 154 | | } |
| 155 | | } |
| 156 | | |
| 157 | | |
| 158 | | - (NSString *)expression { |
| 159 | | return [NSString stringWithUTF8String:downcast<debug_view_memory *>(view)->expression()]; |
| 160 | | } |
| 161 | | |
| 162 | | |
| 163 | | - (void)setExpression:(NSString *)exp { |
| 164 | | downcast<debug_view_memory *>(view)->set_expression([exp UTF8String]); |
| 165 | | } |
| 166 | | |
| 167 | | |
| 168 | | - (debug_view_memory_source const *)source { |
| 169 | | return downcast<debug_view_memory_source const *>(view->source()); |
| 170 | | } |
| 171 | | |
| 172 | | |
| 173 | | - (IBAction)showChunkSize:(id)sender { |
| 174 | | downcast<debug_view_memory *>(view)->set_bytes_per_chunk([sender tag]); |
| 175 | | } |
| 176 | | |
| 177 | | |
| 178 | | - (IBAction)showPhysicalAddresses:(id)sender { |
| 179 | | downcast<debug_view_memory *>(view)->set_physical([sender tag]); |
| 180 | | } |
| 181 | | |
| 182 | | |
| 183 | | - (IBAction)showReverseView:(id)sender { |
| 184 | | downcast<debug_view_memory *>(view)->set_reverse([sender tag]); |
| 185 | | } |
| 186 | | |
| 187 | | |
| 188 | | - (IBAction)showReverseViewToggle:(id)sender { |
| 189 | | downcast<debug_view_memory *>(view)->set_reverse(!downcast<debug_view_memory *>(view)->reverse()); |
| 190 | | } |
| 191 | | |
| 192 | | |
| 193 | | - (IBAction)changeBytesPerLine:(id)sender { |
| 194 | | debug_view_memory *const memView = downcast<debug_view_memory *>(view); |
| 195 | | memView->set_chunks_per_row(memView->chunks_per_row() + [sender tag]); |
| 196 | | } |
| 197 | | |
| 198 | | |
| 199 | | - (void)insertActionItemsInMenu:(NSMenu *)menu atIndex:(NSInteger)index { |
| 200 | | NSInteger tag; |
| 201 | | for (tag = 1; tag <= 8; tag <<= 1) { |
| 202 | | NSString *title = [NSString stringWithFormat:@"%ld-byte Chunks", (long)tag]; |
| 203 | | NSMenuItem *chunkItem = [menu insertItemWithTitle:title |
| 204 | | action:@selector(showChunkSize:) |
| 205 | | keyEquivalent:[NSString stringWithFormat:@"%ld", (long)tag] |
| 206 | | atIndex:index++]; |
| 207 | | [chunkItem setTarget:self]; |
| 208 | | [chunkItem setTag:tag]; |
| 209 | | } |
| 210 | | |
| 211 | | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 212 | | |
| 213 | | NSMenuItem *logicalItem = [menu insertItemWithTitle:@"Logical Addresses" |
| 214 | | action:@selector(showPhysicalAddresses:) |
| 215 | | keyEquivalent:@"v" |
| 216 | | atIndex:index++]; |
| 217 | | [logicalItem setTarget:self]; |
| 218 | | [logicalItem setTag:FALSE]; |
| 219 | | |
| 220 | | NSMenuItem *physicalItem = [menu insertItemWithTitle:@"Physical Addresses" |
| 221 | | action:@selector(showPhysicalAddresses:) |
| 222 | | keyEquivalent:@"y" |
| 223 | | atIndex:index++]; |
| 224 | | [physicalItem setTarget:self]; |
| 225 | | [physicalItem setTag:TRUE]; |
| 226 | | |
| 227 | | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 228 | | |
| 229 | | NSMenuItem *reverseItem = [menu insertItemWithTitle:@"Reverse View" |
| 230 | | action:@selector(showReverseViewToggle:) |
| 231 | | keyEquivalent:@"r" |
| 232 | | atIndex:index++]; |
| 233 | | [reverseItem setTarget:self]; |
| 234 | | |
| 235 | | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 236 | | |
| 237 | | NSMenuItem *increaseItem = [menu insertItemWithTitle:@"Increase Bytes Per Line" |
| 238 | | action:@selector(changeBytesPerLine:) |
| 239 | | keyEquivalent:@"p" |
| 240 | | atIndex:index++]; |
| 241 | | [increaseItem setTarget:self]; |
| 242 | | [increaseItem setTag:1]; |
| 243 | | |
| 244 | | NSMenuItem *decreaseItem = [menu insertItemWithTitle:@"Decrease Bytes Per Line" |
| 245 | | action:@selector(changeBytesPerLine:) |
| 246 | | keyEquivalent:@"o" |
| 247 | | atIndex:index++]; |
| 248 | | [decreaseItem setTarget:self]; |
| 249 | | [decreaseItem setTag:-1]; |
| 250 | | |
| 251 | | if (index < [menu numberOfItems]) |
| 252 | | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 253 | | } |
| 254 | | |
| 255 | | |
| 256 | | - (void)insertSubviewItemsInMenu:(NSMenu *)menu atIndex:(NSInteger)index { |
| 257 | | for (const debug_view_source *source = view->source_list().first(); source != NULL; source = source->next()) |
| 258 | | { |
| 259 | | [[menu insertItemWithTitle:[NSString stringWithUTF8String:source->name()] |
| 260 | | action:NULL |
| 261 | | keyEquivalent:@"" |
| 262 | | atIndex:index++] setTag:view->source_list().indexof(*source)]; |
| 263 | | } |
| 264 | | if (index < [menu numberOfItems]) |
| 265 | | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 266 | | } |
| 267 | | |
| 268 | | @end |
trunk/src/osd/modules/debugger/osx/memoryview.mm
| r0 | r250165 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Vas Crabb |
| 3 | //============================================================ |
| 4 | // |
| 5 | // memoryview.m - MacOS X Cocoa debug window handling |
| 6 | // |
| 7 | //============================================================ |
| 8 | |
| 9 | #import "memoryview.h" |
| 10 | |
| 11 | #include "debug/debugcpu.h" |
| 12 | #include "debug/debugvw.h" |
| 13 | |
| 14 | |
| 15 | @implementation MAMEMemoryView |
| 16 | |
| 17 | - (id)initWithFrame:(NSRect)f machine:(running_machine &)m { |
| 18 | if (!(self = [super initWithFrame:f type:DVT_MEMORY machine:m wholeLineScroll:NO])) |
| 19 | return nil; |
| 20 | return self; |
| 21 | } |
| 22 | |
| 23 | |
| 24 | - (void)dealloc { |
| 25 | [super dealloc]; |
| 26 | } |
| 27 | |
| 28 | |
| 29 | - (BOOL)validateMenuItem:(NSMenuItem *)item { |
| 30 | SEL action = [item action]; |
| 31 | NSInteger tag = [item tag]; |
| 32 | debug_view_memory *memview = downcast<debug_view_memory *>(view); |
| 33 | |
| 34 | if (action == @selector(showChunkSize:)) |
| 35 | { |
| 36 | [item setState:((tag == memview->bytes_per_chunk()) ? NSOnState : NSOffState)]; |
| 37 | return YES; |
| 38 | } |
| 39 | else if (action == @selector(showPhysicalAddresses:)) |
| 40 | { |
| 41 | [item setState:((tag == memview->physical()) ? NSOnState : NSOffState)]; |
| 42 | return YES; |
| 43 | } |
| 44 | else if (action == @selector(showReverseView:)) |
| 45 | { |
| 46 | [item setState:((tag == memview->reverse()) ? NSOnState : NSOffState)]; |
| 47 | return YES; |
| 48 | } |
| 49 | else if (action == @selector(showReverseViewToggle:)) |
| 50 | { |
| 51 | [item setState:(memview->reverse() ? NSOnState : NSOffState)]; |
| 52 | return YES; |
| 53 | } |
| 54 | else if (action == @selector(changeBytesPerLine:)) |
| 55 | { |
| 56 | return (memview->chunks_per_row() + [item tag]) > 0; |
| 57 | } |
| 58 | else |
| 59 | { |
| 60 | return [super validateMenuItem:item]; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | |
| 65 | - (NSSize)maximumFrameSize { |
| 66 | debug_view_xy max(0, 0); |
| 67 | debug_view_source const *source = view->source(); |
| 68 | for (debug_view_source const *source = view->first_source(); source != NULL; source = source->next()) |
| 69 | { |
| 70 | view->set_source(*source); |
| 71 | debug_view_xy const current = view->total_size(); |
| 72 | max.x = MAX(max.x, current.x); |
| 73 | max.y = MAX(max.y, current.y); |
| 74 | } |
| 75 | view->set_source(*source); |
| 76 | return NSMakeSize(ceil((max.x * fontWidth) + (2 * [textContainer lineFragmentPadding])), |
| 77 | ceil(max.y * fontHeight)); |
| 78 | } |
| 79 | |
| 80 | |
| 81 | - (void)addContextMenuItemsToMenu:(NSMenu *)menu { |
| 82 | [super addContextMenuItemsToMenu:menu]; |
| 83 | if ([menu numberOfItems] > 0) |
| 84 | [menu addItem:[NSMenuItem separatorItem]]; |
| 85 | [self insertActionItemsInMenu:menu atIndex:[menu numberOfItems]]; |
| 86 | } |
| 87 | |
| 88 | |
| 89 | - (NSString *)selectedSubviewName { |
| 90 | debug_view_source const *source = view->source(); |
| 91 | if (source != NULL) |
| 92 | return [NSString stringWithUTF8String:source->name()]; |
| 93 | else |
| 94 | return @""; |
| 95 | } |
| 96 | |
| 97 | |
| 98 | - (int)selectedSubviewIndex { |
| 99 | debug_view_source const *source = view->source(); |
| 100 | if (source != NULL) |
| 101 | return view->source_list().indexof(*source); |
| 102 | else |
| 103 | return -1; |
| 104 | } |
| 105 | |
| 106 | |
| 107 | - (void)selectSubviewAtIndex:(int)index { |
| 108 | int const selected = view->source_list().indexof(*view->source()); |
| 109 | if (selected != index) { |
| 110 | view->set_source(*view->source_list().find(index)); |
| 111 | if ([[self window] firstResponder] != self) |
| 112 | view->set_cursor_visible(false); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | |
| 117 | - (BOOL)selectSubviewForDevice:(device_t *)device { |
| 118 | debug_view_source const *const source = view->source_for_device(device); |
| 119 | if (source != NULL) |
| 120 | { |
| 121 | if (view->source() != source) |
| 122 | { |
| 123 | view->set_source(*source); |
| 124 | if ([[self window] firstResponder] != self) |
| 125 | view->set_cursor_visible(false); |
| 126 | } |
| 127 | return YES; |
| 128 | } |
| 129 | else |
| 130 | { |
| 131 | return NO; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | |
| 136 | - (BOOL)selectSubviewForSpace:(address_space *)space { |
| 137 | if (space == NULL) return NO; |
| 138 | debug_view_memory_source const *source = downcast<debug_view_memory_source const *>(view->first_source()); |
| 139 | while ((source != NULL) && (source->space() != space)) |
| 140 | source = downcast<debug_view_memory_source *>(source->next()); |
| 141 | if (source != NULL) |
| 142 | { |
| 143 | if (view->source() != source) |
| 144 | { |
| 145 | view->set_source(*source); |
| 146 | if ([[self window] firstResponder] != self) |
| 147 | view->set_cursor_visible(false); |
| 148 | } |
| 149 | return YES; |
| 150 | } |
| 151 | else |
| 152 | { |
| 153 | return NO; |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | |
| 158 | - (NSString *)expression { |
| 159 | return [NSString stringWithUTF8String:downcast<debug_view_memory *>(view)->expression()]; |
| 160 | } |
| 161 | |
| 162 | |
| 163 | - (void)setExpression:(NSString *)exp { |
| 164 | downcast<debug_view_memory *>(view)->set_expression([exp UTF8String]); |
| 165 | } |
| 166 | |
| 167 | |
| 168 | - (debug_view_memory_source const *)source { |
| 169 | return downcast<debug_view_memory_source const *>(view->source()); |
| 170 | } |
| 171 | |
| 172 | |
| 173 | - (IBAction)showChunkSize:(id)sender { |
| 174 | downcast<debug_view_memory *>(view)->set_bytes_per_chunk([sender tag]); |
| 175 | } |
| 176 | |
| 177 | |
| 178 | - (IBAction)showPhysicalAddresses:(id)sender { |
| 179 | downcast<debug_view_memory *>(view)->set_physical([sender tag]); |
| 180 | } |
| 181 | |
| 182 | |
| 183 | - (IBAction)showReverseView:(id)sender { |
| 184 | downcast<debug_view_memory *>(view)->set_reverse([sender tag]); |
| 185 | } |
| 186 | |
| 187 | |
| 188 | - (IBAction)showReverseViewToggle:(id)sender { |
| 189 | downcast<debug_view_memory *>(view)->set_reverse(!downcast<debug_view_memory *>(view)->reverse()); |
| 190 | } |
| 191 | |
| 192 | |
| 193 | - (IBAction)changeBytesPerLine:(id)sender { |
| 194 | debug_view_memory *const memView = downcast<debug_view_memory *>(view); |
| 195 | memView->set_chunks_per_row(memView->chunks_per_row() + [sender tag]); |
| 196 | } |
| 197 | |
| 198 | |
| 199 | - (void)insertActionItemsInMenu:(NSMenu *)menu atIndex:(NSInteger)index { |
| 200 | NSInteger tag; |
| 201 | for (tag = 1; tag <= 8; tag <<= 1) { |
| 202 | NSString *title = [NSString stringWithFormat:@"%ld-byte Chunks", (long)tag]; |
| 203 | NSMenuItem *chunkItem = [menu insertItemWithTitle:title |
| 204 | action:@selector(showChunkSize:) |
| 205 | keyEquivalent:[NSString stringWithFormat:@"%ld", (long)tag] |
| 206 | atIndex:index++]; |
| 207 | [chunkItem setTarget:self]; |
| 208 | [chunkItem setTag:tag]; |
| 209 | } |
| 210 | |
| 211 | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 212 | |
| 213 | NSMenuItem *logicalItem = [menu insertItemWithTitle:@"Logical Addresses" |
| 214 | action:@selector(showPhysicalAddresses:) |
| 215 | keyEquivalent:@"v" |
| 216 | atIndex:index++]; |
| 217 | [logicalItem setTarget:self]; |
| 218 | [logicalItem setTag:FALSE]; |
| 219 | |
| 220 | NSMenuItem *physicalItem = [menu insertItemWithTitle:@"Physical Addresses" |
| 221 | action:@selector(showPhysicalAddresses:) |
| 222 | keyEquivalent:@"y" |
| 223 | atIndex:index++]; |
| 224 | [physicalItem setTarget:self]; |
| 225 | [physicalItem setTag:TRUE]; |
| 226 | |
| 227 | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 228 | |
| 229 | NSMenuItem *reverseItem = [menu insertItemWithTitle:@"Reverse View" |
| 230 | action:@selector(showReverseViewToggle:) |
| 231 | keyEquivalent:@"r" |
| 232 | atIndex:index++]; |
| 233 | [reverseItem setTarget:self]; |
| 234 | |
| 235 | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 236 | |
| 237 | NSMenuItem *increaseItem = [menu insertItemWithTitle:@"Increase Bytes Per Line" |
| 238 | action:@selector(changeBytesPerLine:) |
| 239 | keyEquivalent:@"p" |
| 240 | atIndex:index++]; |
| 241 | [increaseItem setTarget:self]; |
| 242 | [increaseItem setTag:1]; |
| 243 | |
| 244 | NSMenuItem *decreaseItem = [menu insertItemWithTitle:@"Decrease Bytes Per Line" |
| 245 | action:@selector(changeBytesPerLine:) |
| 246 | keyEquivalent:@"o" |
| 247 | atIndex:index++]; |
| 248 | [decreaseItem setTarget:self]; |
| 249 | [decreaseItem setTag:-1]; |
| 250 | |
| 251 | if (index < [menu numberOfItems]) |
| 252 | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 253 | } |
| 254 | |
| 255 | |
| 256 | - (void)insertSubviewItemsInMenu:(NSMenu *)menu atIndex:(NSInteger)index { |
| 257 | for (const debug_view_source *source = view->source_list().first(); source != NULL; source = source->next()) |
| 258 | { |
| 259 | [[menu insertItemWithTitle:[NSString stringWithUTF8String:source->name()] |
| 260 | action:NULL |
| 261 | keyEquivalent:@"" |
| 262 | atIndex:index++] setTag:view->source_list().indexof(*source)]; |
| 263 | } |
| 264 | if (index < [menu numberOfItems]) |
| 265 | [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; |
| 266 | } |
| 267 | |
| 268 | @end |
trunk/src/osd/modules/debugger/osx/memoryviewer.m
| r250164 | r250165 | |
| 1 | | // license:BSD-3-Clause |
| 2 | | // copyright-holders:Vas Crabb |
| 3 | | //============================================================ |
| 4 | | // |
| 5 | | // memoryviewer.m - MacOS X Cocoa debug window handling |
| 6 | | // |
| 7 | | //============================================================ |
| 8 | | |
| 9 | | #import "memoryviewer.h" |
| 10 | | |
| 11 | | #import "debugconsole.h" |
| 12 | | #import "debugview.h" |
| 13 | | #import "memoryview.h" |
| 14 | | |
| 15 | | #include "debug/debugcpu.h" |
| 16 | | #include "debug/dvmemory.h" |
| 17 | | |
| 18 | | |
| 19 | | @implementation MAMEMemoryViewer |
| 20 | | |
| 21 | | - (id)initWithMachine:(running_machine &)m console:(MAMEDebugConsole *)c { |
| 22 | | NSScrollView *memoryScroll; |
| 23 | | NSView *expressionContainer; |
| 24 | | NSPopUpButton *actionButton; |
| 25 | | NSRect expressionFrame; |
| 26 | | |
| 27 | | if (!(self = [super initWithMachine:m title:@"Memory" console:c])) |
| 28 | | return nil; |
| 29 | | NSRect const contentBounds = [[window contentView] bounds]; |
| 30 | | NSFont *const defaultFont = [[MAMEDebugView class] defaultFontForMachine:m]; |
| 31 | | |
| 32 | | // create the expression field |
| 33 | | expressionField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; |
| 34 | | [expressionField setAutoresizingMask:(NSViewWidthSizable | NSViewMaxXMargin | NSViewMinYMargin)]; |
| 35 | | [expressionField setFont:defaultFont]; |
| 36 | | [expressionField setFocusRingType:NSFocusRingTypeNone]; |
| 37 | | [expressionField setTarget:self]; |
| 38 | | [expressionField setAction:@selector(doExpression:)]; |
| 39 | | [expressionField setDelegate:self]; |
| 40 | | [expressionField sizeToFit]; |
| 41 | | |
| 42 | | // create the subview popup |
| 43 | | subviewButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; |
| 44 | | [subviewButton setAutoresizingMask:(NSViewWidthSizable | NSViewMinXMargin | NSViewMinYMargin)]; |
| 45 | | [subviewButton setBezelStyle:NSShadowlessSquareBezelStyle]; |
| 46 | | [subviewButton setFocusRingType:NSFocusRingTypeNone]; |
| 47 | | [subviewButton setFont:defaultFont]; |
| 48 | | [subviewButton setTarget:self]; |
| 49 | | [subviewButton setAction:@selector(changeSubview:)]; |
| 50 | | [[subviewButton cell] setArrowPosition:NSPopUpArrowAtBottom]; |
| 51 | | [subviewButton sizeToFit]; |
| 52 | | |
| 53 | | // adjust sizes to make it fit nicely |
| 54 | | expressionFrame = [expressionField frame]; |
| 55 | | expressionFrame.size.height = MAX(expressionFrame.size.height, [subviewButton frame].size.height); |
| 56 | | expressionFrame.size.width = (contentBounds.size.width - expressionFrame.size.height) / 2; |
| 57 | | [expressionField setFrame:expressionFrame]; |
| 58 | | expressionFrame.origin.x = expressionFrame.size.width; |
| 59 | | expressionFrame.size.width = contentBounds.size.width - expressionFrame.size.height - expressionFrame.origin.x; |
| 60 | | [subviewButton setFrame:expressionFrame]; |
| 61 | | |
| 62 | | // create a container for the expression field and subview popup |
| 63 | | expressionFrame = NSMakeRect(expressionFrame.size.height, |
| 64 | | contentBounds.size.height - expressionFrame.size.height, |
| 65 | | contentBounds.size.width - expressionFrame.size.height, |
| 66 | | expressionFrame.size.height); |
| 67 | | expressionContainer = [[NSView alloc] initWithFrame:expressionFrame]; |
| 68 | | [expressionContainer setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; |
| 69 | | [expressionContainer addSubview:expressionField]; |
| 70 | | [expressionField release]; |
| 71 | | [expressionContainer addSubview:subviewButton]; |
| 72 | | [subviewButton release]; |
| 73 | | [[window contentView] addSubview:expressionContainer]; |
| 74 | | [expressionContainer release]; |
| 75 | | |
| 76 | | // create the memory view |
| 77 | | memoryView = [[MAMEMemoryView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) |
| 78 | | machine:*machine]; |
| 79 | | [memoryView insertSubviewItemsInMenu:[subviewButton menu] atIndex:0]; |
| 80 | | memoryScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, |
| 81 | | 0, |
| 82 | | contentBounds.size.width, |
| 83 | | expressionFrame.origin.y)]; |
| 84 | | [memoryScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 85 | | [memoryScroll setHasHorizontalScroller:YES]; |
| 86 | | [memoryScroll setHasVerticalScroller:YES]; |
| 87 | | [memoryScroll setAutohidesScrollers:YES]; |
| 88 | | [memoryScroll setBorderType:NSNoBorder]; |
| 89 | | [memoryScroll setDocumentView:memoryView]; |
| 90 | | [memoryView release]; |
| 91 | | [[window contentView] addSubview:memoryScroll]; |
| 92 | | [memoryScroll release]; |
| 93 | | |
| 94 | | // create the action popup |
| 95 | | actionButton = [[self class] newActionButtonWithFrame:NSMakeRect(0, |
| 96 | | expressionFrame.origin.y, |
| 97 | | expressionFrame.size.height, |
| 98 | | expressionFrame.size.height)]; |
| 99 | | [actionButton setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)]; |
| 100 | | [actionButton setFont:[NSFont systemFontOfSize:[defaultFont pointSize]]]; |
| 101 | | [memoryView insertActionItemsInMenu:[actionButton menu] atIndex:1]; |
| 102 | | [[window contentView] addSubview:actionButton]; |
| 103 | | [actionButton release]; |
| 104 | | |
| 105 | | // set default state |
| 106 | | [memoryView selectSubviewForDevice:debug_cpu_get_visible_cpu(*machine)]; |
| 107 | | [memoryView setExpression:@"0"]; |
| 108 | | [expressionField setStringValue:@"0"]; |
| 109 | | [expressionField selectText:self]; |
| 110 | | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[memoryView selectedSubviewIndex]]]; |
| 111 | | [window makeFirstResponder:expressionField]; |
| 112 | | [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; |
| 113 | | |
| 114 | | // calculate the optimal size for everything |
| 115 | | NSSize const desired = [NSScrollView frameSizeForContentSize:[memoryView maximumFrameSize] |
| 116 | | hasHorizontalScroller:YES |
| 117 | | hasVerticalScroller:YES |
| 118 | | borderType:[memoryScroll borderType]]; |
| 119 | | [self cascadeWindowWithDesiredSize:desired forView:memoryScroll]; |
| 120 | | |
| 121 | | // don't forget the result |
| 122 | | return self; |
| 123 | | } |
| 124 | | |
| 125 | | |
| 126 | | - (void)dealloc { |
| 127 | | [super dealloc]; |
| 128 | | } |
| 129 | | |
| 130 | | |
| 131 | | - (id <MAMEDebugViewExpressionSupport>)documentView { |
| 132 | | return memoryView; |
| 133 | | } |
| 134 | | |
| 135 | | |
| 136 | | - (IBAction)debugNewMemoryWindow:(id)sender { |
| 137 | | debug_view_memory_source const *source = [memoryView source]; |
| 138 | | [console debugNewMemoryWindowForSpace:source->space() |
| 139 | | device:source->device() |
| 140 | | expression:[memoryView expression]]; |
| 141 | | } |
| 142 | | |
| 143 | | |
| 144 | | - (IBAction)debugNewDisassemblyWindow:(id)sender { |
| 145 | | debug_view_memory_source const *source = [memoryView source]; |
| 146 | | [console debugNewDisassemblyWindowForSpace:source->space() |
| 147 | | device:source->device() |
| 148 | | expression:[memoryView expression]]; |
| 149 | | } |
| 150 | | |
| 151 | | |
| 152 | | - (BOOL)selectSubviewForDevice:(device_t *)device { |
| 153 | | BOOL const result = [memoryView selectSubviewForDevice:device]; |
| 154 | | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[memoryView selectedSubviewIndex]]]; |
| 155 | | [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; |
| 156 | | return result; |
| 157 | | } |
| 158 | | |
| 159 | | |
| 160 | | - (BOOL)selectSubviewForSpace:(address_space *)space { |
| 161 | | BOOL const result = [memoryView selectSubviewForSpace:space]; |
| 162 | | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[memoryView selectedSubviewIndex]]]; |
| 163 | | [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; |
| 164 | | return result; |
| 165 | | } |
| 166 | | |
| 167 | | |
| 168 | | - (IBAction)changeSubview:(id)sender { |
| 169 | | [memoryView selectSubviewAtIndex:[[sender selectedItem] tag]]; |
| 170 | | [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; |
| 171 | | } |
| 172 | | |
| 173 | | @end |
trunk/src/osd/modules/debugger/osx/memoryviewer.mm
| r0 | r250165 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Vas Crabb |
| 3 | //============================================================ |
| 4 | // |
| 5 | // memoryviewer.m - MacOS X Cocoa debug window handling |
| 6 | // |
| 7 | //============================================================ |
| 8 | |
| 9 | #import "memoryviewer.h" |
| 10 | |
| 11 | #import "debugconsole.h" |
| 12 | #import "debugview.h" |
| 13 | #import "memoryview.h" |
| 14 | |
| 15 | #include "debug/debugcpu.h" |
| 16 | #include "debug/dvmemory.h" |
| 17 | |
| 18 | |
| 19 | @implementation MAMEMemoryViewer |
| 20 | |
| 21 | - (id)initWithMachine:(running_machine &)m console:(MAMEDebugConsole *)c { |
| 22 | NSScrollView *memoryScroll; |
| 23 | NSView *expressionContainer; |
| 24 | NSPopUpButton *actionButton; |
| 25 | NSRect expressionFrame; |
| 26 | |
| 27 | if (!(self = [super initWithMachine:m title:@"Memory" console:c])) |
| 28 | return nil; |
| 29 | NSRect const contentBounds = [[window contentView] bounds]; |
| 30 | NSFont *const defaultFont = [[MAMEDebugView class] defaultFontForMachine:m]; |
| 31 | |
| 32 | // create the expression field |
| 33 | expressionField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; |
| 34 | [expressionField setAutoresizingMask:(NSViewWidthSizable | NSViewMaxXMargin | NSViewMinYMargin)]; |
| 35 | [expressionField setFont:defaultFont]; |
| 36 | [expressionField setFocusRingType:NSFocusRingTypeNone]; |
| 37 | [expressionField setTarget:self]; |
| 38 | [expressionField setAction:@selector(doExpression:)]; |
| 39 | [expressionField setDelegate:self]; |
| 40 | [expressionField sizeToFit]; |
| 41 | |
| 42 | // create the subview popup |
| 43 | subviewButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; |
| 44 | [subviewButton setAutoresizingMask:(NSViewWidthSizable | NSViewMinXMargin | NSViewMinYMargin)]; |
| 45 | [subviewButton setBezelStyle:NSShadowlessSquareBezelStyle]; |
| 46 | [subviewButton setFocusRingType:NSFocusRingTypeNone]; |
| 47 | [subviewButton setFont:defaultFont]; |
| 48 | [subviewButton setTarget:self]; |
| 49 | [subviewButton setAction:@selector(changeSubview:)]; |
| 50 | [[subviewButton cell] setArrowPosition:NSPopUpArrowAtBottom]; |
| 51 | [subviewButton sizeToFit]; |
| 52 | |
| 53 | // adjust sizes to make it fit nicely |
| 54 | expressionFrame = [expressionField frame]; |
| 55 | expressionFrame.size.height = MAX(expressionFrame.size.height, [subviewButton frame].size.height); |
| 56 | expressionFrame.size.width = (contentBounds.size.width - expressionFrame.size.height) / 2; |
| 57 | [expressionField setFrame:expressionFrame]; |
| 58 | expressionFrame.origin.x = expressionFrame.size.width; |
| 59 | expressionFrame.size.width = contentBounds.size.width - expressionFrame.size.height - expressionFrame.origin.x; |
| 60 | [subviewButton setFrame:expressionFrame]; |
| 61 | |
| 62 | // create a container for the expression field and subview popup |
| 63 | expressionFrame = NSMakeRect(expressionFrame.size.height, |
| 64 | contentBounds.size.height - expressionFrame.size.height, |
| 65 | contentBounds.size.width - expressionFrame.size.height, |
| 66 | expressionFrame.size.height); |
| 67 | expressionContainer = [[NSView alloc] initWithFrame:expressionFrame]; |
| 68 | [expressionContainer setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; |
| 69 | [expressionContainer addSubview:expressionField]; |
| 70 | [expressionField release]; |
| 71 | [expressionContainer addSubview:subviewButton]; |
| 72 | [subviewButton release]; |
| 73 | [[window contentView] addSubview:expressionContainer]; |
| 74 | [expressionContainer release]; |
| 75 | |
| 76 | // create the memory view |
| 77 | memoryView = [[MAMEMemoryView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) |
| 78 | machine:*machine]; |
| 79 | [memoryView insertSubviewItemsInMenu:[subviewButton menu] atIndex:0]; |
| 80 | memoryScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, |
| 81 | 0, |
| 82 | contentBounds.size.width, |
| 83 | expressionFrame.origin.y)]; |
| 84 | [memoryScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| 85 | [memoryScroll setHasHorizontalScroller:YES]; |
| 86 | [memoryScroll setHasVerticalScroller:YES]; |
| 87 | [memoryScroll setAutohidesScrollers:YES]; |
| 88 | [memoryScroll setBorderType:NSNoBorder]; |
| 89 | [memoryScroll setDocumentView:memoryView]; |
| 90 | [memoryView release]; |
| 91 | [[window contentView] addSubview:memoryScroll]; |
| 92 | [memoryScroll release]; |
| 93 | |
| 94 | // create the action popup |
| 95 | actionButton = [[self class] newActionButtonWithFrame:NSMakeRect(0, |
| 96 | expressionFrame.origin.y, |
| 97 | expressionFrame.size.height, |
| 98 | expressionFrame.size.height)]; |
| 99 | [actionButton setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)]; |
| 100 | [actionButton setFont:[NSFont systemFontOfSize:[defaultFont pointSize]]]; |
| 101 | [memoryView insertActionItemsInMenu:[actionButton menu] atIndex:1]; |
| 102 | [[window contentView] addSubview:actionButton]; |
| 103 | [actionButton release]; |
| 104 | |
| 105 | // set default state |
| 106 | [memoryView selectSubviewForDevice:debug_cpu_get_visible_cpu(*machine)]; |
| 107 | [memoryView setExpression:@"0"]; |
| 108 | [expressionField setStringValue:@"0"]; |
| 109 | [expressionField selectText:self]; |
| 110 | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[memoryView selectedSubviewIndex]]]; |
| 111 | [window makeFirstResponder:expressionField]; |
| 112 | [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; |
| 113 | |
| 114 | // calculate the optimal size for everything |
| 115 | NSSize const desired = [NSScrollView frameSizeForContentSize:[memoryView maximumFrameSize] |
| 116 | hasHorizontalScroller:YES |
| 117 | hasVerticalScroller:YES |
| 118 | borderType:[memoryScroll borderType]]; |
| 119 | [self cascadeWindowWithDesiredSize:desired forView:memoryScroll]; |
| 120 | |
| 121 | // don't forget the result |
| 122 | return self; |
| 123 | } |
| 124 | |
| 125 | |
| 126 | - (void)dealloc { |
| 127 | [super dealloc]; |
| 128 | } |
| 129 | |
| 130 | |
| 131 | - (id <MAMEDebugViewExpressionSupport>)documentView { |
| 132 | return memoryView; |
| 133 | } |
| 134 | |
| 135 | |
| 136 | - (IBAction)debugNewMemoryWindow:(id)sender { |
| 137 | debug_view_memory_source const *source = [memoryView source]; |
| 138 | [console debugNewMemoryWindowForSpace:source->space() |
| 139 | device:source->device() |
| 140 | expression:[memoryView expression]]; |
| 141 | } |
| 142 | |
| 143 | |
| 144 | - (IBAction)debugNewDisassemblyWindow:(id)sender { |
| 145 | debug_view_memory_source const *source = [memoryView source]; |
| 146 | [console debugNewDisassemblyWindowForSpace:source->space() |
| 147 | device:source->device() |
| 148 | expression:[memoryView expression]]; |
| 149 | } |
| 150 | |
| 151 | |
| 152 | - (BOOL)selectSubviewForDevice:(device_t *)device { |
| 153 | BOOL const result = [memoryView selectSubviewForDevice:device]; |
| 154 | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[memoryView selectedSubviewIndex]]]; |
| 155 | [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; |
| 156 | return result; |
| 157 | } |
| 158 | |
| 159 | |
| 160 | - (BOOL)selectSubviewForSpace:(address_space *)space { |
| 161 | BOOL const result = [memoryView selectSubviewForSpace:space]; |
| 162 | [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[memoryView selectedSubviewIndex]]]; |
| 163 | [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; |
| 164 | return result; |
| 165 | } |
| 166 | |
| 167 | |
| 168 | - (IBAction)changeSubview:(id)sender { |
| 169 | [memoryView selectSubviewAtIndex:[[sender selectedItem] tag]]; |
| 170 | [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; |
| 171 | } |
| 172 | |
| 173 | @end |
trunk/src/osd/sdl/SDLMain_tmpl.m
| r250164 | r250165 | |
| 1 | | // license:Zlib|LGPL-2.1+ |
| 2 | | // copyright-holders:http://libsdl.org/ |
| 3 | | /* SDLMain.m - main entry point for our Cocoa-ized SDL app |
| 4 | | Initial Version: Darrell Walisser <dwaliss1@purdue.edu> |
| 5 | | Non-NIB-Code & other changes: Max Horn <max@quendi.de> |
| 6 | | |
| 7 | | Feel free to customize this file to suit your needs |
| 8 | | */ |
| 9 | | |
| 10 | | #import "sdlinc.h" |
| 11 | | #import "SDLMain_tmpl.h" |
| 12 | | #import <sys/param.h> /* for MAXPATHLEN */ |
| 13 | | #import <unistd.h> |
| 14 | | |
| 15 | | /* For some reason, Apple removed setAppleMenu from the headers in 10.4, |
| 16 | | but the method still is there and works. To avoid warnings, we declare |
| 17 | | it ourselves here. */ |
| 18 | | @interface NSApplication(SDL_Missing_Methods) |
| 19 | | - (void)setAppleMenu:(NSMenu *)menu; |
| 20 | | @end |
| 21 | | |
| 22 | | /* Use this flag to determine whether we use SDLMain.nib or not */ |
| 23 | | #define SDL_USE_NIB_FILE 0 |
| 24 | | |
| 25 | | /* Use this flag to determine whether we use CPS (docking) or not */ |
| 26 | | #define SDL_USE_CPS 0 |
| 27 | | #ifdef SDL_USE_CPS |
| 28 | | /* Portions of CPS.h */ |
| 29 | | typedef struct CPSProcessSerNum |
| 30 | | { |
| 31 | | UInt32 lo; |
| 32 | | UInt32 hi; |
| 33 | | } CPSProcessSerNum; |
| 34 | | |
| 35 | | extern "C" OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); |
| 36 | | extern "C" OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); |
| 37 | | extern "C" OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); |
| 38 | | |
| 39 | | #endif /* SDL_USE_CPS */ |
| 40 | | |
| 41 | | static int gArgc; |
| 42 | | static char **gArgv; |
| 43 | | static BOOL gFinderLaunch; |
| 44 | | static BOOL gCalledAppMainline = FALSE; |
| 45 | | |
| 46 | | static NSString *getApplicationName(void) |
| 47 | | { |
| 48 | | NSDictionary *dict; |
| 49 | | NSString *appName = 0; |
| 50 | | |
| 51 | | /* Determine the application name */ |
| 52 | | dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); |
| 53 | | if (dict) |
| 54 | | appName = [dict objectForKey: @"CFBundleName"]; |
| 55 | | |
| 56 | | if (![appName length]) |
| 57 | | appName = [[NSProcessInfo processInfo] processName]; |
| 58 | | |
| 59 | | return appName; |
| 60 | | } |
| 61 | | |
| 62 | | #if SDL_USE_NIB_FILE |
| 63 | | /* A helper category for NSString */ |
| 64 | | @interface NSString (ReplaceSubString) |
| 65 | | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; |
| 66 | | @end |
| 67 | | #endif |
| 68 | | |
| 69 | | @interface SDLApplication : NSApplication |
| 70 | | @end |
| 71 | | |
| 72 | | @implementation SDLApplication |
| 73 | | /* Invoked from the Quit menu item */ |
| 74 | | - (void)terminate:(id)sender |
| 75 | | { |
| 76 | | /* Post a SDL_QUIT event */ |
| 77 | | SDL_Event event; |
| 78 | | event.type = SDL_QUIT; |
| 79 | | SDL_PushEvent(&event); |
| 80 | | } |
| 81 | | @end |
| 82 | | |
| 83 | | /* The main class of the application, the application's delegate */ |
| 84 | | @implementation SDLMain |
| 85 | | |
| 86 | | /* Set the working directory to the .app's parent directory */ |
| 87 | | - (void) setupWorkingDirectory:(BOOL)shouldChdir |
| 88 | | { |
| 89 | | if (shouldChdir) |
| 90 | | { |
| 91 | | char parentdir[MAXPATHLEN]; |
| 92 | | CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); |
| 93 | | CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); |
| 94 | | if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) { |
| 95 | | assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */ |
| 96 | | } |
| 97 | | CFRelease(url); |
| 98 | | CFRelease(url2); |
| 99 | | } |
| 100 | | |
| 101 | | } |
| 102 | | |
| 103 | | #if SDL_USE_NIB_FILE |
| 104 | | |
| 105 | | /* Fix menu to contain the real app name instead of "SDL App" */ |
| 106 | | - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName |
| 107 | | { |
| 108 | | NSRange aRange; |
| 109 | | NSEnumerator *enumerator; |
| 110 | | NSMenuItem *menuItem; |
| 111 | | |
| 112 | | aRange = [[aMenu title] rangeOfString:@"SDL App"]; |
| 113 | | if (aRange.length != 0) |
| 114 | | [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; |
| 115 | | |
| 116 | | enumerator = [[aMenu itemArray] objectEnumerator]; |
| 117 | | while ((menuItem = [enumerator nextObject])) |
| 118 | | { |
| 119 | | aRange = [[menuItem title] rangeOfString:@"SDL App"]; |
| 120 | | if (aRange.length != 0) |
| 121 | | [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; |
| 122 | | if ([menuItem hasSubmenu]) |
| 123 | | [self fixMenu:[menuItem submenu] withAppName:appName]; |
| 124 | | } |
| 125 | | [ aMenu sizeToFit ]; |
| 126 | | } |
| 127 | | |
| 128 | | #else |
| 129 | | |
| 130 | | static void setApplicationMenu(void) |
| 131 | | { |
| 132 | | /* warning: this code is very odd */ |
| 133 | | NSMenu *appleMenu; |
| 134 | | NSMenuItem *menuItem; |
| 135 | | NSString *title; |
| 136 | | NSString *appName; |
| 137 | | |
| 138 | | appName = getApplicationName(); |
| 139 | | appleMenu = [[NSMenu alloc] initWithTitle:@""]; |
| 140 | | |
| 141 | | /* Add menu items */ |
| 142 | | title = [@"About " stringByAppendingString:appName]; |
| 143 | | [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; |
| 144 | | |
| 145 | | [appleMenu addItem:[NSMenuItem separatorItem]]; |
| 146 | | |
| 147 | | title = [@"Hide " stringByAppendingString:appName]; |
| 148 | | [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; |
| 149 | | |
| 150 | | menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; |
| 151 | | [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; |
| 152 | | |
| 153 | | [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; |
| 154 | | |
| 155 | | [appleMenu addItem:[NSMenuItem separatorItem]]; |
| 156 | | |
| 157 | | title = [@"Quit " stringByAppendingString:appName]; |
| 158 | | [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; |
| 159 | | |
| 160 | | |
| 161 | | /* Put menu into the menubar */ |
| 162 | | menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; |
| 163 | | [menuItem setSubmenu:appleMenu]; |
| 164 | | [[NSApp mainMenu] addItem:menuItem]; |
| 165 | | |
| 166 | | /* Tell the application object that this is now the application menu */ |
| 167 | | [NSApp setAppleMenu:appleMenu]; |
| 168 | | |
| 169 | | /* Finally give up our references to the objects */ |
| 170 | | [appleMenu release]; |
| 171 | | [menuItem release]; |
| 172 | | } |
| 173 | | |
| 174 | | /* Create a window menu */ |
| 175 | | static void setupWindowMenu(void) |
| 176 | | { |
| 177 | | NSMenu *windowMenu; |
| 178 | | NSMenuItem *windowMenuItem; |
| 179 | | NSMenuItem *menuItem; |
| 180 | | |
| 181 | | windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; |
| 182 | | |
| 183 | | /* "Minimize" item */ |
| 184 | | menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; |
| 185 | | [windowMenu addItem:menuItem]; |
| 186 | | [menuItem release]; |
| 187 | | |
| 188 | | /* Put menu into the menubar */ |
| 189 | | windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; |
| 190 | | [windowMenuItem setSubmenu:windowMenu]; |
| 191 | | [[NSApp mainMenu] addItem:windowMenuItem]; |
| 192 | | |
| 193 | | /* Tell the application object that this is now the window menu */ |
| 194 | | [NSApp setWindowsMenu:windowMenu]; |
| 195 | | |
| 196 | | /* Finally give up our references to the objects */ |
| 197 | | [windowMenu release]; |
| 198 | | [windowMenuItem release]; |
| 199 | | } |
| 200 | | |
| 201 | | /* Replacement for NSApplicationMain */ |
| 202 | | static void CustomApplicationMain (int argc, char **argv) |
| 203 | | { |
| 204 | | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| 205 | | SDLMain *sdlMain; |
| 206 | | |
| 207 | | /* Ensure the application object is initialised */ |
| 208 | | [SDLApplication sharedApplication]; |
| 209 | | |
| 210 | | #ifdef SDL_USE_CPS |
| 211 | | { |
| 212 | | CPSProcessSerNum PSN; |
| 213 | | /* Tell the dock about us */ |
| 214 | | if (!CPSGetCurrentProcess(&PSN)) |
| 215 | | if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) |
| 216 | | if (!CPSSetFrontProcess(&PSN)) |
| 217 | | [SDLApplication sharedApplication]; |
| 218 | | } |
| 219 | | #endif /* SDL_USE_CPS */ |
| 220 | | |
| 221 | | /* Set up the menubar */ |
| 222 | | [NSApp setMainMenu:[[NSMenu alloc] init]]; |
| 223 | | setApplicationMenu(); |
| 224 | | setupWindowMenu(); |
| 225 | | |
| 226 | | /* Create SDLMain and make it the app delegate */ |
| 227 | | sdlMain = [[SDLMain alloc] init]; |
| 228 | | [NSApp setDelegate:sdlMain]; |
| 229 | | |
| 230 | | /* Start the main event loop */ |
| 231 | | [NSApp run]; |
| 232 | | |
| 233 | | [sdlMain release]; |
| 234 | | [pool release]; |
| 235 | | } |
| 236 | | |
| 237 | | #endif |
| 238 | | |
| 239 | | |
| 240 | | /* |
| 241 | | * Catch document open requests...this lets us notice files when the app |
| 242 | | * was launched by double-clicking a document, or when a document was |
| 243 | | * dragged/dropped on the app's icon. You need to have a |
| 244 | | * CFBundleDocumentsType section in your Info.plist to get this message, |
| 245 | | * apparently. |
| 246 | | * |
| 247 | | * Files are added to gArgv, so to the app, they'll look like command line |
| 248 | | * arguments. Previously, apps launched from the finder had nothing but |
| 249 | | * an argv[0]. |
| 250 | | * |
| 251 | | * This message may be received multiple times to open several docs on launch. |
| 252 | | * |
| 253 | | * This message is ignored once the app's mainline has been called. |
| 254 | | */ |
| 255 | | - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename |
| 256 | | { |
| 257 | | const char *temparg; |
| 258 | | size_t arglen; |
| 259 | | char *arg; |
| 260 | | char **newargv; |
| 261 | | |
| 262 | | if (!gFinderLaunch) /* MacOS is passing command line args. */ |
| 263 | | return FALSE; |
| 264 | | |
| 265 | | if (gCalledAppMainline) /* app has started, ignore this document. */ |
| 266 | | return FALSE; |
| 267 | | |
| 268 | | temparg = [filename UTF8String]; |
| 269 | | arglen = SDL_strlen(temparg) + 1; |
| 270 | | arg = (char *) SDL_malloc(arglen); |
| 271 | | if (arg == NULL) |
| 272 | | return FALSE; |
| 273 | | |
| 274 | | newargv = (char **) SDL_realloc(gArgv, sizeof (char *) * (gArgc + 2)); |
| 275 | | if (newargv == NULL) |
| 276 | | { |
| 277 | | SDL_free(arg); |
| 278 | | return FALSE; |
| 279 | | } |
| 280 | | gArgv = newargv; |
| 281 | | |
| 282 | | SDL_strlcpy(arg, temparg, arglen); |
| 283 | | gArgv[gArgc++] = arg; |
| 284 | | gArgv[gArgc] = NULL; |
| 285 | | return TRUE; |
| 286 | | } |
| 287 | | |
| 288 | | |
| 289 | | /* Called when the internal event loop has just started running */ |
| 290 | | - (void) applicationDidFinishLaunching: (NSNotification *) note |
| 291 | | { |
| 292 | | int status; |
| 293 | | |
| 294 | | /* Set the working directory to the .app's parent directory */ |
| 295 | | [self setupWorkingDirectory:gFinderLaunch]; |
| 296 | | |
| 297 | | #if SDL_USE_NIB_FILE |
| 298 | | /* Set the main menu to contain the real app name instead of "SDL App" */ |
| 299 | | [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; |
| 300 | | #endif |
| 301 | | |
| 302 | | /* Hand off to main application code */ |
| 303 | | gCalledAppMainline = TRUE; |
| 304 | | status = SDL_main (gArgc, gArgv); |
| 305 | | |
| 306 | | /* We're done, thank you for playing */ |
| 307 | | exit(status); |
| 308 | | } |
| 309 | | @end |
| 310 | | |
| 311 | | |
| 312 | | @implementation NSString (ReplaceSubString) |
| 313 | | |
| 314 | | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString |
| 315 | | { |
| 316 | | unsigned int bufferSize; |
| 317 | | unsigned int selfLen = [self length]; |
| 318 | | unsigned int aStringLen = [aString length]; |
| 319 | | unichar *buffer; |
| 320 | | NSRange localRange; |
| 321 | | NSString *result; |
| 322 | | |
| 323 | | bufferSize = selfLen + aStringLen - aRange.length; |
| 324 | | buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); |
| 325 | | |
| 326 | | /* Get first part into buffer */ |
| 327 | | localRange.location = 0; |
| 328 | | localRange.length = aRange.location; |
| 329 | | [self getCharacters:buffer range:localRange]; |
| 330 | | |
| 331 | | /* Get middle part into buffer */ |
| 332 | | localRange.location = 0; |
| 333 | | localRange.length = aStringLen; |
| 334 | | [aString getCharacters:(buffer+aRange.location) range:localRange]; |
| 335 | | |
| 336 | | /* Get last part into buffer */ |
| 337 | | localRange.location = aRange.location + aRange.length; |
| 338 | | localRange.length = selfLen - localRange.location; |
| 339 | | [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; |
| 340 | | |
| 341 | | /* Build output string */ |
| 342 | | result = [NSString stringWithCharacters:buffer length:bufferSize]; |
| 343 | | |
| 344 | | NSDeallocateMemoryPages(buffer, bufferSize); |
| 345 | | |
| 346 | | return result; |
| 347 | | } |
| 348 | | |
| 349 | | @end |
| 350 | | |
| 351 | | |
| 352 | | |
| 353 | | #ifdef main |
| 354 | | # undef main |
| 355 | | #endif |
| 356 | | |
| 357 | | |
| 358 | | /* Main entry point to executable - should *not* be SDL_main! */ |
| 359 | | int main (int argc, char **argv) |
| 360 | | { |
| 361 | | /* Copy the arguments into a global variable */ |
| 362 | | /* This is passed if we are launched by double-clicking */ |
| 363 | | if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { |
| 364 | | gArgv = (char **) SDL_malloc(sizeof (char *) * 2); |
| 365 | | gArgv[0] = argv[0]; |
| 366 | | gArgv[1] = NULL; |
| 367 | | gArgc = 1; |
| 368 | | gFinderLaunch = YES; |
| 369 | | } else { |
| 370 | | int i; |
| 371 | | gArgc = argc; |
| 372 | | gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); |
| 373 | | for (i = 0; i <= argc; i++) |
| 374 | | gArgv[i] = argv[i]; |
| 375 | | gFinderLaunch = NO; |
| 376 | | } |
| 377 | | |
| 378 | | #if SDL_USE_NIB_FILE |
| 379 | | [SDLApplication poseAsClass:[NSApplication class]]; |
| 380 | | NSApplicationMain (argc, argv); |
| 381 | | #else |
| 382 | | CustomApplicationMain (argc, argv); |
| 383 | | #endif |
| 384 | | return 0; |
| 385 | | } |
| 386 | | |
trunk/src/osd/sdl/SDLMain_tmpl.mm
| r0 | r250165 | |
| 1 | // license:Zlib|LGPL-2.1+ |
| 2 | // copyright-holders:http://libsdl.org/ |
| 3 | /* SDLMain.m - main entry point for our Cocoa-ized SDL app |
| 4 | Initial Version: Darrell Walisser <dwaliss1@purdue.edu> |
| 5 | Non-NIB-Code & other changes: Max Horn <max@quendi.de> |
| 6 | |
| 7 | Feel free to customize this file to suit your needs |
| 8 | */ |
| 9 | |
| 10 | #import "sdlinc.h" |
| 11 | #import "SDLMain_tmpl.h" |
| 12 | #import <sys/param.h> /* for MAXPATHLEN */ |
| 13 | #import <unistd.h> |
| 14 | |
| 15 | /* For some reason, Apple removed setAppleMenu from the headers in 10.4, |
| 16 | but the method still is there and works. To avoid warnings, we declare |
| 17 | it ourselves here. */ |
| 18 | @interface NSApplication(SDL_Missing_Methods) |
| 19 | - (void)setAppleMenu:(NSMenu *)menu; |
| 20 | @end |
| 21 | |
| 22 | /* Use this flag to determine whether we use SDLMain.nib or not */ |
| 23 | #define SDL_USE_NIB_FILE 0 |
| 24 | |
| 25 | /* Use this flag to determine whether we use CPS (docking) or not */ |
| 26 | #define SDL_USE_CPS 0 |
| 27 | #ifdef SDL_USE_CPS |
| 28 | /* Portions of CPS.h */ |
| 29 | typedef struct CPSProcessSerNum |
| 30 | { |
| 31 | UInt32 lo; |
| 32 | UInt32 hi; |
| 33 | } CPSProcessSerNum; |
| 34 | |
| 35 | extern "C" OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); |
| 36 | extern "C" OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); |
| 37 | extern "C" OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); |
| 38 | |
| 39 | #endif /* SDL_USE_CPS */ |
| 40 | |
| 41 | static int gArgc; |
| 42 | static char **gArgv; |
| 43 | static BOOL gFinderLaunch; |
| 44 | static BOOL gCalledAppMainline = FALSE; |
| 45 | |
| 46 | static NSString *getApplicationName(void) |
| 47 | { |
| 48 | NSDictionary *dict; |
| 49 | NSString *appName = 0; |
| 50 | |
| 51 | /* Determine the application name */ |
| 52 | dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); |
| 53 | if (dict) |
| 54 | appName = [dict objectForKey: @"CFBundleName"]; |
| 55 | |
| 56 | if (![appName length]) |
| 57 | appName = [[NSProcessInfo processInfo] processName]; |
| 58 | |
| 59 | return appName; |
| 60 | } |
| 61 | |
| 62 | #if SDL_USE_NIB_FILE |
| 63 | /* A helper category for NSString */ |
| 64 | @interface NSString (ReplaceSubString) |
| 65 | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; |
| 66 | @end |
| 67 | #endif |
| 68 | |
| 69 | @interface SDLApplication : NSApplication |
| 70 | @end |
| 71 | |
| 72 | @implementation SDLApplication |
| 73 | /* Invoked from the Quit menu item */ |
| 74 | - (void)terminate:(id)sender |
| 75 | { |
| 76 | /* Post a SDL_QUIT event */ |
| 77 | SDL_Event event; |
| 78 | event.type = SDL_QUIT; |
| 79 | SDL_PushEvent(&event); |
| 80 | } |
| 81 | @end |
| 82 | |
| 83 | /* The main class of the application, the application's delegate */ |
| 84 | @implementation SDLMain |
| 85 | |
| 86 | /* Set the working directory to the .app's parent directory */ |
| 87 | - (void) setupWorkingDirectory:(BOOL)shouldChdir |
| 88 | { |
| 89 | if (shouldChdir) |
| 90 | { |
| 91 | char parentdir[MAXPATHLEN]; |
| 92 | CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); |
| 93 | CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); |
| 94 | if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) { |
| 95 | assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */ |
| 96 | } |
| 97 | CFRelease(url); |
| 98 | CFRelease(url2); |
| 99 | } |
| 100 | |
| 101 | } |
| 102 | |
| 103 | #if SDL_USE_NIB_FILE |
| 104 | |
| 105 | /* Fix menu to contain the real app name instead of "SDL App" */ |
| 106 | - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName |
| 107 | { |
| 108 | NSRange aRange; |
| 109 | NSEnumerator *enumerator; |
| 110 | NSMenuItem *menuItem; |
| 111 | |
| 112 | aRange = [[aMenu title] rangeOfString:@"SDL App"]; |
| 113 | if (aRange.length != 0) |
| 114 | [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; |
| 115 | |
| 116 | enumerator = [[aMenu itemArray] objectEnumerator]; |
| 117 | while ((menuItem = [enumerator nextObject])) |
| 118 | { |
| 119 | aRange = [[menuItem title] rangeOfString:@"SDL App"]; |
| 120 | if (aRange.length != 0) |
| 121 | [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; |
| 122 | if ([menuItem hasSubmenu]) |
| 123 | [self fixMenu:[menuItem submenu] withAppName:appName]; |
| 124 | } |
| 125 | [ aMenu sizeToFit ]; |
| 126 | } |
| 127 | |
| 128 | #else |
| 129 | |
| 130 | static void setApplicationMenu(void) |
| 131 | { |
| 132 | /* warning: this code is very odd */ |
| 133 | NSMenu *appleMenu; |
| 134 | NSMenuItem *menuItem; |
| 135 | NSString *title; |
| 136 | NSString *appName; |
| 137 | |
| 138 | appName = getApplicationName(); |
| 139 | appleMenu = [[NSMenu alloc] initWithTitle:@""]; |
| 140 | |
| 141 | /* Add menu items */ |
| 142 | title = [@"About " stringByAppendingString:appName]; |
| 143 | [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; |
| 144 | |
| 145 | [appleMenu addItem:[NSMenuItem separatorItem]]; |
| 146 | |
| 147 | title = [@"Hide " stringByAppendingString:appName]; |
| 148 | [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; |
| 149 | |
| 150 | menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; |
| 151 | [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; |
| 152 | |
| 153 | [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; |
| 154 | |
| 155 | [appleMenu addItem:[NSMenuItem separatorItem]]; |
| 156 | |
| 157 | title = [@"Quit " stringByAppendingString:appName]; |
| 158 | [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; |
| 159 | |
| 160 | |
| 161 | /* Put menu into the menubar */ |
| 162 | menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; |
| 163 | [menuItem setSubmenu:appleMenu]; |
| 164 | [[NSApp mainMenu] addItem:menuItem]; |
| 165 | |
| 166 | /* Tell the application object that this is now the application menu */ |
| 167 | [NSApp setAppleMenu:appleMenu]; |
| 168 | |
| 169 | /* Finally give up our references to the objects */ |
| 170 | [appleMenu release]; |
| 171 | [menuItem release]; |
| 172 | } |
| 173 | |
| 174 | /* Create a window menu */ |
| 175 | static void setupWindowMenu(void) |
| 176 | { |
| 177 | NSMenu *windowMenu; |
| 178 | NSMenuItem *windowMenuItem; |
| 179 | NSMenuItem *menuItem; |
| 180 | |
| 181 | windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; |
| 182 | |
| 183 | /* "Minimize" item */ |
| 184 | menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; |
| 185 | [windowMenu addItem:menuItem]; |
| 186 | [menuItem release]; |
| 187 | |
| 188 | /* Put menu into the menubar */ |
| 189 | windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; |
| 190 | [windowMenuItem setSubmenu:windowMenu]; |
| 191 | [[NSApp mainMenu] addItem:windowMenuItem]; |
| 192 | |
| 193 | /* Tell the application object that this is now the window menu */ |
| 194 | [NSApp setWindowsMenu:windowMenu]; |
| 195 | |
| 196 | /* Finally give up our references to the objects */ |
| 197 | [windowMenu release]; |
| 198 | [windowMenuItem release]; |
| 199 | } |
| 200 | |
| 201 | /* Replacement for NSApplicationMain */ |
| 202 | static void CustomApplicationMain (int argc, char **argv) |
| 203 | { |
| 204 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| 205 | SDLMain *sdlMain; |
| 206 | |
| 207 | /* Ensure the application object is initialised */ |
| 208 | [SDLApplication sharedApplication]; |
| 209 | |
| 210 | #ifdef SDL_USE_CPS |
| 211 | { |
| 212 | CPSProcessSerNum PSN; |
| 213 | /* Tell the dock about us */ |
| 214 | if (!CPSGetCurrentProcess(&PSN)) |
| 215 | if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) |
| 216 | if (!CPSSetFrontProcess(&PSN)) |
| 217 | [SDLApplication sharedApplication]; |
| 218 | } |
| 219 | #endif /* SDL_USE_CPS */ |
| 220 | |
| 221 | /* Set up the menubar */ |
| 222 | [NSApp setMainMenu:[[NSMenu alloc] init]]; |
| 223 | setApplicationMenu(); |
| 224 | setupWindowMenu(); |
| 225 | |
| 226 | /* Create SDLMain and make it the app delegate */ |
| 227 | sdlMain = [[SDLMain alloc] init]; |
| 228 | [NSApp setDelegate:sdlMain]; |
| 229 | |
| 230 | /* Start the main event loop */ |
| 231 | [NSApp run]; |
| 232 | |
| 233 | [sdlMain release]; |
| 234 | [pool release]; |
| 235 | } |
| 236 | |
| 237 | #endif |
| 238 | |
| 239 | |
| 240 | /* |
| 241 | * Catch document open requests...this lets us notice files when the app |
| 242 | * was launched by double-clicking a document, or when a document was |
| 243 | * dragged/dropped on the app's icon. You need to have a |
| 244 | * CFBundleDocumentsType section in your Info.plist to get this message, |
| 245 | * apparently. |
| 246 | * |
| 247 | * Files are added to gArgv, so to the app, they'll look like command line |
| 248 | * arguments. Previously, apps launched from the finder had nothing but |
| 249 | * an argv[0]. |
| 250 | * |
| 251 | * This message may be received multiple times to open several docs on launch. |
| 252 | * |
| 253 | * This message is ignored once the app's mainline has been called. |
| 254 | */ |
| 255 | - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename |
| 256 | { |
| 257 | const char *temparg; |
| 258 | size_t arglen; |
| 259 | char *arg; |
| 260 | char **newargv; |
| 261 | |
| 262 | if (!gFinderLaunch) /* MacOS is passing command line args. */ |
| 263 | return FALSE; |
| 264 | |
| 265 | if (gCalledAppMainline) /* app has started, ignore this document. */ |
| 266 | return FALSE; |
| 267 | |
| 268 | temparg = [filename UTF8String]; |
| 269 | arglen = SDL_strlen(temparg) + 1; |
| 270 | arg = (char *) SDL_malloc(arglen); |
| 271 | if (arg == NULL) |
| 272 | return FALSE; |
| 273 | |
| 274 | newargv = (char **) SDL_realloc(gArgv, sizeof (char *) * (gArgc + 2)); |
| 275 | if (newargv == NULL) |
| 276 | { |
| 277 | SDL_free(arg); |
| 278 | return FALSE; |
| 279 | } |
| 280 | gArgv = newargv; |
| 281 | |
| 282 | SDL_strlcpy(arg, temparg, arglen); |
| 283 | gArgv[gArgc++] = arg; |
| 284 | gArgv[gArgc] = NULL; |
| 285 | return TRUE; |
| 286 | } |
| 287 | |
| 288 | |
| 289 | /* Called when the internal event loop has just started running */ |
| 290 | - (void) applicationDidFinishLaunching: (NSNotification *) note |
| 291 | { |
| 292 | int status; |
| 293 | |
| 294 | /* Set the working directory to the .app's parent directory */ |
| 295 | [self setupWorkingDirectory:gFinderLaunch]; |
| 296 | |
| 297 | #if SDL_USE_NIB_FILE |
| 298 | /* Set the main menu to contain the real app name instead of "SDL App" */ |
| 299 | [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; |
| 300 | #endif |
| 301 | |
| 302 | /* Hand off to main application code */ |
| 303 | gCalledAppMainline = TRUE; |
| 304 | status = SDL_main (gArgc, gArgv); |
| 305 | |
| 306 | /* We're done, thank you for playing */ |
| 307 | exit(status); |
| 308 | } |
| 309 | @end |
| 310 | |
| 311 | |
| 312 | @implementation NSString (ReplaceSubString) |
| 313 | |
| 314 | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString |
| 315 | { |
| 316 | unsigned int bufferSize; |
| 317 | unsigned int selfLen = [self length]; |
| 318 | unsigned int aStringLen = [aString length]; |
| 319 | unichar *buffer; |
| 320 | NSRange localRange; |
| 321 | NSString *result; |
| 322 | |
| 323 | bufferSize = selfLen + aStringLen - aRange.length; |
| 324 | buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); |
| 325 | |
| 326 | /* Get first part into buffer */ |
| 327 | localRange.location = 0; |
| 328 | localRange.length = aRange.location; |
| 329 | [self getCharacters:buffer range:localRange]; |
| 330 | |
| 331 | /* Get middle part into buffer */ |
| 332 | localRange.location = 0; |
| 333 | localRange.length = aStringLen; |
| 334 | [aString getCharacters:(buffer+aRange.location) range:localRange]; |
| 335 | |
| 336 | /* Get last part into buffer */ |
| 337 | localRange.location = aRange.location + aRange.length; |
| 338 | localRange.length = selfLen - localRange.location; |
| 339 | [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; |
| 340 | |
| 341 | /* Build output string */ |
| 342 | result = [NSString stringWithCharacters:buffer length:bufferSize]; |
| 343 | |
| 344 | NSDeallocateMemoryPages(buffer, bufferSize); |
| 345 | |
| 346 | return result; |
| 347 | } |
| 348 | |
| 349 | @end |
| 350 | |
| 351 | |
| 352 | |
| 353 | #ifdef main |
| 354 | # undef main |
| 355 | #endif |
| 356 | |
| 357 | |
| 358 | /* Main entry point to executable - should *not* be SDL_main! */ |
| 359 | int main (int argc, char **argv) |
| 360 | { |
| 361 | /* Copy the arguments into a global variable */ |
| 362 | /* This is passed if we are launched by double-clicking */ |
| 363 | if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { |
| 364 | gArgv = (char **) SDL_malloc(sizeof (char *) * 2); |
| 365 | gArgv[0] = argv[0]; |
| 366 | gArgv[1] = NULL; |
| 367 | gArgc = 1; |
| 368 | gFinderLaunch = YES; |
| 369 | } else { |
| 370 | int i; |
| 371 | gArgc = argc; |
| 372 | gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); |
| 373 | for (i = 0; i <= argc; i++) |
| 374 | gArgv[i] = argv[i]; |
| 375 | gFinderLaunch = NO; |
| 376 | } |
| 377 | |
| 378 | #if SDL_USE_NIB_FILE |
| 379 | [SDLApplication poseAsClass:[NSApplication class]]; |
| 380 | NSApplicationMain (argc, argv); |
| 381 | #else |
| 382 | CustomApplicationMain (argc, argv); |
| 383 | #endif |
| 384 | return 0; |
| 385 | } |
| 386 | |
trunk/src/osd/sdl/aueffectutil.m
| r250164 | r250165 | |
| 1 | | #import <AvailabilityMacros.h> |
| 2 | | #import <AudioUnit/AudioUnit.h> |
| 3 | | #import <AudioUnit/AUCocoaUIView.h> |
| 4 | | #import <AudioToolbox/AudioToolbox.h> |
| 5 | | #import <Cocoa/Cocoa.h> |
| 6 | | #import <CoreAudio/CoreAudio.h> |
| 7 | | #import <CoreAudioKit/CoreAudioKit.h> |
| 8 | | #import <CoreFoundation/CoreFoundation.h> |
| 9 | | #import <CoreServices/CoreServices.h> |
| 10 | | |
| 11 | | #include <utility> |
| 12 | | #include <vector> |
| 13 | | |
| 14 | | #include <stdlib.h> |
| 15 | | |
| 16 | | |
| 17 | | #ifdef MAC_OS_X_VERSION_MAX_ALLOWED |
| 18 | | |
| 19 | | #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 |
| 20 | | |
| 21 | | typedef ComponentDescription AudioComponentDescription; |
| 22 | | |
| 23 | | @protocol NSApplicationDelegate <NSObject> |
| 24 | | @end |
| 25 | | |
| 26 | | @protocol NSWindowDelegate <NSObject> |
| 27 | | @end |
| 28 | | |
| 29 | | #endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1060 |
| 30 | | |
| 31 | | #endif // MAC_OS_X_VERSION_MAX_ALLOWED |
| 32 | | |
| 33 | | |
| 34 | | struct EffectInfo |
| 35 | | { |
| 36 | | Component component; |
| 37 | | OSType type; |
| 38 | | OSType subtype; |
| 39 | | OSType manufacturer; |
| 40 | | }; |
| 41 | | |
| 42 | | |
| 43 | | static NSString *const AUEffectUtilErrorDomain = @"AUEffectUtilErrorDomain"; |
| 44 | | |
| 45 | | static NSString *const AUEffectDocumentType = @"AUEffect"; |
| 46 | | static NSString *const AUPresetDocumentType = @"AudioUnit Preset"; |
| 47 | | |
| 48 | | static NSString *const ComponentTypeKey = @"ComponentType"; |
| 49 | | static NSString *const ComponentSubTypeKey = @"ComponentSubType"; |
| 50 | | static NSString *const ComponentManufacturerKey = @"ComponentManufacturer"; |
| 51 | | static NSString *const ClassInfoKey = @"ClassInfo"; |
| 52 | | static NSString *const ForceGenericViewKey = @"ForceGenericView"; |
| 53 | | static NSString *const WindowFrameKey = @"WindowFrame"; |
| 54 | | |
| 55 | | |
| 56 | | static void UpdateChangeCountCallback(void *userData, |
| 57 | | void *object, |
| 58 | | AudioUnitEvent const *inEvent, |
| 59 | | UInt64 inEventHostTime, |
| 60 | | AudioUnitParameterValue inParameterValue) |
| 61 | | { |
| 62 | | [(NSDocument *)userData updateChangeCount:NSChangeDone]; |
| 63 | | } |
| 64 | | |
| 65 | | |
| 66 | | @interface AUEffectDocument : NSDocument <NSWindowDelegate> |
| 67 | | { |
| 68 | | IBOutlet NSWindow *window; |
| 69 | | IBOutlet NSButton *genericViewButton; |
| 70 | | IBOutlet NSPopUpButton *presetButton; |
| 71 | | NSView *view; |
| 72 | | NSSize headerSize; |
| 73 | | CFArrayRef presets; |
| 74 | | AUParameterListenerRef listener; |
| 75 | | BOOL forceGenericView; |
| 76 | | NSString *restoreFrame; |
| 77 | | |
| 78 | | AudioComponentDescription description; |
| 79 | | AUGraph graph; |
| 80 | | AUNode outputNode, sourceNode, effectNode; |
| 81 | | AudioUnit outputUnit, sourceUnit, effectUnit; |
| 82 | | } |
| 83 | | |
| 84 | | - (void)dealloc; |
| 85 | | |
| 86 | | - (void)makeWindowControllers; |
| 87 | | - (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error; |
| 88 | | - (NSData *)dataOfType:(NSString *)type error:(NSError **)error; |
| 89 | | |
| 90 | | - (IBAction)toggleGenericView:(id)sender; |
| 91 | | - (IBAction)loadPreset:(id)sender; |
| 92 | | |
| 93 | | - (void)viewFrameDidChange:(NSNotification *)notification; |
| 94 | | |
| 95 | | @end |
| 96 | | |
| 97 | | @implementation AUEffectDocument |
| 98 | | |
| 99 | | - (void)loadEffectUI { |
| 100 | | if ((0 == effectNode) || (nil == window)) |
| 101 | | return; |
| 102 | | |
| 103 | | BOOL customViewValid = NO; |
| 104 | | OSStatus status; |
| 105 | | UInt32 uiDescSize; |
| 106 | | AudioUnitCocoaViewInfo *viewInfo; |
| 107 | | status = AudioUnitGetPropertyInfo(effectUnit, |
| 108 | | kAudioUnitProperty_CocoaUI, |
| 109 | | kAudioUnitScope_Global, |
| 110 | | 0, |
| 111 | | &uiDescSize, |
| 112 | | NULL); |
| 113 | | UInt32 const uiClassCount = 1 + ((uiDescSize - sizeof(*viewInfo)) / sizeof(viewInfo->mCocoaAUViewClass[0])); |
| 114 | | if ((noErr == status) && (0 < uiClassCount)) |
| 115 | | { |
| 116 | | viewInfo = (AudioUnitCocoaViewInfo *)malloc(uiDescSize); |
| 117 | | status = AudioUnitGetProperty(effectUnit, |
| 118 | | kAudioUnitProperty_CocoaUI, |
| 119 | | kAudioUnitScope_Global, |
| 120 | | 0, |
| 121 | | viewInfo, |
| 122 | | &uiDescSize); |
| 123 | | if (noErr == status) |
| 124 | | { |
| 125 | | NSBundle *const bundle = [NSBundle bundleWithPath:[(NSURL *)viewInfo->mCocoaAUViewBundleLocation path]]; |
| 126 | | Class const viewClass = [bundle classNamed:(NSString *)viewInfo->mCocoaAUViewClass[0]]; |
| 127 | | if ((NULL != viewClass) |
| 128 | | && [viewClass conformsToProtocol:@protocol(AUCocoaUIBase)] |
| 129 | | && [viewClass instancesRespondToSelector:@selector(uiViewForAudioUnit:withSize:)]) |
| 130 | | { |
| 131 | | customViewValid = YES; |
| 132 | | if (!forceGenericView) |
| 133 | | { |
| 134 | | id const factory = [[viewClass alloc] init]; |
| 135 | | view = [factory uiViewForAudioUnit:effectUnit |
| 136 | | withSize:[[window contentView] bounds].size]; |
| 137 | | [factory release]; |
| 138 | | } |
| 139 | | } |
| 140 | | CFRelease(viewInfo->mCocoaAUViewBundleLocation); |
| 141 | | for (UInt32 i = 0; i < uiClassCount; i++) |
| 142 | | CFRelease(viewInfo->mCocoaAUViewClass[i]); |
| 143 | | } |
| 144 | | free(viewInfo); |
| 145 | | } |
| 146 | | if (nil == view) |
| 147 | | { |
| 148 | | view = [[[AUGenericView alloc] initWithAudioUnit:effectUnit] autorelease]; |
| 149 | | [(AUGenericView *)view setShowsExpertParameters:YES]; |
| 150 | | } |
| 151 | | |
| 152 | | [view setAutoresizingMask:NSViewNotSizable]; |
| 153 | | [view setFrameOrigin:NSMakePoint(0, 0)]; |
| 154 | | NSRect const oldFrame = [window frame]; |
| 155 | | NSRect const desired = [window frameRectForContentRect:[view frame]]; |
| 156 | | NSRect const newFrame = NSMakeRect(oldFrame.origin.x, |
| 157 | | oldFrame.origin.y + oldFrame.size.height - headerSize.height - desired.size.height, |
| 158 | | desired.size.width, |
| 159 | | headerSize.height + desired.size.height); |
| 160 | | [window setFrame:newFrame display:YES animate:NO]; |
| 161 | | [[window contentView] addSubview:view]; |
| 162 | | [view setPostsFrameChangedNotifications:YES]; |
| 163 | | [[NSNotificationCenter defaultCenter] addObserver:self |
| 164 | | selector:@selector(viewFrameDidChange:) |
| 165 | | name:NSViewFrameDidChangeNotification |
| 166 | | object:view]; |
| 167 | | |
| 168 | | [genericViewButton setEnabled:customViewValid]; |
| 169 | | if (!customViewValid) |
| 170 | | { |
| 171 | | forceGenericView = YES; |
| 172 | | [genericViewButton setState:NSOnState]; |
| 173 | | } |
| 174 | | |
| 175 | | CFIndex const presetCount = (NULL != presets) ? CFArrayGetCount(presets) : 0; |
| 176 | | [presetButton setEnabled:(0 < presetCount)]; |
| 177 | | while (1 < [presetButton numberOfItems]) |
| 178 | | [presetButton removeItemAtIndex:1]; |
| 179 | | for (CFIndex i = 0; i < presetCount; i++) |
| 180 | | { |
| 181 | | AUPreset const *preset = (AUPreset const*)CFArrayGetValueAtIndex(presets, i); |
| 182 | | NSMenuItem const *item = [[presetButton menu] addItemWithTitle:(NSString *)preset->presetName |
| 183 | | action:@selector(loadPreset:) |
| 184 | | keyEquivalent:@""]; |
| 185 | | [item setTarget:self]; |
| 186 | | [item setTag:i]; |
| 187 | | } |
| 188 | | } |
| 189 | | |
| 190 | | - (id)init { |
| 191 | | if (!(self = [super init])) return nil; |
| 192 | | |
| 193 | | window = nil; |
| 194 | | genericViewButton = nil; |
| 195 | | presetButton = nil; |
| 196 | | view = nil; |
| 197 | | presets = NULL; |
| 198 | | listener = NULL; |
| 199 | | forceGenericView = NO; |
| 200 | | restoreFrame = nil; |
| 201 | | |
| 202 | | description.componentType = description.componentSubType = description.componentManufacturer = 0; |
| 203 | | description.componentFlags = description.componentFlagsMask = 0; |
| 204 | | graph = NULL; |
| 205 | | outputNode = sourceNode = effectNode = 0; |
| 206 | | |
| 207 | | AudioComponentDescription const outputDesc = { kAudioUnitType_Output, |
| 208 | | kAudioUnitSubType_DefaultOutput, |
| 209 | | kAudioUnitManufacturer_Apple, |
| 210 | | 0, |
| 211 | | 0, }; |
| 212 | | AudioComponentDescription const sourceDesc = { kAudioUnitType_Generator, |
| 213 | | kAudioUnitSubType_AudioFilePlayer, |
| 214 | | kAudioUnitManufacturer_Apple, |
| 215 | | 0, |
| 216 | | 0, }; |
| 217 | | if ((noErr != NewAUGraph(&graph)) |
| 218 | | || (noErr != AUGraphAddNode(graph, &outputDesc, &outputNode)) |
| 219 | | || (noErr != AUGraphAddNode(graph, &sourceDesc, &sourceNode)) |
| 220 | | || (noErr != AUGraphOpen(graph)) |
| 221 | | || (noErr != AUGraphNodeInfo(graph, outputNode, NULL, &outputUnit)) |
| 222 | | || (noErr != AUGraphNodeInfo(graph, sourceNode, NULL, &sourceUnit)) |
| 223 | | || (noErr != AUGraphInitialize(graph))) |
| 224 | | { |
| 225 | | [self release]; |
| 226 | | return nil; |
| 227 | | } |
| 228 | | |
| 229 | | return self; |
| 230 | | } |
| 231 | | |
| 232 | | - (void)dealloc { |
| 233 | | if (NULL != presets) |
| 234 | | CFRelease(presets); |
| 235 | | |
| 236 | | if (NULL != listener) |
| 237 | | AUListenerDispose(listener); |
| 238 | | |
| 239 | | if (nil != restoreFrame) |
| 240 | | [restoreFrame release]; |
| 241 | | |
| 242 | | if (NULL != graph) |
| 243 | | { |
| 244 | | AUGraphClose(graph); |
| 245 | | DisposeAUGraph(graph); |
| 246 | | } |
| 247 | | |
| 248 | | [super dealloc]; |
| 249 | | } |
| 250 | | |
| 251 | | - (void)makeWindowControllers { |
| 252 | | genericViewButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 18)]; |
| 253 | | [genericViewButton setAutoresizingMask:NSViewNotSizable]; |
| 254 | | [[genericViewButton cell] setControlSize:NSSmallControlSize]; |
| 255 | | [genericViewButton setButtonType:NSSwitchButton]; |
| 256 | | [genericViewButton setBordered:NO]; |
| 257 | | [genericViewButton setAllowsMixedState:NO]; |
| 258 | | [genericViewButton setState:(forceGenericView ? NSOnState : NSOffState)]; |
| 259 | | [genericViewButton setTitle:@"Use generic editor view"]; |
| 260 | | [genericViewButton setAction:@selector(toggleGenericView:)]; |
| 261 | | [genericViewButton setTarget:self]; |
| 262 | | [genericViewButton sizeToFit]; |
| 263 | | |
| 264 | | presetButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 22) pullsDown:YES]; |
| 265 | | [presetButton setAutoresizingMask:NSViewNotSizable]; |
| 266 | | [[presetButton cell] setControlSize:NSSmallControlSize]; |
| 267 | | [[presetButton cell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; |
| 268 | | [presetButton setTitle:@"Load preset"]; |
| 269 | | [[[presetButton menu] addItemWithTitle:@"Load preset" action:NULL keyEquivalent:@""] setHidden:YES]; |
| 270 | | [presetButton sizeToFit]; |
| 271 | | |
| 272 | | CGFloat const controlWidth = MAX(NSWidth([genericViewButton frame]), NSWidth([presetButton frame])); |
| 273 | | NSRect const presetFrame = NSMakeRect(17, |
| 274 | | 8, |
| 275 | | controlWidth, |
| 276 | | NSHeight([presetButton frame])); |
| 277 | | NSRect const genericViewFrame = NSMakeRect(17, |
| 278 | | NSMaxY(presetFrame) + 9, |
| 279 | | controlWidth, |
| 280 | | NSHeight([genericViewButton frame])); |
| 281 | | [genericViewButton setFrame:genericViewFrame]; |
| 282 | | [presetButton setFrame:presetFrame]; |
| 283 | | |
| 284 | | headerSize = NSMakeSize((2 * 17) + controlWidth, 18 + NSMaxY(genericViewFrame)); |
| 285 | | NSView *const container = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, headerSize.width, headerSize.height)]; |
| 286 | | [container setAutoresizingMask:(NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin)]; |
| 287 | | [container addSubview:genericViewButton]; |
| 288 | | [genericViewButton release]; |
| 289 | | [container addSubview:presetButton]; |
| 290 | | [presetButton release]; |
| 291 | | |
| 292 | | window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, headerSize.width, headerSize.height) |
| 293 | | styleMask:(NSTitledWindowMask | |
| 294 | | NSClosableWindowMask | |
| 295 | | NSMiniaturizableWindowMask) |
| 296 | | backing:NSBackingStoreBuffered |
| 297 | | defer:YES]; |
| 298 | | [window setReleasedWhenClosed:NO]; |
| 299 | | [window setDelegate:self]; |
| 300 | | [window setTitle:@"Effect"]; |
| 301 | | [[window contentView] addSubview:container]; |
| 302 | | [container release]; |
| 303 | | [self setWindow:window]; |
| 304 | | |
| 305 | | NSWindowController *const controller = [[NSWindowController alloc] initWithWindow:window]; |
| 306 | | [self addWindowController:controller]; |
| 307 | | [controller release]; |
| 308 | | [window release]; |
| 309 | | |
| 310 | | [self loadEffectUI]; |
| 311 | | if (nil != restoreFrame) |
| 312 | | { |
| 313 | | [window setFrameFromString:restoreFrame]; |
| 314 | | } |
| 315 | | else |
| 316 | | { |
| 317 | | NSRect const available = [[NSScreen mainScreen] visibleFrame]; |
| 318 | | NSRect frame = [window frame]; |
| 319 | | frame.origin.x = (NSWidth(available) - NSWidth(frame)) / 4; |
| 320 | | frame.origin.y = (NSHeight(available) - NSHeight(frame)) * 3 / 4; |
| 321 | | [window setFrame:frame display:YES animate:NO]; |
| 322 | | } |
| 323 | | } |
| 324 | | |
| 325 | | - (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error { |
| 326 | | OSStatus status; |
| 327 | | UInt32 propertySize; |
| 328 | | |
| 329 | | BOOL const hasWrapper = [type isEqualToString:AUEffectDocumentType]; |
| 330 | | if (!hasWrapper && ![type isEqualToString:AUPresetDocumentType]) |
| 331 | | { |
| 332 | | if (NULL != error) |
| 333 | | { |
| 334 | | NSString *const message = [NSString stringWithFormat:@"Unsupported document type %@", type]; |
| 335 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 336 | | nil]; |
| 337 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 338 | | } |
| 339 | | return NO; |
| 340 | | } |
| 341 | | |
| 342 | | NSString *errDesc = nil; |
| 343 | | id const desc = [NSPropertyListSerialization propertyListFromData:data |
| 344 | | mutabilityOption:0 |
| 345 | | format:NULL |
| 346 | | errorDescription:&errDesc]; |
| 347 | | if ((nil == desc) || ![desc isKindOfClass:[NSDictionary class]] || (nil != errDesc)) |
| 348 | | { |
| 349 | | if (NULL != error) |
| 350 | | { |
| 351 | | NSString *const message = [NSString stringWithFormat:@"Error in file format (%@)", errDesc]; |
| 352 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 353 | | nil]; |
| 354 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 355 | | } |
| 356 | | if (nil != errDesc) |
| 357 | | [errDesc release]; |
| 358 | | return NO; |
| 359 | | } |
| 360 | | |
| 361 | | id const typeValue = [desc objectForKey:(hasWrapper ? ComponentTypeKey : (NSString *)CFSTR(kAUPresetTypeKey))]; |
| 362 | | id const subtypeValue = [desc objectForKey:(hasWrapper ? ComponentSubTypeKey : (NSString *)CFSTR(kAUPresetSubtypeKey))]; |
| 363 | | id const manufacturerValue = [desc objectForKey:(hasWrapper ? ComponentManufacturerKey : (NSString *)CFSTR(kAUPresetManufacturerKey))]; |
| 364 | | if ((nil == typeValue) || ![typeValue isKindOfClass:[NSNumber class]] |
| 365 | | || (nil == subtypeValue) || ![subtypeValue isKindOfClass:[NSNumber class]] |
| 366 | | || (nil == manufacturerValue) || ![manufacturerValue isKindOfClass:[NSNumber class]] |
| 367 | | || ([typeValue unsignedLongValue] != kAudioUnitType_Effect)) |
| 368 | | { |
| 369 | | if (NULL != error) |
| 370 | | { |
| 371 | | NSString *const message = @"Error in effect description file format"; |
| 372 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 373 | | nil]; |
| 374 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 375 | | } |
| 376 | | return NO; |
| 377 | | } |
| 378 | | |
| 379 | | if (NULL != presets) |
| 380 | | { |
| 381 | | CFRelease(presets); |
| 382 | | presets = NULL; |
| 383 | | } |
| 384 | | if (NULL != listener) |
| 385 | | { |
| 386 | | AUListenerDispose(listener); |
| 387 | | listener = NULL; |
| 388 | | } |
| 389 | | if (nil != view) |
| 390 | | { |
| 391 | | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 392 | | name:NSViewFrameDidChangeNotification |
| 393 | | object:nil]; |
| 394 | | [view removeFromSuperview]; |
| 395 | | view = nil; |
| 396 | | } |
| 397 | | if (0 != effectNode) |
| 398 | | { |
| 399 | | view = nil; |
| 400 | | AUGraphRemoveNode(graph, effectNode); |
| 401 | | effectNode = 0; |
| 402 | | } |
| 403 | | |
| 404 | | description.componentType = [typeValue longValue]; |
| 405 | | description.componentSubType = [subtypeValue longValue]; |
| 406 | | description.componentManufacturer = [manufacturerValue longValue]; |
| 407 | | status = noErr; |
| 408 | | status = AUGraphClearConnections(graph); |
| 409 | | if (noErr == status) status = AUGraphAddNode(graph, &description, &effectNode); |
| 410 | | if (noErr == status) status = AUGraphNodeInfo(graph, effectNode, NULL, &effectUnit); |
| 411 | | if (noErr == status) status = AUGraphConnectNodeInput(graph, sourceNode, 0, effectNode, 0); |
| 412 | | if (noErr == status) status = AUGraphConnectNodeInput(graph, effectNode, 0, outputNode, 0); |
| 413 | | if (noErr == status) status = AUGraphUpdate(graph, NULL); |
| 414 | | if (noErr != status) |
| 415 | | { |
| 416 | | if (NULL != error) |
| 417 | | { |
| 418 | | NSString * const message = @"Error encountered while configuring AudioUnit graph"; |
| 419 | | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 420 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 421 | | underlying, NSUnderlyingErrorKey, |
| 422 | | nil]; |
| 423 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 424 | | } |
| 425 | | return NO; |
| 426 | | } |
| 427 | | |
| 428 | | CFPropertyListRef const classInfo = (CFPropertyListRef)(hasWrapper ? [desc objectForKey:ClassInfoKey] : desc); |
| 429 | | if (NULL != classInfo) |
| 430 | | { |
| 431 | | AudioUnitParameter change = { effectUnit, kAUParameterListener_AnyParameter, 0, 0 }; |
| 432 | | status = AudioUnitSetProperty(effectUnit, |
| 433 | | kAudioUnitProperty_ClassInfo, |
| 434 | | kAudioUnitScope_Global, |
| 435 | | 0, |
| 436 | | &classInfo, |
| 437 | | sizeof(classInfo)); |
| 438 | | if (noErr == status) status = AUParameterListenerNotify(NULL, NULL, &change); |
| 439 | | if (noErr != status) |
| 440 | | { |
| 441 | | if (NULL != error) |
| 442 | | { |
| 443 | | NSString * const message = @"Error configuring effect"; |
| 444 | | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 445 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 446 | | underlying, NSUnderlyingErrorKey, |
| 447 | | nil]; |
| 448 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 449 | | } |
| 450 | | return NO; |
| 451 | | } |
| 452 | | } |
| 453 | | |
| 454 | | propertySize = 0; |
| 455 | | status = AudioUnitGetPropertyInfo( |
| 456 | | effectUnit, |
| 457 | | kAudioUnitProperty_ParameterList, |
| 458 | | kAudioUnitScope_Global, |
| 459 | | 0, |
| 460 | | &propertySize, |
| 461 | | NULL); |
| 462 | | if (noErr != status) |
| 463 | | { |
| 464 | | if (NULL != error) |
| 465 | | { |
| 466 | | NSString * const message = @"Error getting effect parameters"; |
| 467 | | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 468 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 469 | | underlying, NSUnderlyingErrorKey, |
| 470 | | nil]; |
| 471 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 472 | | } |
| 473 | | return NO; |
| 474 | | } |
| 475 | | UInt32 const paramCount = propertySize / sizeof(AudioUnitParameterID); |
| 476 | | if (0U < paramCount) |
| 477 | | { |
| 478 | | status = AUEventListenerCreate(UpdateChangeCountCallback, |
| 479 | | self, |
| 480 | | CFRunLoopGetCurrent(), |
| 481 | | kCFRunLoopDefaultMode, |
| 482 | | 0.05, |
| 483 | | 0.05, |
| 484 | | &listener); |
| 485 | | if (noErr != status) |
| 486 | | { |
| 487 | | if (NULL != error) |
| 488 | | { |
| 489 | | NSString * const message = @"Error creating AudioUnit event listener"; |
| 490 | | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 491 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 492 | | underlying, NSUnderlyingErrorKey, |
| 493 | | nil]; |
| 494 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 495 | | } |
| 496 | | return NO; |
| 497 | | } |
| 498 | | AudioUnitParameterID *const params = (AudioUnitParameterID *)malloc(propertySize); |
| 499 | | AudioUnitGetProperty( |
| 500 | | effectUnit, |
| 501 | | kAudioUnitProperty_ParameterList, |
| 502 | | kAudioUnitScope_Global, |
| 503 | | 0, |
| 504 | | params, |
| 505 | | &propertySize); |
| 506 | | if (noErr != status) |
| 507 | | { |
| 508 | | free(params); |
| 509 | | if (NULL != error) |
| 510 | | { |
| 511 | | NSString * const message = @"Error getting effect parameters"; |
| 512 | | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 513 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 514 | | underlying, NSUnderlyingErrorKey, |
| 515 | | nil]; |
| 516 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 517 | | } |
| 518 | | return NO; |
| 519 | | } |
| 520 | | for (UInt32 i = 0; (i < paramCount) && (noErr == status); i++) |
| 521 | | { |
| 522 | | AudioUnitEvent event; |
| 523 | | event.mEventType = kAudioUnitEvent_ParameterValueChange; |
| 524 | | event.mArgument.mParameter.mAudioUnit = effectUnit; |
| 525 | | event.mArgument.mParameter.mParameterID = params[i]; |
| 526 | | event.mArgument.mParameter.mScope = kAudioUnitScope_Global; |
| 527 | | event.mArgument.mParameter.mElement = 0; |
| 528 | | status = AUEventListenerAddEventType(listener, self, &event); |
| 529 | | } |
| 530 | | free(params); |
| 531 | | if (noErr != status) |
| 532 | | { |
| 533 | | free(params); |
| 534 | | if (NULL != error) |
| 535 | | { |
| 536 | | NSString * const message = @"Error getting effect parameters"; |
| 537 | | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 538 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 539 | | underlying, NSUnderlyingErrorKey, |
| 540 | | nil]; |
| 541 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 542 | | } |
| 543 | | return NO; |
| 544 | | } |
| 545 | | } |
| 546 | | |
| 547 | | propertySize = sizeof(presets); |
| 548 | | status = AudioUnitGetProperty(effectUnit, |
| 549 | | kAudioUnitProperty_FactoryPresets, |
| 550 | | kAudioUnitScope_Global, |
| 551 | | 0, |
| 552 | | &presets, |
| 553 | | &propertySize); |
| 554 | | if ((noErr != status) && (NULL != presets)) |
| 555 | | { |
| 556 | | CFRelease(presets); |
| 557 | | presets = NULL; |
| 558 | | } |
| 559 | | |
| 560 | | if (hasWrapper) |
| 561 | | { |
| 562 | | if ((nil != [desc objectForKey:ForceGenericViewKey]) |
| 563 | | && [[desc objectForKey:ForceGenericViewKey] respondsToSelector:@selector(boolValue)]) |
| 564 | | { |
| 565 | | forceGenericView = [[desc objectForKey:ForceGenericViewKey] boolValue]; |
| 566 | | [genericViewButton setState:(forceGenericView ? NSOnState : NSOffState)]; |
| 567 | | } |
| 568 | | if ((nil != [desc objectForKey:WindowFrameKey]) |
| 569 | | && [[desc objectForKey:WindowFrameKey] isKindOfClass:[NSString class]]) |
| 570 | | { |
| 571 | | if (nil != restoreFrame) [restoreFrame release]; |
| 572 | | restoreFrame = [[NSString alloc] initWithString:[desc objectForKey:WindowFrameKey]]; |
| 573 | | } |
| 574 | | } |
| 575 | | |
| 576 | | [self loadEffectUI]; |
| 577 | | |
| 578 | | return YES; |
| 579 | | } |
| 580 | | |
| 581 | | - (NSData *)dataOfType:(NSString *)type error:(NSError **)error { |
| 582 | | CFPropertyListRef classInfo; |
| 583 | | UInt32 infoSize = sizeof(classInfo); |
| 584 | | OSStatus const status = AudioUnitGetProperty(effectUnit, |
| 585 | | kAudioUnitProperty_ClassInfo, |
| 586 | | kAudioUnitScope_Global, |
| 587 | | 0, |
| 588 | | &classInfo, |
| 589 | | &infoSize); |
| 590 | | if (noErr != status) |
| 591 | | { |
| 592 | | if (NULL != error) |
| 593 | | { |
| 594 | | NSString const *message = @"Error getting effect settings"; |
| 595 | | NSError const *underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 596 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 597 | | underlying, NSUnderlyingErrorKey, |
| 598 | | nil]; |
| 599 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 600 | | } |
| 601 | | return nil; |
| 602 | | } |
| 603 | | NSDictionary *desc = nil; |
| 604 | | if ([type isEqualToString:AUEffectDocumentType]) |
| 605 | | { |
| 606 | | NSNumber const *typeVal = [NSNumber numberWithUnsignedLong:description.componentType]; |
| 607 | | NSNumber const *subtypeVal = [NSNumber numberWithUnsignedLong:description.componentSubType]; |
| 608 | | NSNumber const *manufacturerVal = [NSNumber numberWithUnsignedLong:description.componentManufacturer]; |
| 609 | | NSNumber const *forceGenericViewVal = [NSNumber numberWithBool:forceGenericView]; |
| 610 | | NSString const *windowFrameVal = [window stringWithSavedFrame]; |
| 611 | | desc = [NSDictionary dictionaryWithObjectsAndKeys:typeVal, ComponentTypeKey, |
| 612 | | subtypeVal, ComponentSubTypeKey, |
| 613 | | manufacturerVal, ComponentManufacturerKey, |
| 614 | | classInfo, ClassInfoKey, |
| 615 | | forceGenericViewVal, ForceGenericViewKey, |
| 616 | | windowFrameVal, WindowFrameKey, |
| 617 | | nil]; |
| 618 | | } |
| 619 | | else if ([type isEqualToString:AUPresetDocumentType]) |
| 620 | | { |
| 621 | | desc = [NSDictionary dictionaryWithDictionary:(NSDictionary *)classInfo]; |
| 622 | | } |
| 623 | | CFRelease(classInfo); |
| 624 | | if (nil == desc) |
| 625 | | { |
| 626 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:@"Unsupported document type", NSLocalizedDescriptionKey, |
| 627 | | nil]; |
| 628 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 629 | | return nil; |
| 630 | | } |
| 631 | | |
| 632 | | NSString *errDesc = nil; |
| 633 | | NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc |
| 634 | | format:NSPropertyListXMLFormat_v1_0 |
| 635 | | errorDescription:&errDesc]; |
| 636 | | if ((nil == data) || (nil != errDesc)) |
| 637 | | { |
| 638 | | if (NULL != error) |
| 639 | | { |
| 640 | | NSString *message; |
| 641 | | if (nil != errDesc) |
| 642 | | message = [NSString stringWithFormat:@"Error serialising effect settings: %@", errDesc]; |
| 643 | | else |
| 644 | | message = @"Error serialising effect settings"; |
| 645 | | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 646 | | nil]; |
| 647 | | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 648 | | } |
| 649 | | if (nil != errDesc) [errDesc release]; |
| 650 | | return nil; |
| 651 | | } |
| 652 | | return data; |
| 653 | | } |
| 654 | | |
| 655 | | - (IBAction)toggleGenericView:(id)sender { |
| 656 | | forceGenericView = (NSOnState == [sender state]); |
| 657 | | if (nil != view) |
| 658 | | { |
| 659 | | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 660 | | name:NSViewFrameDidChangeNotification |
| 661 | | object:nil]; |
| 662 | | [view removeFromSuperview]; |
| 663 | | view = nil; |
| 664 | | } |
| 665 | | if (0 != effectNode) |
| 666 | | [self loadEffectUI]; |
| 667 | | } |
| 668 | | |
| 669 | | - (IBAction)loadPreset:(id)sender { |
| 670 | | OSStatus status; |
| 671 | | |
| 672 | | CFIndex const idx = [sender tag]; |
| 673 | | CFIndex const total = (NULL == presets) ? 0 : CFArrayGetCount(presets); |
| 674 | | if ((0 > idx) || (total <= idx)) |
| 675 | | { |
| 676 | | NSAlert const *alert = [[NSAlert alloc] init]; |
| 677 | | [alert setMessageText:@"Invalid preset selected"]; |
| 678 | | [alert setInformativeText:[NSString stringWithFormat:@"Tried to select preset %ld of %ld", |
| 679 | | (long)idx + 1, |
| 680 | | (long)total]]; |
| 681 | | [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; |
| 682 | | return; |
| 683 | | } |
| 684 | | |
| 685 | | AUPreset const *preset = (AUPreset const *)CFArrayGetValueAtIndex(presets, idx); |
| 686 | | status = AudioUnitSetProperty(effectUnit, |
| 687 | | kAudioUnitProperty_PresentPreset, |
| 688 | | kAudioUnitScope_Global, |
| 689 | | 0, |
| 690 | | preset, |
| 691 | | sizeof(AUPreset)); |
| 692 | | if (noErr != status) |
| 693 | | { |
| 694 | | NSAlert const *alert = [[NSAlert alloc] init]; |
| 695 | | [alert setMessageText:[NSString stringWithFormat:@"Error loading preset %@", preset->presetName]]; |
| 696 | | [alert setInformativeText:[NSString stringWithFormat:@"Error %ld encountered while setting AudioUnit property", |
| 697 | | (long)status]]; |
| 698 | | [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; |
| 699 | | return; |
| 700 | | } |
| 701 | | |
| 702 | | AudioUnitParameter change = { effectUnit, kAUParameterListener_AnyParameter, 0, 0 }; |
| 703 | | status = AUParameterListenerNotify(NULL, NULL, &change); |
| 704 | | if (noErr != status) |
| 705 | | { |
| 706 | | NSAlert const *alert = [[NSAlert alloc] init]; |
| 707 | | [alert setMessageText:[NSString stringWithFormat:@"Error notifying of parameter changes for preset %@", |
| 708 | | preset->presetName]]; |
| 709 | | [alert setInformativeText:[NSString stringWithFormat:@"Error %ld encountered while sending notification", |
| 710 | | (long)status]]; |
| 711 | | [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; |
| 712 | | return; |
| 713 | | } |
| 714 | | } |
| 715 | | |
| 716 | | - (void)viewFrameDidChange:(NSNotification *)notification { |
| 717 | | NSRect const oldFrame = [window frame]; |
| 718 | | NSRect const desired = [window frameRectForContentRect:[[notification object] frame]]; |
| 719 | | NSRect const newFrame = NSMakeRect(oldFrame.origin.x, |
| 720 | | oldFrame.origin.y + oldFrame.size.height - headerSize.height- desired.size.height, |
| 721 | | desired.size.width, |
| 722 | | headerSize.height + desired.size.height); |
| 723 | | [window setFrame:newFrame display:YES animate:NO]; |
| 724 | | } |
| 725 | | |
| 726 | | @end |
| 727 | | |
| 728 | | |
| 729 | | @interface AUEffectUtilAppDelegate : NSObject <NSApplicationDelegate> |
| 730 | | { |
| 731 | | EffectInfo *effects; |
| 732 | | |
| 733 | | IBOutlet NSMenu *newEffectMenu; |
| 734 | | } |
| 735 | | |
| 736 | | - (id)init; |
| 737 | | - (void)dealloc; |
| 738 | | |
| 739 | | - (IBAction)newEffect:(id)sender; |
| 740 | | |
| 741 | | - (void)applicationWillFinishLaunching:(NSNotification *)notification; |
| 742 | | - (void)applicationDidFinishLaunching:(NSNotification *)notification; |
| 743 | | - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender; |
| 744 | | |
| 745 | | @end |
| 746 | | |
| 747 | | @implementation AUEffectUtilAppDelegate |
| 748 | | |
| 749 | | - (void)appendApplicationMenu:(NSMenu *)parent { |
| 750 | | NSMenuItem *item; |
| 751 | | NSMenu *submenu; |
| 752 | | NSString *const appName = [(NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()) objectForKey:@"CFBundleName"]; |
| 753 | | |
| 754 | | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Application"]; |
| 755 | | item = [parent addItemWithTitle:@"Application" action:NULL keyEquivalent:@""]; |
| 756 | | [parent setSubmenu:menu forItem:item]; |
| 757 | | [menu release]; |
| 758 | | [menu setValue:@"NSAppleMenu" forKey:@"name"]; |
| 759 | | |
| 760 | | item = [menu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; |
| 761 | | [item setTarget:NSApp]; |
| 762 | | |
| 763 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 764 | | |
| 765 | | item = [menu addItemWithTitle:@"Services" action:NULL keyEquivalent:@""]; |
| 766 | | submenu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Services"]; |
| 767 | | [menu setSubmenu:submenu forItem:item]; |
| 768 | | [submenu release]; |
| 769 | | [NSApp setServicesMenu:submenu]; |
| 770 | | |
| 771 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 772 | | |
| 773 | | item = [menu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] action:@selector(hide:) keyEquivalent:@"h"]; |
| 774 | | item = [menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; |
| 775 | | [item setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask]; |
| 776 | | item = [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; |
| 777 | | |
| 778 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 779 | | |
| 780 | | item = [menu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] action:@selector(terminate:) keyEquivalent:@"q"]; |
| 781 | | [item setTarget:NSApp]; |
| 782 | | } |
| 783 | | |
| 784 | | - (void)appendFileMenu:(NSMenu *)parent { |
| 785 | | NSMenuItem *item; |
| 786 | | |
| 787 | | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"File"]; |
| 788 | | item = [parent addItemWithTitle:@"File" action:NULL keyEquivalent:@""]; |
| 789 | | [parent setSubmenu:menu forItem:item]; |
| 790 | | [menu release]; |
| 791 | | |
| 792 | | item = [menu addItemWithTitle:@"New" action:NULL keyEquivalent:@""]; |
| 793 | | newEffectMenu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"New"]; |
| 794 | | [menu setSubmenu:newEffectMenu forItem:item]; |
| 795 | | [newEffectMenu release]; |
| 796 | | item = [menu addItemWithTitle:[NSString stringWithFormat:@"Open%C", (unichar)0x2026] action:@selector(openDocument:) keyEquivalent:@"o"]; |
| 797 | | |
| 798 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 799 | | |
| 800 | | item = [menu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"]; |
| 801 | | item = [menu addItemWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]; |
| 802 | | item = [menu addItemWithTitle:[NSString stringWithFormat:@"Save As%C", (unichar)0x2026] action:@selector(saveDocumentAs:) keyEquivalent:@"S"]; |
| 803 | | item = [menu addItemWithTitle:@"Save All" action:@selector(saveAllDocuments:) keyEquivalent:@""]; |
| 804 | | item = [menu addItemWithTitle:@"Revert to Saved" action:@selector(revertDocumentToSaved:) keyEquivalent:@"u"]; |
| 805 | | } |
| 806 | | |
| 807 | | - (void)appendEditMenu:(NSMenu *)parent { |
| 808 | | NSMenuItem *item; |
| 809 | | |
| 810 | | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Edit"]; |
| 811 | | item = [parent addItemWithTitle:@"Edit" action:NULL keyEquivalent:@""]; |
| 812 | | [parent setSubmenu:menu forItem:item]; |
| 813 | | [menu release]; |
| 814 | | |
| 815 | | item = [menu addItemWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"]; |
| 816 | | item = [menu addItemWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"]; |
| 817 | | |
| 818 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 819 | | |
| 820 | | item = [menu addItemWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]; |
| 821 | | item = [menu addItemWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]; |
| 822 | | item = [menu addItemWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]; |
| 823 | | item = [menu addItemWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""]; |
| 824 | | item = [menu addItemWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"]; |
| 825 | | } |
| 826 | | |
| 827 | | - (void)appendWindowMenu:(NSMenu *)parent { |
| 828 | | NSMenuItem *item; |
| 829 | | |
| 830 | | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Window"]; |
| 831 | | item = [parent addItemWithTitle:@"Window" action:NULL keyEquivalent:@""]; |
| 832 | | [parent setSubmenu:menu forItem:item]; |
| 833 | | [menu release]; |
| 834 | | [NSApp setWindowsMenu:menu]; |
| 835 | | |
| 836 | | item = [menu addItemWithTitle:@"Minimize" action:@selector(performMinimize:) keyEquivalent:@"m"]; |
| 837 | | item = [menu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; |
| 838 | | |
| 839 | | [menu addItem:[NSMenuItem separatorItem]]; |
| 840 | | |
| 841 | | item = [menu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""]; |
| 842 | | } |
| 843 | | |
| 844 | | - (void)appendHelpMenu:(NSMenu *)parent { |
| 845 | | NSMenuItem *item; |
| 846 | | NSString *const appName = [(NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()) objectForKey:@"CFBundleName"]; |
| 847 | | |
| 848 | | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Help"]; |
| 849 | | item = [parent addItemWithTitle:@"Help" action:NULL keyEquivalent:@""]; |
| 850 | | [parent setSubmenu:menu forItem:item]; |
| 851 | | [menu release]; |
| 852 | | [menu setValue:@"NSHelpMenu" forKey:@"name"]; |
| 853 | | if ([NSApp respondsToSelector:@selector(setHelpMenu:)]) |
| 854 | | [NSApp performSelector:@selector(setHelpMenu:) withObject:menu]; |
| 855 | | |
| 856 | | item = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ Help", appName] action:@selector(showHelp:) keyEquivalent:@"?"]; |
| 857 | | } |
| 858 | | |
| 859 | | - (id)init { |
| 860 | | if (!(self = [super init])) return nil; |
| 861 | | effects = NULL; |
| 862 | | return self; |
| 863 | | } |
| 864 | | |
| 865 | | - (void)dealloc { |
| 866 | | if (effects) free(effects); |
| 867 | | [super dealloc]; |
| 868 | | } |
| 869 | | |
| 870 | | - (IBAction)newEffect:(id)sender { |
| 871 | | int const index = [sender tag]; |
| 872 | | if ((0 > index) || (0 == effects[index].component)) |
| 873 | | { |
| 874 | | NSAlert *const alert = [[NSAlert alloc] init]; |
| 875 | | [alert setAlertStyle:NSWarningAlertStyle]; |
| 876 | | [alert setMessageText:@"Invalid effect component"]; |
| 877 | | [alert addButtonWithTitle:@"OK"]; |
| 878 | | [alert runModal]; |
| 879 | | [alert release]; |
| 880 | | return; |
| 881 | | } |
| 882 | | |
| 883 | | NSNumber *const typeValue = [NSNumber numberWithUnsignedLong:effects[index].type]; |
| 884 | | NSNumber *const subtypeValue = [NSNumber numberWithUnsignedLong:effects[index].subtype]; |
| 885 | | NSNumber *const manufacturerValue = [NSNumber numberWithUnsignedLong:effects[index].manufacturer]; |
| 886 | | NSDictionary *const desc = [NSDictionary dictionaryWithObjectsAndKeys:typeValue, ComponentTypeKey, |
| 887 | | subtypeValue, ComponentSubTypeKey, |
| 888 | | manufacturerValue, ComponentManufacturerKey, |
| 889 | | nil]; |
| 890 | | NSString *errDesc = nil; |
| 891 | | NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc |
| 892 | | format:NSPropertyListXMLFormat_v1_0 |
| 893 | | errorDescription:&errDesc]; |
| 894 | | if ((nil == data) || (nil != errDesc)) |
| 895 | | { |
| 896 | | NSAlert *const alert = [[NSAlert alloc] init]; |
| 897 | | [alert setAlertStyle:NSWarningAlertStyle]; |
| 898 | | [alert setMessageText:@"Error serialising properties for new effect"]; |
| 899 | | if (nil != errDesc) [alert setInformativeText:[errDesc autorelease]]; |
| 900 | | [alert addButtonWithTitle:@"OK"]; |
| 901 | | [alert runModal]; |
| 902 | | [alert release]; |
| 903 | | return; |
| 904 | | } |
| 905 | | |
| 906 | | NSError *err = nil; |
| 907 | | AUEffectDocument *const document = [[AUEffectDocument alloc] init]; |
| 908 | | if ((nil == document) || ![document readFromData:data ofType:AUEffectDocumentType error:&err]) |
| 909 | | { |
| 910 | | [document release]; |
| 911 | | if (nil != err) |
| 912 | | { |
| 913 | | [[NSAlert alertWithError:err] runModal]; |
| 914 | | } |
| 915 | | else |
| 916 | | { |
| 917 | | NSAlert *const alert = [[NSAlert alloc] init]; |
| 918 | | [alert setAlertStyle:NSWarningAlertStyle]; |
| 919 | | [alert setMessageText:@"Error creating new effect document"]; |
| 920 | | [alert addButtonWithTitle:@"OK"]; |
| 921 | | [alert runModal]; |
| 922 | | [alert release]; |
| 923 | | } |
| 924 | | return; |
| 925 | | } |
| 926 | | |
| 927 | | [document makeWindowControllers]; |
| 928 | | [document showWindows]; |
| 929 | | [[NSDocumentController sharedDocumentController] addDocument:document]; |
| 930 | | [document release]; |
| 931 | | } |
| 932 | | |
| 933 | | - (void)applicationWillFinishLaunching:(NSNotification *)notification { |
| 934 | | NSMenu *const menubar = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"MainMenu"]; |
| 935 | | [NSApp setMainMenu:menubar]; |
| 936 | | [menubar release]; |
| 937 | | [self appendApplicationMenu:menubar]; |
| 938 | | [self appendFileMenu:menubar]; |
| 939 | | [self appendEditMenu:menubar]; |
| 940 | | [self appendWindowMenu:menubar]; |
| 941 | | [self appendHelpMenu:menubar]; |
| 942 | | |
| 943 | | ProcessSerialNumber const serial = { 0, kCurrentProcess }; |
| 944 | | OSStatus const status = TransformProcessType(&serial, kProcessTransformToForegroundApplication); |
| 945 | | if (noErr != status) |
| 946 | | { |
| 947 | | NSLog(@"Error transforming to foreground application (%ld)", (long)status); |
| 948 | | [NSApp terminate:self]; |
| 949 | | } |
| 950 | | else |
| 951 | | { |
| 952 | | [NSApp activateIgnoringOtherApps:YES]; |
| 953 | | } |
| 954 | | } |
| 955 | | |
| 956 | | - (void)applicationDidFinishLaunching:(NSNotification *)notification { |
| 957 | | ComponentDescription effectFilter = { kAudioUnitType_Effect, 0, 0, 0, 0 }; |
| 958 | | long const count = CountComponents(&effectFilter); |
| 959 | | if (0 == count) |
| 960 | | { |
| 961 | | NSAlert *const alert = [[NSAlert alloc] init]; |
| 962 | | [alert setAlertStyle:NSWarningAlertStyle]; |
| 963 | | [alert setMessageText:@"No AudioUnit effects found"]; |
| 964 | | [alert addButtonWithTitle:@"OK"]; |
| 965 | | [alert runModal]; |
| 966 | | [alert release]; |
| 967 | | } |
| 968 | | |
| 969 | | std::vector<std::pair<Component, OSStatus> > failed; |
| 970 | | effects = (EffectInfo *)malloc(count * sizeof(*effects)); |
| 971 | | Component effect = FindNextComponent(0, &effectFilter); |
| 972 | | for (long i = 0; (i < count) && (effect != 0); i++, effect = FindNextComponent(effect, &effectFilter)) |
| 973 | | { |
| 974 | | ComponentDescription effectDesc; |
| 975 | | Handle const nameHandle = NewHandle(4); |
| 976 | | OSStatus const err = GetComponentInfo(effect, &effectDesc, nameHandle, NULL, NULL); |
| 977 | | if (noErr == err) |
| 978 | | { |
| 979 | | effects[i].component = effect; |
| 980 | | effects[i].type = effectDesc.componentType; |
| 981 | | effects[i].subtype = effectDesc.componentSubType; |
| 982 | | effects[i].manufacturer = effectDesc.componentManufacturer; |
| 983 | | HLock(nameHandle); |
| 984 | | CFStringRef name = CFStringCreateWithPascalString(NULL, |
| 985 | | (unsigned char const *)*nameHandle, |
| 986 | | kCFStringEncodingMacRoman); |
| 987 | | HUnlock(nameHandle); |
| 988 | | NSMenuItem *const item = [newEffectMenu addItemWithTitle:(NSString *)name |
| 989 | | action:@selector(newEffect:) |
| 990 | | keyEquivalent:@""]; |
| 991 | | [item setTag:i]; |
| 992 | | [item setTarget:self]; |
| 993 | | CFRelease(name); |
| 994 | | } |
| 995 | | else |
| 996 | | { |
| 997 | | effects[i].component = 0; |
| 998 | | failed.push_back(std::make_pair(effect, err)); |
| 999 | | } |
| 1000 | | DisposeHandle(nameHandle); |
| 1001 | | } |
| 1002 | | |
| 1003 | | if (!failed.empty()) |
| 1004 | | { |
| 1005 | | NSString *const message = [NSString stringWithFormat:@"Failed to get info for %lu effect%s", |
| 1006 | | (unsigned long)failed.size(), |
| 1007 | | ((1U == failed.size()) ? "" : "s")]; |
| 1008 | | NSMutableString *const detail = [NSMutableString stringWithCapacity:(16 * failed.size())]; |
| 1009 | | std::vector<std::pair<Component, OSStatus> >::const_iterator it = failed.begin(); |
| 1010 | | [detail appendFormat:@"%lu (%ld)", (unsigned long)it->first, (long)it->second]; |
| 1011 | | ++it; |
| 1012 | | while (failed.end() != it) |
| 1013 | | { |
| 1014 | | [detail appendFormat:@", %lu (%ld)", (unsigned long)it->first, (long)it->second]; |
| 1015 | | ++it; |
| 1016 | | } |
| 1017 | | NSAlert *const alert = [[NSAlert alloc] init]; |
| 1018 | | [alert setAlertStyle:NSWarningAlertStyle]; |
| 1019 | | [alert setMessageText:message]; |
| 1020 | | [alert setInformativeText:[NSString stringWithString:detail]]; |
| 1021 | | [alert addButtonWithTitle:@"OK"]; |
| 1022 | | [alert runModal]; |
| 1023 | | [alert release]; |
| 1024 | | } |
| 1025 | | } |
| 1026 | | |
| 1027 | | - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender { |
| 1028 | | return NO; |
| 1029 | | } |
| 1030 | | |
| 1031 | | @end |
| 1032 | | |
| 1033 | | |
| 1034 | | int main(int argc, char *argv[]) |
| 1035 | | { |
| 1036 | | NSAutoreleasePool *pool; |
| 1037 | | |
| 1038 | | // Initialise NSApplication |
| 1039 | | pool = [[NSAutoreleasePool alloc] init]; |
| 1040 | | [NSApplication sharedApplication]; |
| 1041 | | AUEffectUtilAppDelegate *const delegate = [[AUEffectUtilAppDelegate alloc] init]; |
| 1042 | | [[NSApplication sharedApplication] setDelegate:delegate]; |
| 1043 | | [pool release]; |
| 1044 | | |
| 1045 | | // Let's go! |
| 1046 | | pool = [[NSAutoreleasePool alloc] init]; |
| 1047 | | [NSApp run]; |
| 1048 | | [delegate release]; |
| 1049 | | [pool release]; |
| 1050 | | return 0; |
| 1051 | | } |
trunk/src/osd/sdl/aueffectutil.mm
| r0 | r250165 | |
| 1 | #import <AvailabilityMacros.h> |
| 2 | #import <AudioUnit/AudioUnit.h> |
| 3 | #import <AudioUnit/AUCocoaUIView.h> |
| 4 | #import <AudioToolbox/AudioToolbox.h> |
| 5 | #import <Cocoa/Cocoa.h> |
| 6 | #import <CoreAudio/CoreAudio.h> |
| 7 | #import <CoreAudioKit/CoreAudioKit.h> |
| 8 | #import <CoreFoundation/CoreFoundation.h> |
| 9 | #import <CoreServices/CoreServices.h> |
| 10 | |
| 11 | #include <utility> |
| 12 | #include <vector> |
| 13 | |
| 14 | #include <stdlib.h> |
| 15 | |
| 16 | |
| 17 | #ifdef MAC_OS_X_VERSION_MAX_ALLOWED |
| 18 | |
| 19 | #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 |
| 20 | |
| 21 | typedef ComponentDescription AudioComponentDescription; |
| 22 | |
| 23 | @protocol NSApplicationDelegate <NSObject> |
| 24 | @end |
| 25 | |
| 26 | @protocol NSWindowDelegate <NSObject> |
| 27 | @end |
| 28 | |
| 29 | #endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1060 |
| 30 | |
| 31 | #endif // MAC_OS_X_VERSION_MAX_ALLOWED |
| 32 | |
| 33 | |
| 34 | struct EffectInfo |
| 35 | { |
| 36 | Component component; |
| 37 | OSType type; |
| 38 | OSType subtype; |
| 39 | OSType manufacturer; |
| 40 | }; |
| 41 | |
| 42 | |
| 43 | static NSString *const AUEffectUtilErrorDomain = @"AUEffectUtilErrorDomain"; |
| 44 | |
| 45 | static NSString *const AUEffectDocumentType = @"AUEffect"; |
| 46 | static NSString *const AUPresetDocumentType = @"AudioUnit Preset"; |
| 47 | |
| 48 | static NSString *const ComponentTypeKey = @"ComponentType"; |
| 49 | static NSString *const ComponentSubTypeKey = @"ComponentSubType"; |
| 50 | static NSString *const ComponentManufacturerKey = @"ComponentManufacturer"; |
| 51 | static NSString *const ClassInfoKey = @"ClassInfo"; |
| 52 | static NSString *const ForceGenericViewKey = @"ForceGenericView"; |
| 53 | static NSString *const WindowFrameKey = @"WindowFrame"; |
| 54 | |
| 55 | |
| 56 | static void UpdateChangeCountCallback(void *userData, |
| 57 | void *object, |
| 58 | AudioUnitEvent const *inEvent, |
| 59 | UInt64 inEventHostTime, |
| 60 | AudioUnitParameterValue inParameterValue) |
| 61 | { |
| 62 | [(NSDocument *)userData updateChangeCount:NSChangeDone]; |
| 63 | } |
| 64 | |
| 65 | |
| 66 | @interface AUEffectDocument : NSDocument <NSWindowDelegate> |
| 67 | { |
| 68 | IBOutlet NSWindow *window; |
| 69 | IBOutlet NSButton *genericViewButton; |
| 70 | IBOutlet NSPopUpButton *presetButton; |
| 71 | NSView *view; |
| 72 | NSSize headerSize; |
| 73 | CFArrayRef presets; |
| 74 | AUParameterListenerRef listener; |
| 75 | BOOL forceGenericView; |
| 76 | NSString *restoreFrame; |
| 77 | |
| 78 | AudioComponentDescription description; |
| 79 | AUGraph graph; |
| 80 | AUNode outputNode, sourceNode, effectNode; |
| 81 | AudioUnit outputUnit, sourceUnit, effectUnit; |
| 82 | } |
| 83 | |
| 84 | - (void)dealloc; |
| 85 | |
| 86 | - (void)makeWindowControllers; |
| 87 | - (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error; |
| 88 | - (NSData *)dataOfType:(NSString *)type error:(NSError **)error; |
| 89 | |
| 90 | - (IBAction)toggleGenericView:(id)sender; |
| 91 | - (IBAction)loadPreset:(id)sender; |
| 92 | |
| 93 | - (void)viewFrameDidChange:(NSNotification *)notification; |
| 94 | |
| 95 | @end |
| 96 | |
| 97 | @implementation AUEffectDocument |
| 98 | |
| 99 | - (void)loadEffectUI { |
| 100 | if ((0 == effectNode) || (nil == window)) |
| 101 | return; |
| 102 | |
| 103 | BOOL customViewValid = NO; |
| 104 | OSStatus status; |
| 105 | UInt32 uiDescSize; |
| 106 | AudioUnitCocoaViewInfo *viewInfo; |
| 107 | status = AudioUnitGetPropertyInfo(effectUnit, |
| 108 | kAudioUnitProperty_CocoaUI, |
| 109 | kAudioUnitScope_Global, |
| 110 | 0, |
| 111 | &uiDescSize, |
| 112 | NULL); |
| 113 | UInt32 const uiClassCount = 1 + ((uiDescSize - sizeof(*viewInfo)) / sizeof(viewInfo->mCocoaAUViewClass[0])); |
| 114 | if ((noErr == status) && (0 < uiClassCount)) |
| 115 | { |
| 116 | viewInfo = (AudioUnitCocoaViewInfo *)malloc(uiDescSize); |
| 117 | status = AudioUnitGetProperty(effectUnit, |
| 118 | kAudioUnitProperty_CocoaUI, |
| 119 | kAudioUnitScope_Global, |
| 120 | 0, |
| 121 | viewInfo, |
| 122 | &uiDescSize); |
| 123 | if (noErr == status) |
| 124 | { |
| 125 | NSBundle *const bundle = [NSBundle bundleWithPath:[(NSURL *)viewInfo->mCocoaAUViewBundleLocation path]]; |
| 126 | Class const viewClass = [bundle classNamed:(NSString *)viewInfo->mCocoaAUViewClass[0]]; |
| 127 | if ((NULL != viewClass) |
| 128 | && [viewClass conformsToProtocol:@protocol(AUCocoaUIBase)] |
| 129 | && [viewClass instancesRespondToSelector:@selector(uiViewForAudioUnit:withSize:)]) |
| 130 | { |
| 131 | customViewValid = YES; |
| 132 | if (!forceGenericView) |
| 133 | { |
| 134 | id const factory = [[viewClass alloc] init]; |
| 135 | view = [factory uiViewForAudioUnit:effectUnit |
| 136 | withSize:[[window contentView] bounds].size]; |
| 137 | [factory release]; |
| 138 | } |
| 139 | } |
| 140 | CFRelease(viewInfo->mCocoaAUViewBundleLocation); |
| 141 | for (UInt32 i = 0; i < uiClassCount; i++) |
| 142 | CFRelease(viewInfo->mCocoaAUViewClass[i]); |
| 143 | } |
| 144 | free(viewInfo); |
| 145 | } |
| 146 | if (nil == view) |
| 147 | { |
| 148 | view = [[[AUGenericView alloc] initWithAudioUnit:effectUnit] autorelease]; |
| 149 | [(AUGenericView *)view setShowsExpertParameters:YES]; |
| 150 | } |
| 151 | |
| 152 | [view setAutoresizingMask:NSViewNotSizable]; |
| 153 | [view setFrameOrigin:NSMakePoint(0, 0)]; |
| 154 | NSRect const oldFrame = [window frame]; |
| 155 | NSRect const desired = [window frameRectForContentRect:[view frame]]; |
| 156 | NSRect const newFrame = NSMakeRect(oldFrame.origin.x, |
| 157 | oldFrame.origin.y + oldFrame.size.height - headerSize.height - desired.size.height, |
| 158 | desired.size.width, |
| 159 | headerSize.height + desired.size.height); |
| 160 | [window setFrame:newFrame display:YES animate:NO]; |
| 161 | [[window contentView] addSubview:view]; |
| 162 | [view setPostsFrameChangedNotifications:YES]; |
| 163 | [[NSNotificationCenter defaultCenter] addObserver:self |
| 164 | selector:@selector(viewFrameDidChange:) |
| 165 | name:NSViewFrameDidChangeNotification |
| 166 | object:view]; |
| 167 | |
| 168 | [genericViewButton setEnabled:customViewValid]; |
| 169 | if (!customViewValid) |
| 170 | { |
| 171 | forceGenericView = YES; |
| 172 | [genericViewButton setState:NSOnState]; |
| 173 | } |
| 174 | |
| 175 | CFIndex const presetCount = (NULL != presets) ? CFArrayGetCount(presets) : 0; |
| 176 | [presetButton setEnabled:(0 < presetCount)]; |
| 177 | while (1 < [presetButton numberOfItems]) |
| 178 | [presetButton removeItemAtIndex:1]; |
| 179 | for (CFIndex i = 0; i < presetCount; i++) |
| 180 | { |
| 181 | AUPreset const *preset = (AUPreset const*)CFArrayGetValueAtIndex(presets, i); |
| 182 | NSMenuItem const *item = [[presetButton menu] addItemWithTitle:(NSString *)preset->presetName |
| 183 | action:@selector(loadPreset:) |
| 184 | keyEquivalent:@""]; |
| 185 | [item setTarget:self]; |
| 186 | [item setTag:i]; |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | - (id)init { |
| 191 | if (!(self = [super init])) return nil; |
| 192 | |
| 193 | window = nil; |
| 194 | genericViewButton = nil; |
| 195 | presetButton = nil; |
| 196 | view = nil; |
| 197 | presets = NULL; |
| 198 | listener = NULL; |
| 199 | forceGenericView = NO; |
| 200 | restoreFrame = nil; |
| 201 | |
| 202 | description.componentType = description.componentSubType = description.componentManufacturer = 0; |
| 203 | description.componentFlags = description.componentFlagsMask = 0; |
| 204 | graph = NULL; |
| 205 | outputNode = sourceNode = effectNode = 0; |
| 206 | |
| 207 | AudioComponentDescription const outputDesc = { kAudioUnitType_Output, |
| 208 | kAudioUnitSubType_DefaultOutput, |
| 209 | kAudioUnitManufacturer_Apple, |
| 210 | 0, |
| 211 | 0, }; |
| 212 | AudioComponentDescription const sourceDesc = { kAudioUnitType_Generator, |
| 213 | kAudioUnitSubType_AudioFilePlayer, |
| 214 | kAudioUnitManufacturer_Apple, |
| 215 | 0, |
| 216 | 0, }; |
| 217 | if ((noErr != NewAUGraph(&graph)) |
| 218 | || (noErr != AUGraphAddNode(graph, &outputDesc, &outputNode)) |
| 219 | || (noErr != AUGraphAddNode(graph, &sourceDesc, &sourceNode)) |
| 220 | || (noErr != AUGraphOpen(graph)) |
| 221 | || (noErr != AUGraphNodeInfo(graph, outputNode, NULL, &outputUnit)) |
| 222 | || (noErr != AUGraphNodeInfo(graph, sourceNode, NULL, &sourceUnit)) |
| 223 | || (noErr != AUGraphInitialize(graph))) |
| 224 | { |
| 225 | [self release]; |
| 226 | return nil; |
| 227 | } |
| 228 | |
| 229 | return self; |
| 230 | } |
| 231 | |
| 232 | - (void)dealloc { |
| 233 | if (NULL != presets) |
| 234 | CFRelease(presets); |
| 235 | |
| 236 | if (NULL != listener) |
| 237 | AUListenerDispose(listener); |
| 238 | |
| 239 | if (nil != restoreFrame) |
| 240 | [restoreFrame release]; |
| 241 | |
| 242 | if (NULL != graph) |
| 243 | { |
| 244 | AUGraphClose(graph); |
| 245 | DisposeAUGraph(graph); |
| 246 | } |
| 247 | |
| 248 | [super dealloc]; |
| 249 | } |
| 250 | |
| 251 | - (void)makeWindowControllers { |
| 252 | genericViewButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 18)]; |
| 253 | [genericViewButton setAutoresizingMask:NSViewNotSizable]; |
| 254 | [[genericViewButton cell] setControlSize:NSSmallControlSize]; |
| 255 | [genericViewButton setButtonType:NSSwitchButton]; |
| 256 | [genericViewButton setBordered:NO]; |
| 257 | [genericViewButton setAllowsMixedState:NO]; |
| 258 | [genericViewButton setState:(forceGenericView ? NSOnState : NSOffState)]; |
| 259 | [genericViewButton setTitle:@"Use generic editor view"]; |
| 260 | [genericViewButton setAction:@selector(toggleGenericView:)]; |
| 261 | [genericViewButton setTarget:self]; |
| 262 | [genericViewButton sizeToFit]; |
| 263 | |
| 264 | presetButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 22) pullsDown:YES]; |
| 265 | [presetButton setAutoresizingMask:NSViewNotSizable]; |
| 266 | [[presetButton cell] setControlSize:NSSmallControlSize]; |
| 267 | [[presetButton cell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; |
| 268 | [presetButton setTitle:@"Load preset"]; |
| 269 | [[[presetButton menu] addItemWithTitle:@"Load preset" action:NULL keyEquivalent:@""] setHidden:YES]; |
| 270 | [presetButton sizeToFit]; |
| 271 | |
| 272 | CGFloat const controlWidth = MAX(NSWidth([genericViewButton frame]), NSWidth([presetButton frame])); |
| 273 | NSRect const presetFrame = NSMakeRect(17, |
| 274 | 8, |
| 275 | controlWidth, |
| 276 | NSHeight([presetButton frame])); |
| 277 | NSRect const genericViewFrame = NSMakeRect(17, |
| 278 | NSMaxY(presetFrame) + 9, |
| 279 | controlWidth, |
| 280 | NSHeight([genericViewButton frame])); |
| 281 | [genericViewButton setFrame:genericViewFrame]; |
| 282 | [presetButton setFrame:presetFrame]; |
| 283 | |
| 284 | headerSize = NSMakeSize((2 * 17) + controlWidth, 18 + NSMaxY(genericViewFrame)); |
| 285 | NSView *const container = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, headerSize.width, headerSize.height)]; |
| 286 | [container setAutoresizingMask:(NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin)]; |
| 287 | [container addSubview:genericViewButton]; |
| 288 | [genericViewButton release]; |
| 289 | [container addSubview:presetButton]; |
| 290 | [presetButton release]; |
| 291 | |
| 292 | window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, headerSize.width, headerSize.height) |
| 293 | styleMask:(NSTitledWindowMask | |
| 294 | NSClosableWindowMask | |
| 295 | NSMiniaturizableWindowMask) |
| 296 | backing:NSBackingStoreBuffered |
| 297 | defer:YES]; |
| 298 | [window setReleasedWhenClosed:NO]; |
| 299 | [window setDelegate:self]; |
| 300 | [window setTitle:@"Effect"]; |
| 301 | [[window contentView] addSubview:container]; |
| 302 | [container release]; |
| 303 | [self setWindow:window]; |
| 304 | |
| 305 | NSWindowController *const controller = [[NSWindowController alloc] initWithWindow:window]; |
| 306 | [self addWindowController:controller]; |
| 307 | [controller release]; |
| 308 | [window release]; |
| 309 | |
| 310 | [self loadEffectUI]; |
| 311 | if (nil != restoreFrame) |
| 312 | { |
| 313 | [window setFrameFromString:restoreFrame]; |
| 314 | } |
| 315 | else |
| 316 | { |
| 317 | NSRect const available = [[NSScreen mainScreen] visibleFrame]; |
| 318 | NSRect frame = [window frame]; |
| 319 | frame.origin.x = (NSWidth(available) - NSWidth(frame)) / 4; |
| 320 | frame.origin.y = (NSHeight(available) - NSHeight(frame)) * 3 / 4; |
| 321 | [window setFrame:frame display:YES animate:NO]; |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | - (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error { |
| 326 | OSStatus status; |
| 327 | UInt32 propertySize; |
| 328 | |
| 329 | BOOL const hasWrapper = [type isEqualToString:AUEffectDocumentType]; |
| 330 | if (!hasWrapper && ![type isEqualToString:AUPresetDocumentType]) |
| 331 | { |
| 332 | if (NULL != error) |
| 333 | { |
| 334 | NSString *const message = [NSString stringWithFormat:@"Unsupported document type %@", type]; |
| 335 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 336 | nil]; |
| 337 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 338 | } |
| 339 | return NO; |
| 340 | } |
| 341 | |
| 342 | NSString *errDesc = nil; |
| 343 | id const desc = [NSPropertyListSerialization propertyListFromData:data |
| 344 | mutabilityOption:0 |
| 345 | format:NULL |
| 346 | errorDescription:&errDesc]; |
| 347 | if ((nil == desc) || ![desc isKindOfClass:[NSDictionary class]] || (nil != errDesc)) |
| 348 | { |
| 349 | if (NULL != error) |
| 350 | { |
| 351 | NSString *const message = [NSString stringWithFormat:@"Error in file format (%@)", errDesc]; |
| 352 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 353 | nil]; |
| 354 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 355 | } |
| 356 | if (nil != errDesc) |
| 357 | [errDesc release]; |
| 358 | return NO; |
| 359 | } |
| 360 | |
| 361 | id const typeValue = [desc objectForKey:(hasWrapper ? ComponentTypeKey : (NSString *)CFSTR(kAUPresetTypeKey))]; |
| 362 | id const subtypeValue = [desc objectForKey:(hasWrapper ? ComponentSubTypeKey : (NSString *)CFSTR(kAUPresetSubtypeKey))]; |
| 363 | id const manufacturerValue = [desc objectForKey:(hasWrapper ? ComponentManufacturerKey : (NSString *)CFSTR(kAUPresetManufacturerKey))]; |
| 364 | if ((nil == typeValue) || ![typeValue isKindOfClass:[NSNumber class]] |
| 365 | || (nil == subtypeValue) || ![subtypeValue isKindOfClass:[NSNumber class]] |
| 366 | || (nil == manufacturerValue) || ![manufacturerValue isKindOfClass:[NSNumber class]] |
| 367 | || ([typeValue unsignedLongValue] != kAudioUnitType_Effect)) |
| 368 | { |
| 369 | if (NULL != error) |
| 370 | { |
| 371 | NSString *const message = @"Error in effect description file format"; |
| 372 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 373 | nil]; |
| 374 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 375 | } |
| 376 | return NO; |
| 377 | } |
| 378 | |
| 379 | if (NULL != presets) |
| 380 | { |
| 381 | CFRelease(presets); |
| 382 | presets = NULL; |
| 383 | } |
| 384 | if (NULL != listener) |
| 385 | { |
| 386 | AUListenerDispose(listener); |
| 387 | listener = NULL; |
| 388 | } |
| 389 | if (nil != view) |
| 390 | { |
| 391 | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 392 | name:NSViewFrameDidChangeNotification |
| 393 | object:nil]; |
| 394 | [view removeFromSuperview]; |
| 395 | view = nil; |
| 396 | } |
| 397 | if (0 != effectNode) |
| 398 | { |
| 399 | view = nil; |
| 400 | AUGraphRemoveNode(graph, effectNode); |
| 401 | effectNode = 0; |
| 402 | } |
| 403 | |
| 404 | description.componentType = [typeValue longValue]; |
| 405 | description.componentSubType = [subtypeValue longValue]; |
| 406 | description.componentManufacturer = [manufacturerValue longValue]; |
| 407 | status = noErr; |
| 408 | status = AUGraphClearConnections(graph); |
| 409 | if (noErr == status) status = AUGraphAddNode(graph, &description, &effectNode); |
| 410 | if (noErr == status) status = AUGraphNodeInfo(graph, effectNode, NULL, &effectUnit); |
| 411 | if (noErr == status) status = AUGraphConnectNodeInput(graph, sourceNode, 0, effectNode, 0); |
| 412 | if (noErr == status) status = AUGraphConnectNodeInput(graph, effectNode, 0, outputNode, 0); |
| 413 | if (noErr == status) status = AUGraphUpdate(graph, NULL); |
| 414 | if (noErr != status) |
| 415 | { |
| 416 | if (NULL != error) |
| 417 | { |
| 418 | NSString * const message = @"Error encountered while configuring AudioUnit graph"; |
| 419 | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 420 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 421 | underlying, NSUnderlyingErrorKey, |
| 422 | nil]; |
| 423 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 424 | } |
| 425 | return NO; |
| 426 | } |
| 427 | |
| 428 | CFPropertyListRef const classInfo = (CFPropertyListRef)(hasWrapper ? [desc objectForKey:ClassInfoKey] : desc); |
| 429 | if (NULL != classInfo) |
| 430 | { |
| 431 | AudioUnitParameter change = { effectUnit, kAUParameterListener_AnyParameter, 0, 0 }; |
| 432 | status = AudioUnitSetProperty(effectUnit, |
| 433 | kAudioUnitProperty_ClassInfo, |
| 434 | kAudioUnitScope_Global, |
| 435 | 0, |
| 436 | &classInfo, |
| 437 | sizeof(classInfo)); |
| 438 | if (noErr == status) status = AUParameterListenerNotify(NULL, NULL, &change); |
| 439 | if (noErr != status) |
| 440 | { |
| 441 | if (NULL != error) |
| 442 | { |
| 443 | NSString * const message = @"Error configuring effect"; |
| 444 | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 445 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 446 | underlying, NSUnderlyingErrorKey, |
| 447 | nil]; |
| 448 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 449 | } |
| 450 | return NO; |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | propertySize = 0; |
| 455 | status = AudioUnitGetPropertyInfo( |
| 456 | effectUnit, |
| 457 | kAudioUnitProperty_ParameterList, |
| 458 | kAudioUnitScope_Global, |
| 459 | 0, |
| 460 | &propertySize, |
| 461 | NULL); |
| 462 | if (noErr != status) |
| 463 | { |
| 464 | if (NULL != error) |
| 465 | { |
| 466 | NSString * const message = @"Error getting effect parameters"; |
| 467 | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 468 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 469 | underlying, NSUnderlyingErrorKey, |
| 470 | nil]; |
| 471 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 472 | } |
| 473 | return NO; |
| 474 | } |
| 475 | UInt32 const paramCount = propertySize / sizeof(AudioUnitParameterID); |
| 476 | if (0U < paramCount) |
| 477 | { |
| 478 | status = AUEventListenerCreate(UpdateChangeCountCallback, |
| 479 | self, |
| 480 | CFRunLoopGetCurrent(), |
| 481 | kCFRunLoopDefaultMode, |
| 482 | 0.05, |
| 483 | 0.05, |
| 484 | &listener); |
| 485 | if (noErr != status) |
| 486 | { |
| 487 | if (NULL != error) |
| 488 | { |
| 489 | NSString * const message = @"Error creating AudioUnit event listener"; |
| 490 | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 491 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 492 | underlying, NSUnderlyingErrorKey, |
| 493 | nil]; |
| 494 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 495 | } |
| 496 | return NO; |
| 497 | } |
| 498 | AudioUnitParameterID *const params = (AudioUnitParameterID *)malloc(propertySize); |
| 499 | AudioUnitGetProperty( |
| 500 | effectUnit, |
| 501 | kAudioUnitProperty_ParameterList, |
| 502 | kAudioUnitScope_Global, |
| 503 | 0, |
| 504 | params, |
| 505 | &propertySize); |
| 506 | if (noErr != status) |
| 507 | { |
| 508 | free(params); |
| 509 | if (NULL != error) |
| 510 | { |
| 511 | NSString * const message = @"Error getting effect parameters"; |
| 512 | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 513 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 514 | underlying, NSUnderlyingErrorKey, |
| 515 | nil]; |
| 516 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 517 | } |
| 518 | return NO; |
| 519 | } |
| 520 | for (UInt32 i = 0; (i < paramCount) && (noErr == status); i++) |
| 521 | { |
| 522 | AudioUnitEvent event; |
| 523 | event.mEventType = kAudioUnitEvent_ParameterValueChange; |
| 524 | event.mArgument.mParameter.mAudioUnit = effectUnit; |
| 525 | event.mArgument.mParameter.mParameterID = params[i]; |
| 526 | event.mArgument.mParameter.mScope = kAudioUnitScope_Global; |
| 527 | event.mArgument.mParameter.mElement = 0; |
| 528 | status = AUEventListenerAddEventType(listener, self, &event); |
| 529 | } |
| 530 | free(params); |
| 531 | if (noErr != status) |
| 532 | { |
| 533 | free(params); |
| 534 | if (NULL != error) |
| 535 | { |
| 536 | NSString * const message = @"Error getting effect parameters"; |
| 537 | NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 538 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 539 | underlying, NSUnderlyingErrorKey, |
| 540 | nil]; |
| 541 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 542 | } |
| 543 | return NO; |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | propertySize = sizeof(presets); |
| 548 | status = AudioUnitGetProperty(effectUnit, |
| 549 | kAudioUnitProperty_FactoryPresets, |
| 550 | kAudioUnitScope_Global, |
| 551 | 0, |
| 552 | &presets, |
| 553 | &propertySize); |
| 554 | if ((noErr != status) && (NULL != presets)) |
| 555 | { |
| 556 | CFRelease(presets); |
| 557 | presets = NULL; |
| 558 | } |
| 559 | |
| 560 | if (hasWrapper) |
| 561 | { |
| 562 | if ((nil != [desc objectForKey:ForceGenericViewKey]) |
| 563 | && [[desc objectForKey:ForceGenericViewKey] respondsToSelector:@selector(boolValue)]) |
| 564 | { |
| 565 | forceGenericView = [[desc objectForKey:ForceGenericViewKey] boolValue]; |
| 566 | [genericViewButton setState:(forceGenericView ? NSOnState : NSOffState)]; |
| 567 | } |
| 568 | if ((nil != [desc objectForKey:WindowFrameKey]) |
| 569 | && [[desc objectForKey:WindowFrameKey] isKindOfClass:[NSString class]]) |
| 570 | { |
| 571 | if (nil != restoreFrame) [restoreFrame release]; |
| 572 | restoreFrame = [[NSString alloc] initWithString:[desc objectForKey:WindowFrameKey]]; |
| 573 | } |
| 574 | } |
| 575 | |
| 576 | [self loadEffectUI]; |
| 577 | |
| 578 | return YES; |
| 579 | } |
| 580 | |
| 581 | - (NSData *)dataOfType:(NSString *)type error:(NSError **)error { |
| 582 | CFPropertyListRef classInfo; |
| 583 | UInt32 infoSize = sizeof(classInfo); |
| 584 | OSStatus const status = AudioUnitGetProperty(effectUnit, |
| 585 | kAudioUnitProperty_ClassInfo, |
| 586 | kAudioUnitScope_Global, |
| 587 | 0, |
| 588 | &classInfo, |
| 589 | &infoSize); |
| 590 | if (noErr != status) |
| 591 | { |
| 592 | if (NULL != error) |
| 593 | { |
| 594 | NSString const *message = @"Error getting effect settings"; |
| 595 | NSError const *underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; |
| 596 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 597 | underlying, NSUnderlyingErrorKey, |
| 598 | nil]; |
| 599 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 600 | } |
| 601 | return nil; |
| 602 | } |
| 603 | NSDictionary *desc = nil; |
| 604 | if ([type isEqualToString:AUEffectDocumentType]) |
| 605 | { |
| 606 | NSNumber const *typeVal = [NSNumber numberWithUnsignedLong:description.componentType]; |
| 607 | NSNumber const *subtypeVal = [NSNumber numberWithUnsignedLong:description.componentSubType]; |
| 608 | NSNumber const *manufacturerVal = [NSNumber numberWithUnsignedLong:description.componentManufacturer]; |
| 609 | NSNumber const *forceGenericViewVal = [NSNumber numberWithBool:forceGenericView]; |
| 610 | NSString const *windowFrameVal = [window stringWithSavedFrame]; |
| 611 | desc = [NSDictionary dictionaryWithObjectsAndKeys:typeVal, ComponentTypeKey, |
| 612 | subtypeVal, ComponentSubTypeKey, |
| 613 | manufacturerVal, ComponentManufacturerKey, |
| 614 | classInfo, ClassInfoKey, |
| 615 | forceGenericViewVal, ForceGenericViewKey, |
| 616 | windowFrameVal, WindowFrameKey, |
| 617 | nil]; |
| 618 | } |
| 619 | else if ([type isEqualToString:AUPresetDocumentType]) |
| 620 | { |
| 621 | desc = [NSDictionary dictionaryWithDictionary:(NSDictionary *)classInfo]; |
| 622 | } |
| 623 | CFRelease(classInfo); |
| 624 | if (nil == desc) |
| 625 | { |
| 626 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:@"Unsupported document type", NSLocalizedDescriptionKey, |
| 627 | nil]; |
| 628 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 629 | return nil; |
| 630 | } |
| 631 | |
| 632 | NSString *errDesc = nil; |
| 633 | NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc |
| 634 | format:NSPropertyListXMLFormat_v1_0 |
| 635 | errorDescription:&errDesc]; |
| 636 | if ((nil == data) || (nil != errDesc)) |
| 637 | { |
| 638 | if (NULL != error) |
| 639 | { |
| 640 | NSString *message; |
| 641 | if (nil != errDesc) |
| 642 | message = [NSString stringWithFormat:@"Error serialising effect settings: %@", errDesc]; |
| 643 | else |
| 644 | message = @"Error serialising effect settings"; |
| 645 | NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, |
| 646 | nil]; |
| 647 | *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; |
| 648 | } |
| 649 | if (nil != errDesc) [errDesc release]; |
| 650 | return nil; |
| 651 | } |
| 652 | return data; |
| 653 | } |
| 654 | |
| 655 | - (IBAction)toggleGenericView:(id)sender { |
| 656 | forceGenericView = (NSOnState == [sender state]); |
| 657 | if (nil != view) |
| 658 | { |
| 659 | [[NSNotificationCenter defaultCenter] removeObserver:self |
| 660 | name:NSViewFrameDidChangeNotification |
| 661 | object:nil]; |
| 662 | [view removeFromSuperview]; |
| 663 | view = nil; |
| 664 | } |
| 665 | if (0 != effectNode) |
| 666 | [self loadEffectUI]; |
| 667 | } |
| 668 | |
| 669 | - (IBAction)loadPreset:(id)sender { |
| 670 | OSStatus status; |
| 671 | |
| 672 | CFIndex const idx = [sender tag]; |
| 673 | CFIndex const total = (NULL == presets) ? 0 : CFArrayGetCount(presets); |
| 674 | if ((0 > idx) || (total <= idx)) |
| 675 | { |
| 676 | NSAlert const *alert = [[NSAlert alloc] init]; |
| 677 | [alert setMessageText:@"Invalid preset selected"]; |
| 678 | [alert setInformativeText:[NSString stringWithFormat:@"Tried to select preset %ld of %ld", |
| 679 | (long)idx + 1, |
| 680 | (long)total]]; |
| 681 | [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; |
| 682 | return; |
| 683 | } |
| 684 | |
| 685 | AUPreset const *preset = (AUPreset const *)CFArrayGetValueAtIndex(presets, idx); |
| 686 | status = AudioUnitSetProperty(effectUnit, |
| 687 | kAudioUnitProperty_PresentPreset, |
| 688 | kAudioUnitScope_Global, |
| 689 | 0, |
| 690 | preset, |
| 691 | sizeof(AUPreset)); |
| 692 | if (noErr != status) |
| 693 | { |
| 694 | NSAlert const *alert = [[NSAlert alloc] init]; |
| 695 | [alert setMessageText:[NSString stringWithFormat:@"Error loading preset %@", preset->presetName]]; |
| 696 | [alert setInformativeText:[NSString stringWithFormat:@"Error %ld encountered while setting AudioUnit property", |
| 697 | (long)status]]; |
| 698 | [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; |
| 699 | return; |
| 700 | } |
| 701 | |
| 702 | AudioUnitParameter change = { effectUnit, kAUParameterListener_AnyParameter, 0, 0 }; |
| 703 | status = AUParameterListenerNotify(NULL, NULL, &change); |
| 704 | if (noErr != status) |
| 705 | { |
| 706 | NSAlert const *alert = [[NSAlert alloc] init]; |
| 707 | [alert setMessageText:[NSString stringWithFormat:@"Error notifying of parameter changes for preset %@", |
| 708 | preset->presetName]]; |
| 709 | [alert setInformativeText:[NSString stringWithFormat:@"Error %ld encountered while sending notification", |
| 710 | (long)status]]; |
| 711 | [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; |
| 712 | return; |
| 713 | } |
| 714 | } |
| 715 | |
| 716 | - (void)viewFrameDidChange:(NSNotification *)notification { |
| 717 | NSRect const oldFrame = [window frame]; |
| 718 | NSRect const desired = [window frameRectForContentRect:[[notification object] frame]]; |
| 719 | NSRect const newFrame = NSMakeRect(oldFrame.origin.x, |
| 720 | oldFrame.origin.y + oldFrame.size.height - headerSize.height- desired.size.height, |
| 721 | desired.size.width, |
| 722 | headerSize.height + desired.size.height); |
| 723 | [window setFrame:newFrame display:YES animate:NO]; |
| 724 | } |
| 725 | |
| 726 | @end |
| 727 | |
| 728 | |
| 729 | @interface AUEffectUtilAppDelegate : NSObject <NSApplicationDelegate> |
| 730 | { |
| 731 | EffectInfo *effects; |
| 732 | |
| 733 | IBOutlet NSMenu *newEffectMenu; |
| 734 | } |
| 735 | |
| 736 | - (id)init; |
| 737 | - (void)dealloc; |
| 738 | |
| 739 | - (IBAction)newEffect:(id)sender; |
| 740 | |
| 741 | - (void)applicationWillFinishLaunching:(NSNotification *)notification; |
| 742 | - (void)applicationDidFinishLaunching:(NSNotification *)notification; |
| 743 | - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender; |
| 744 | |
| 745 | @end |
| 746 | |
| 747 | @implementation AUEffectUtilAppDelegate |
| 748 | |
| 749 | - (void)appendApplicationMenu:(NSMenu *)parent { |
| 750 | NSMenuItem *item; |
| 751 | NSMenu *submenu; |
| 752 | NSString *const appName = [(NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()) objectForKey:@"CFBundleName"]; |
| 753 | |
| 754 | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Application"]; |
| 755 | item = [parent addItemWithTitle:@"Application" action:NULL keyEquivalent:@""]; |
| 756 | [parent setSubmenu:menu forItem:item]; |
| 757 | [menu release]; |
| 758 | [menu setValue:@"NSAppleMenu" forKey:@"name"]; |
| 759 | |
| 760 | item = [menu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; |
| 761 | [item setTarget:NSApp]; |
| 762 | |
| 763 | [menu addItem:[NSMenuItem separatorItem]]; |
| 764 | |
| 765 | item = [menu addItemWithTitle:@"Services" action:NULL keyEquivalent:@""]; |
| 766 | submenu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Services"]; |
| 767 | [menu setSubmenu:submenu forItem:item]; |
| 768 | [submenu release]; |
| 769 | [NSApp setServicesMenu:submenu]; |
| 770 | |
| 771 | [menu addItem:[NSMenuItem separatorItem]]; |
| 772 | |
| 773 | item = [menu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] action:@selector(hide:) keyEquivalent:@"h"]; |
| 774 | item = [menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; |
| 775 | [item setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask]; |
| 776 | item = [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; |
| 777 | |
| 778 | [menu addItem:[NSMenuItem separatorItem]]; |
| 779 | |
| 780 | item = [menu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] action:@selector(terminate:) keyEquivalent:@"q"]; |
| 781 | [item setTarget:NSApp]; |
| 782 | } |
| 783 | |
| 784 | - (void)appendFileMenu:(NSMenu *)parent { |
| 785 | NSMenuItem *item; |
| 786 | |
| 787 | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"File"]; |
| 788 | item = [parent addItemWithTitle:@"File" action:NULL keyEquivalent:@""]; |
| 789 | [parent setSubmenu:menu forItem:item]; |
| 790 | [menu release]; |
| 791 | |
| 792 | item = [menu addItemWithTitle:@"New" action:NULL keyEquivalent:@""]; |
| 793 | newEffectMenu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"New"]; |
| 794 | [menu setSubmenu:newEffectMenu forItem:item]; |
| 795 | [newEffectMenu release]; |
| 796 | item = [menu addItemWithTitle:[NSString stringWithFormat:@"Open%C", (unichar)0x2026] action:@selector(openDocument:) keyEquivalent:@"o"]; |
| 797 | |
| 798 | [menu addItem:[NSMenuItem separatorItem]]; |
| 799 | |
| 800 | item = [menu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"]; |
| 801 | item = [menu addItemWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]; |
| 802 | item = [menu addItemWithTitle:[NSString stringWithFormat:@"Save As%C", (unichar)0x2026] action:@selector(saveDocumentAs:) keyEquivalent:@"S"]; |
| 803 | item = [menu addItemWithTitle:@"Save All" action:@selector(saveAllDocuments:) keyEquivalent:@""]; |
| 804 | item = [menu addItemWithTitle:@"Revert to Saved" action:@selector(revertDocumentToSaved:) keyEquivalent:@"u"]; |
| 805 | } |
| 806 | |
| 807 | - (void)appendEditMenu:(NSMenu *)parent { |
| 808 | NSMenuItem *item; |
| 809 | |
| 810 | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Edit"]; |
| 811 | item = [parent addItemWithTitle:@"Edit" action:NULL keyEquivalent:@""]; |
| 812 | [parent setSubmenu:menu forItem:item]; |
| 813 | [menu release]; |
| 814 | |
| 815 | item = [menu addItemWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"]; |
| 816 | item = [menu addItemWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"]; |
| 817 | |
| 818 | [menu addItem:[NSMenuItem separatorItem]]; |
| 819 | |
| 820 | item = [menu addItemWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]; |
| 821 | item = [menu addItemWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]; |
| 822 | item = [menu addItemWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]; |
| 823 | item = [menu addItemWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""]; |
| 824 | item = [menu addItemWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"]; |
| 825 | } |
| 826 | |
| 827 | - (void)appendWindowMenu:(NSMenu *)parent { |
| 828 | NSMenuItem *item; |
| 829 | |
| 830 | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Window"]; |
| 831 | item = [parent addItemWithTitle:@"Window" action:NULL keyEquivalent:@""]; |
| 832 | [parent setSubmenu:menu forItem:item]; |
| 833 | [menu release]; |
| 834 | [NSApp setWindowsMenu:menu]; |
| 835 | |
| 836 | item = [menu addItemWithTitle:@"Minimize" action:@selector(performMinimize:) keyEquivalent:@"m"]; |
| 837 | item = [menu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; |
| 838 | |
| 839 | [menu addItem:[NSMenuItem separatorItem]]; |
| 840 | |
| 841 | item = [menu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""]; |
| 842 | } |
| 843 | |
| 844 | - (void)appendHelpMenu:(NSMenu *)parent { |
| 845 | NSMenuItem *item; |
| 846 | NSString *const appName = [(NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()) objectForKey:@"CFBundleName"]; |
| 847 | |
| 848 | NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Help"]; |
| 849 | item = [parent addItemWithTitle:@"Help" action:NULL keyEquivalent:@""]; |
| 850 | [parent setSubmenu:menu forItem:item]; |
| 851 | [menu release]; |
| 852 | [menu setValue:@"NSHelpMenu" forKey:@"name"]; |
| 853 | if ([NSApp respondsToSelector:@selector(setHelpMenu:)]) |
| 854 | [NSApp performSelector:@selector(setHelpMenu:) withObject:menu]; |
| 855 | |
| 856 | item = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ Help", appName] action:@selector(showHelp:) keyEquivalent:@"?"]; |
| 857 | } |
| 858 | |
| 859 | - (id)init { |
| 860 | if (!(self = [super init])) return nil; |
| 861 | effects = NULL; |
| 862 | return self; |
| 863 | } |
| 864 | |
| 865 | - (void)dealloc { |
| 866 | if (effects) free(effects); |
| 867 | [super dealloc]; |
| 868 | } |
| 869 | |
| 870 | - (IBAction)newEffect:(id)sender { |
| 871 | int const index = [sender tag]; |
| 872 | if ((0 > index) || (0 == effects[index].component)) |
| 873 | { |
| 874 | NSAlert *const alert = [[NSAlert alloc] init]; |
| 875 | [alert setAlertStyle:NSWarningAlertStyle]; |
| 876 | [alert setMessageText:@"Invalid effect component"]; |
| 877 | [alert addButtonWithTitle:@"OK"]; |
| 878 | [alert runModal]; |
| 879 | [alert release]; |
| 880 | return; |
| 881 | } |
| 882 | |
| 883 | NSNumber *const typeValue = [NSNumber numberWithUnsignedLong:effects[index].type]; |
| 884 | NSNumber *const subtypeValue = [NSNumber numberWithUnsignedLong:effects[index].subtype]; |
| 885 | NSNumber *const manufacturerValue = [NSNumber numberWithUnsignedLong:effects[index].manufacturer]; |
| 886 | NSDictionary *const desc = [NSDictionary dictionaryWithObjectsAndKeys:typeValue, ComponentTypeKey, |
| 887 | subtypeValue, ComponentSubTypeKey, |
| 888 | manufacturerValue, ComponentManufacturerKey, |
| 889 | nil]; |
| 890 | NSString *errDesc = nil; |
| 891 | NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc |
| 892 | format:NSPropertyListXMLFormat_v1_0 |
| 893 | errorDescription:&errDesc]; |
| 894 | if ((nil == data) || (nil != errDesc)) |
| 895 | { |
| 896 | NSAlert *const alert = [[NSAlert alloc] init]; |
| 897 | [alert setAlertStyle:NSWarningAlertStyle]; |
| 898 | [alert setMessageText:@"Error serialising properties for new effect"]; |
| 899 | if (nil != errDesc) [alert setInformativeText:[errDesc autorelease]]; |
| 900 | [alert addButtonWithTitle:@"OK"]; |
| 901 | [alert runModal]; |
| 902 | [alert release]; |
| 903 | return; |
| 904 | } |
| 905 | |
| 906 | NSError *err = nil; |
| 907 | AUEffectDocument *const document = [[AUEffectDocument alloc] init]; |
| 908 | if ((nil == document) || ![document readFromData:data ofType:AUEffectDocumentType error:&err]) |
| 909 | { |
| 910 | [document release]; |
| 911 | if (nil != err) |
| 912 | { |
| 913 | [[NSAlert alertWithError:err] runModal]; |
| 914 | } |
| 915 | else |
| 916 | { |
| 917 | NSAlert *const alert = [[NSAlert alloc] init]; |
| 918 | [alert setAlertStyle:NSWarningAlertStyle]; |
| 919 | [alert setMessageText:@"Error creating new effect document"]; |
| 920 | [alert addButtonWithTitle:@"OK"]; |
| 921 | [alert runModal]; |
| 922 | [alert release]; |
| 923 | } |
| 924 | return; |
| 925 | } |
| 926 | |
| 927 | [document makeWindowControllers]; |
| 928 | [document showWindows]; |
| 929 | [[NSDocumentController sharedDocumentController] addDocument:document]; |
| 930 | [document release]; |
| 931 | } |
| 932 | |
| 933 | - (void)applicationWillFinishLaunching:(NSNotification *)notification { |
| 934 | NSMenu *const menubar = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"MainMenu"]; |
| 935 | [NSApp setMainMenu:menubar]; |
| 936 | [menubar release]; |
| 937 | [self appendApplicationMenu:menubar]; |
| 938 | [self appendFileMenu:menubar]; |
| 939 | [self appendEditMenu:menubar]; |
| 940 | [self appendWindowMenu:menubar]; |
| 941 | [self appendHelpMenu:menubar]; |
| 942 | |
| 943 | ProcessSerialNumber const serial = { 0, kCurrentProcess }; |
| 944 | OSStatus const status = TransformProcessType(&serial, kProcessTransformToForegroundApplication); |
| 945 | if (noErr != status) |
| 946 | { |
| 947 | NSLog(@"Error transforming to foreground application (%ld)", (long)status); |
| 948 | [NSApp terminate:self]; |
| 949 | } |
| 950 | else |
| 951 | { |
| 952 | [NSApp activateIgnoringOtherApps:YES]; |
| 953 | } |
| 954 | } |
| 955 | |
| 956 | - (void)applicationDidFinishLaunching:(NSNotification *)notification { |
| 957 | ComponentDescription effectFilter = { kAudioUnitType_Effect, 0, 0, 0, 0 }; |
| 958 | long const count = CountComponents(&effectFilter); |
| 959 | if (0 == count) |
| 960 | { |
| 961 | NSAlert *const alert = [[NSAlert alloc] init]; |
| 962 | [alert setAlertStyle:NSWarningAlertStyle]; |
| 963 | [alert setMessageText:@"No AudioUnit effects found"]; |
| 964 | [alert addButtonWithTitle:@"OK"]; |
| 965 | [alert runModal]; |
| 966 | [alert release]; |
| 967 | } |
| 968 | |
| 969 | std::vector<std::pair<Component, OSStatus> > failed; |
| 970 | effects = (EffectInfo *)malloc(count * sizeof(*effects)); |
| 971 | Component effect = FindNextComponent(0, &effectFilter); |
| 972 | for (long i = 0; (i < count) && (effect != 0); i++, effect = FindNextComponent(effect, &effectFilter)) |
| 973 | { |
| 974 | ComponentDescription effectDesc; |
| 975 | Handle const nameHandle = NewHandle(4); |
| 976 | OSStatus const err = GetComponentInfo(effect, &effectDesc, nameHandle, NULL, NULL); |
| 977 | if (noErr == err) |
| 978 | { |
| 979 | effects[i].component = effect; |
| 980 | effects[i].type = effectDesc.componentType; |
| 981 | effects[i].subtype = effectDesc.componentSubType; |
| 982 | effects[i].manufacturer = effectDesc.componentManufacturer; |
| 983 | HLock(nameHandle); |
| 984 | CFStringRef name = CFStringCreateWithPascalString(NULL, |
| 985 | (unsigned char const *)*nameHandle, |
| 986 | kCFStringEncodingMacRoman); |
| 987 | HUnlock(nameHandle); |
| 988 | NSMenuItem *const item = [newEffectMenu addItemWithTitle:(NSString *)name |
| 989 | action:@selector(newEffect:) |
| 990 | keyEquivalent:@""]; |
| 991 | [item setTag:i]; |
| 992 | [item setTarget:self]; |
| 993 | CFRelease(name); |
| 994 | } |
| 995 | else |
| 996 | { |
| 997 | effects[i].component = 0; |
| 998 | failed.push_back(std::make_pair(effect, err)); |
| 999 | } |
| 1000 | DisposeHandle(nameHandle); |
| 1001 | } |
| 1002 | |
| 1003 | if (!failed.empty()) |
| 1004 | { |
| 1005 | NSString *const message = [NSString stringWithFormat:@"Failed to get info for %lu effect%s", |
| 1006 | (unsigned long)failed.size(), |
| 1007 | ((1U == failed.size()) ? "" : "s")]; |
| 1008 | NSMutableString *const detail = [NSMutableString stringWithCapacity:(16 * failed.size())]; |
| 1009 | std::vector<std::pair<Component, OSStatus> >::const_iterator it = failed.begin(); |
| 1010 | [detail appendFormat:@"%lu (%ld)", (unsigned long)it->first, (long)it->second]; |
| 1011 | ++it; |
| 1012 | while (failed.end() != it) |
| 1013 | { |
| 1014 | [detail appendFormat:@", %lu (%ld)", (unsigned long)it->first, (long)it->second]; |
| 1015 | ++it; |
| 1016 | } |
| 1017 | NSAlert *const alert = [[NSAlert alloc] init]; |
| 1018 | [alert setAlertStyle:NSWarningAlertStyle]; |
| 1019 | [alert setMessageText:message]; |
| 1020 | [alert setInformativeText:[NSString stringWithString:detail]]; |
| 1021 | [alert addButtonWithTitle:@"OK"]; |
| 1022 | [alert runModal]; |
| 1023 | [alert release]; |
| 1024 | } |
| 1025 | } |
| 1026 | |
| 1027 | - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender { |
| 1028 | return NO; |
| 1029 | } |
| 1030 | |
| 1031 | @end |
| 1032 | |
| 1033 | |
| 1034 | int main(int argc, char *argv[]) |
| 1035 | { |
| 1036 | NSAutoreleasePool *pool; |
| 1037 | |
| 1038 | // Initialise NSApplication |
| 1039 | pool = [[NSAutoreleasePool alloc] init]; |
| 1040 | [NSApplication sharedApplication]; |
| 1041 | AUEffectUtilAppDelegate *const delegate = [[AUEffectUtilAppDelegate alloc] init]; |
| 1042 | [[NSApplication sharedApplication] setDelegate:delegate]; |
| 1043 | [pool release]; |
| 1044 | |
| 1045 | // Let's go! |
| 1046 | pool = [[NSAutoreleasePool alloc] init]; |
| 1047 | [NSApp run]; |
| 1048 | [delegate release]; |
| 1049 | [pool release]; |
| 1050 | return 0; |
| 1051 | } |