1/* DocView.m 2 * The Cenon document view class 3 * 4 * Copyright (C) 1996-2013 by vhf interservice GmbH 5 * Author: Georg Fleischmann 6 * 7 * created: 1996-01-29 8 * modified: 2013-06-29 (-scaleUnitSquareToSize: save scale factor to docSettingsDict) 9 * 2013-02-13 (include header VCurveFit.h from GraphicObjects.subproj) 10 * 2012-11-15 (-dragSelect: fixed for group selection) 11 * 2012-06-29 (-reverse: -pathSetStartPoint: layerList updateObject) 12 * 2012-04-13 (-moveObject: scroll_rect is now part of rect_vis around pt) 13 * 2012-04-13 (-drawRect: centerScanRect added, -redrawObject: centerScanRect changed) 14 * 2012-02-29 (-joinSelection:messages: copy path for correct undo) 15 * 2012-02-19 (-setList: keep order, when separating color to layers) 16 * 2012-02-13 (-drawRect: centerScanRect removed) 17 * 2012-01-25 (-draw: do not draw invisible layers) 18 * 2012-01-24 (-knowsPageRange: added) 19 * 2012-01-04 (-mouseDown: no beep for locked objects, if mouse didn't move) 20 * 2011-04-06 (pathSetStartPoint: added) 21 * 2011-04-06 (-buildContour: [change setRemoveSource:], [path setSelected:YES], fitGraphic added) 22 * (-vectorizeWithTolerance:... new) 23 * 2009-09-22 (-dragMagnify: init region.origin) 24 * 2009-09-21 (-draw: use NSAffineTranform on Apple/GNUstep and PStranslate on OpenStep) 25 * 2009-13-19 (-draw: display #DATE_...#, display non-template elements on even/odd template layer) 26 * 2008-12-18 (-selectColor: VGroup got a fillColor) 27 * 28 * This program is free software; you can redistribute it and/or 29 * modify it under the terms of the vhf Public License as 30 * published by vhf interservice GmbH. Among other things, the 31 * License requires that the copyright notices and this notice 32 * be preserved on all copies. 33 * 34 * This program is distributed in the hope that it will be useful, 35 * but WITHOUT ANY WARRANTY; without even the implied warranty of 36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 37 * See the vhf Public License for more details. 38 * 39 * You should have received a copy of the vhf Public License along 40 * with this program; see the file LICENSE. If not, write to vhf. 41 * 42 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany 43 * eMail: info@vhf.de 44 * http://www.vhf.de 45 */ 46 47#include <AppKit/AppKit.h> 48#include <math.h> 49#include <VHFShared/types.h> 50#include <VHFShared/vhf2DFunctions.h> 51#include <VHFShared/VHFStringAdditions.h> 52#include <VHFShared/vhfSoundFunctions.h> 53#include "App.h" 54#include "Document.h" 55#include "DocWindow.h" 56#include "DocView.h" 57#include "LayerObject.h" 58#include "TileObject.h" 59#include "InspectorPanel.subproj/InspectorPanel.h" 60#include "PreferencesPanel.subproj/NotificationNames.h" 61#include "TileScrollView.h" 62#include "Graphics.h" 63#include "GraphicObjects.subproj/VCurveFit.h" // vectorization 64#include "messages.h" 65#include "graphicsUndo.subproj/undo.h" 66#include "propertyList.h" 67#include "GraphicObjects.subproj/HiddenArea.h" 68#include "GraphicObjects.subproj/PathContour.h" // for buildContour: 69 70/* Private methods 71 */ 72@interface DocView(PrivateMethods) 73- (void)scrollPointToVisible:(NSPoint)point; 74- (NSRect)dragSelect:(NSEvent *)event; 75- (void)dragMagnify:(NSEvent *)event; 76- (void)rotateObject:obj :(NSEvent *)event :(NSRect)redrawRect; 77- (void)joinSelection:(id)change messages:(BOOL)messages; 78@end 79 80@interface Dummy 81- (CGFloat)backingScaleFactor; 82@end 83 84NSString *e2PboardType = @"Cenon Graphic List"; 85 86@implementation DocView 87 88/* common functions 89 */ 90/* 91 * Timers used to automatically scroll when the mouse is 92 * outside the drawing view and not moving. 93 */ 94static void startTimer(BOOL *inTimerLoop) 95{ 96 if (!*inTimerLoop) 97 { [NSEvent startPeriodicEventsAfterDelay:0.15 withPeriod:0.2]; 98 //[NSEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.5]; 99 *inTimerLoop = YES; 100 } 101} 102static void stopTimer(BOOL *inTimerLoop) 103{ 104 if (*inTimerLoop) 105 { [NSEvent stopPeriodicEvents]; 106 *inTimerLoop = NO; 107 } 108} 109 110extern NSEvent *periodicEventWithLocationSetToPoint(NSEvent *oldEvent, NSPoint point) 111{ 112 return [NSEvent otherEventWithType:[oldEvent type] location:point modifierFlags:[oldEvent modifierFlags] timestamp:[oldEvent timestamp] windowNumber:[oldEvent windowNumber] context:[oldEvent context] subtype:[oldEvent subtype] data1:[oldEvent data1] data2:[oldEvent data2]]; 113} 114 115/* class methods 116 */ 117 118 119/* instance methods 120 */ 121 122/* 123 * Create a plain window the size of the rectangle passed in and 124 * then insert a view into the window as a subview. A clip view 125 * is swapped for the content view if addclipview is YES. The 126 * ClipView is used for the alpha buffer, which holds the primary 127 * drawing. The beta buffer does not need to scroll so a ClipView 128 * is unnecessary. 129 */ 130static id createBuffer(NSRect winRect, BOOL addclipview) 131{ id buffer, clipview; 132 NSWindow *window; 133 NSRect contRect; 134 135 contRect.origin.x = contRect.origin.y = 0; 136 contRect.size = winRect.size; 137 window = [[NSWindow alloc] initWithContentRect:contRect styleMask:NSBorderlessWindowMask 138 backing:NSBackingStoreRetained defer:NO]; 139 [window setReleasedWhenClosed:NO]; // we close the window, not [App terminate] 140 141 buffer = [[NSView alloc] initWithFrame:contRect]; 142 [buffer allocateGState]; 143 if (addclipview) 144 { 145 clipview = [[NSClipView alloc] init]; 146 //[clipview setDisplayOnScroll:NO]; 147 [window setContentView:clipview]; 148 [clipview release]; 149 [clipview setDocumentView:buffer]; 150 [buffer release]; 151 } 152 else 153 { [[window contentView] addSubview:buffer]; 154 [buffer release]; 155 } 156 [window setAutodisplay:NO]; 157 [window display]; 158 //[window orderFront:nil]; // debugging: display cache window 159 160 return buffer; 161} 162 163/* 164 * This sets the class version so that we can compatibly read 165 * old Graphic objects out of an archive. 166 */ 167+ (void)initialize 168{ 169 [DocView setVersion:6]; 170 return; 171} 172 173+ (NSRect)boundsOfArray:(NSArray*)list 174{ int i, l; 175 NSRect rect, bbox = NSZeroRect; 176 177 if ( ![list count] ) 178 return bbox; 179 180 /* layer list */ 181 if ( [[list objectAtIndex:0] isKindOfClass:[LayerObject class]] ) 182 { 183 for (l=[list count]-1; l>=0; l--) 184 { 185 if ( [[[list objectAtIndex:l] list] count] ) 186 { 187 rect = [self boundsOfArray:[[list objectAtIndex:l] list]]; 188 bbox = (!bbox.size.width) ? rect : NSUnionRect(rect, bbox); 189 } 190 } 191 return bbox; 192 } 193 /* slayList */ 194 else if ( [[list objectAtIndex:0] isKindOfClass:[NSMutableArray class]] ) 195 { 196 for (l=[list count]-1; l>=0; l--) 197 { 198 if ( [(NSArray*)[list objectAtIndex:l] count] ) 199 { 200 rect = [self boundsOfArray:[list objectAtIndex:l]]; 201 bbox = (!bbox.size.width) ? rect : NSUnionRect(rect, bbox); 202 } 203 } 204 return bbox; 205 } 206 207 /* graphic list */ 208 bbox = [[list objectAtIndex:0] bounds]; 209 for (i=[list count]-1; i>0; i--) 210 { 211 rect = [[list objectAtIndex:i] bounds]; 212 bbox = NSUnionRect(rect, bbox); 213 } 214 215 return bbox; 216} 217 218- (BOOL)acceptsFirstResponder 219{ 220 return YES; 221} 222 223- (void)fillAllObjects 224{ int l, i; 225 226 if ( !Prefs_FillObjects ) 227 return; 228 229 for ( l=0; l<(int)[layerList count]; l++ ) 230 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 231 232 //[self selectAll:self redraw:NO]; 233 //[self joinSelection:nil messages:NO]; 234 for ( i=0; i<(int)[list count]; i++ ) 235 [[list objectAtIndex:i] setFilled:YES]; 236 //[self deselectAll:nil redraw:NO]; 237 } 238} 239 240- (void)setBackgroundColor:(NSColor*)color 241{ 242 backgroundColor = [color retain]; 243} 244- (NSColor*)backgroundColor 245{ 246 return backgroundColor; 247} 248 249- (void)setSeparationColor:(NSColor*)color 250{ 251 separationColor = [color retain]; 252} 253- (NSColor*)separationColor { return separationColor; } 254 255#define vhfColorDifference(c1, c2) (Diff([(c1) redComponent], [(c2) redComponent]) + Diff([(c1) greenComponent], [(c2) greenComponent]) + Diff([(c1) blueComponent], [(c2) blueComponent])) 256#define COLOR_TOLERANCE 0.02 257int colorLayer(NSColor *color, NSDictionary *colorDict) 258{ NSEnumerator *enumerator = [colorDict keyEnumerator]; 259 id key; 260 NSColor *color1 = [color colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"]; 261 262 while ( (key = [enumerator nextObject]) ) 263 { NSColor *colorD = [[colorDict objectForKey:key] colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"]; 264 265 if ( vhfColorDifference( colorD, color1 ) < COLOR_TOLERANCE ) 266 return [key intValue]; 267 } 268 return -1; 269} 270/* set single list of objects or complete layerList 271 * modified: 2012-02-19 (keep order, when separating color to layers) 272 */ 273- (void)setList:(NSMutableArray*)list 274{ int l; 275 276 if ([list count] && [[list objectAtIndex:0] isKindOfClass:[LayerObject class]]) 277 { 278 [layerList release]; 279 layerList = [list retain]; 280 [self getSelection]; 281 //[self addLayerWithName:LAYERCLIPPING_STRING type:LAYER_CLIPPING tag:0 list:nil editable:NO]; 282 for ( l=[layerList count]-1; l>=0; l-- ) 283 [[layerList objectAtIndex:l] createPerformanceMapWithFrame:[self bounds]]; 284 } 285 else 286 { 287 if ( ![layerList count] ) 288 return; 289 for ( l=[layerList count]-1; (Prefs_ColorToLayer) ? l>=0 : l>0; l-- ) 290 { 291 [layerList removeObjectAtIndex:l]; 292 [slayList removeObjectAtIndex:l]; 293 } 294 if ( Prefs_ColorToLayer ) 295 { int i; 296 NSMutableDictionary *colorDict = [NSMutableDictionary dictionary]; 297 298 /* create dictionary with colors and layer index */ 299 for ( i=0, l=0; i<(int)[list count]; i++ ) 300 { id obj = [list objectAtIndex:i]; 301 NSColor *col = ([obj color]) ? [obj color] : [NSColor blackColor]; 302 303 /* add color to dictionary, create layer */ 304 if ( colorLayer(col, colorDict) < 0 ) 305 { LayerObject *layerObject = [LayerObject layerObjectWithFrame:[self bounds]]; 306 307 [layerObject setString:[NSString stringWithFormat:@"Layer %d", l+1]]; 308 [layerList addObject:layerObject]; 309 [slayList addObject:[NSMutableArray array]]; 310 [colorDict setObject:col forKey:[NSNumber numberWithInt:l++]]; 311 } 312 } 313 314 /* separate objects to layers */ 315 //for ( i=[list count]-1; i>=0; i-- ) // 2012-02-19: this was changing the order of the list ! 316 for ( i=0; i < [list count]; i++ ) 317 { id obj = [list objectAtIndex:i]; 318 319 if ( (l = colorLayer([obj color], colorDict)) < 0 ) 320 l = 0; 321 [[layerList objectAtIndex:l] addObject:obj]; 322 } 323 } 324 else 325 { 326 [[layerList objectAtIndex:0] setList:list]; 327 [self getSelection]; 328 } 329 } 330 331 [self fillAllObjects]; 332 333 if ( [self window] ) 334 [self drawAndDisplay]; 335} 336 337/* build a single list if list is a layerList 338 */ 339- (id)singleList:(NSArray*)list 340{ NSMutableArray *array; 341 int l, i; 342 343 if (![list count] || ![[list objectAtIndex:0] isKindOfClass:[LayerObject class]]) 344 return list; 345 346 array = [NSMutableArray array]; 347 for (l=0; l<(int)[list count]; l++) 348 { NSArray *objs = [[list objectAtIndex:l] list]; 349 350 for (i=0; i<(int)[objs count]; i++) 351 [array addObject:[objs objectAtIndex:i]]; 352 } 353 return array; 354} 355 356/* add list of objects or layers 357 * list array of graphic objects or LayerObjects 358 * layer index of layer 359 * -1 => create layer(s) 360 * -2 => to existing layers (by color or name) 361 * replaceObjs remove objects before adding 362 * 363 * modified: 2005-09-25 (use existing layer) 364 */ 365- (void)addList:(NSMutableArray*)list toLayerAtIndex:(int)layer //replaceObjects:(BOOL)replaceObjs 366{ int i, l, insertOffset = 0; 367 368 if ( ![list count] ) 369 return; 370 371 /* use existing layers */ 372 if ( layer == -2 ) 373 { BOOL createNonExistingLayers = YES; 374 375 /* add to layers by layer name (LayerObjects) */ 376 if ([[list objectAtIndex:0] isKindOfClass:[LayerObject class]]) 377 { 378 for (i=0; i<(int)[list count]; i++) // find layer with name 379 { LayerObject *layerObject = [list objectAtIndex:i], *destinationLayer = nil; 380 NSString *name = [layerObject string]; 381 382 for (l=0; l<[layerList count]; l++) 383 { 384 if ( [[[layerList objectAtIndex:l] string] isEqual:name] ) 385 { destinationLayer = [layerList objectAtIndex:l]; 386 break; 387 } 388 } 389 if (!destinationLayer) // no layer with this name 390 { 391 NSLog(@"Import to existing layer: No layer available with name '%@'", name); 392 if ( createNonExistingLayers && 393 NSRunAlertPanel(@"", IMPORTTONOTEXISTINGLAYER_STRING, 394 CREATELAYER_STRING, SKIP_STRING, nil, name) 395 == NSAlertDefaultReturn ) 396 { 397 [slayList insertObject:[NSMutableArray array] atIndex:[layerList count]-insertOffset]; 398 [layerList insertObject:layerObject atIndex:[layerList count]-insertOffset]; 399 [layerObject createPerformanceMapWithFrame:[self bounds]]; 400 } 401 else 402 createNonExistingLayers = NO; 403 } 404 else 405 { 406 //if (replaceObjs) // remove all objects from destination layer 407 [destinationLayer removeAllObjects]; 408 [destinationLayer addObjectsFromArray:[layerObject list]]; // add objects 409 } 410 } 411 } 412 /* add to layers by color of existing object */ 413 else if (Prefs_ColorToLayer) 414 { NSMutableDictionary *colorDict = [NSMutableDictionary dictionary]; 415 LayerObject *extraLayer = nil; 416 417 /* create dictionary with colors and layer index */ 418 for ( l=0; l<(int)[layerList count]; l++ ) 419 { LayerObject *layerObject = [layerList objectAtIndex:l]; 420 VGraphic *g = ([[layerObject list] count]) ? [[layerObject list] objectAtIndex:0] : nil; 421 NSColor *col = ([g color]) ? [g color] : [NSColor blackColor]; 422 423 /* add color to dictionary */ 424 if ( colorLayer(col, colorDict) < 0 ) // not in dictionary 425 [colorDict setObject:col forKey:[NSNumber numberWithInt:l]]; 426 //if (replaceObjs) // remove all objects from destination layer 427 [layerObject removeAllObjects]; 428 } 429 430 /* separate objects to layers */ 431 for ( i=[list count]-1; i>=0; i-- ) 432 { VGraphic *g = [list objectAtIndex:i]; 433 434 if ( (l = colorLayer([g color], colorDict)) < 0) // not in dictionary -> add to extra layer 435 { 436 if (!extraLayer) 437 { 438 if ( createNonExistingLayers ) // log only first object ! 439 NSLog(@"Import to existing layer: No layer available with color '%@'", [g color]); 440 if ( createNonExistingLayers && 441 NSRunAlertPanel(@"", IMPORTTONOTEXISTINGLAYER_STRING, 442 CREATELAYER_STRING, SKIP_STRING, nil, [g color]) 443 == NSAlertDefaultReturn ) // create extra layer 444 { 445 extraLayer = [LayerObject layerObjectWithFrame:[self bounds]]; 446 [extraLayer setString:@"Extra Layer"]; 447 [slayList insertObject:[NSMutableArray array] atIndex:[layerList count]-insertOffset]; 448 [layerList insertObject:extraLayer atIndex:[layerList count]-insertOffset]; 449 } 450 else 451 createNonExistingLayers = NO; 452 } 453 [extraLayer addObject:g]; 454 } 455 else 456 [[layerList objectAtIndex:l] addObject:g]; 457 } 458 } 459 else // fallback -> create one new layer for everything 460 { LayerObject *layerObject = [LayerObject layerObjectWithFrame:[self bounds]]; 461 462 NSLog(@"Import to existing layer: We either need layer names (DXF) or reference objects with color."); 463 [layerObject setString:[NSString stringWithFormat:@"Layer %d", (int)[layerList count]-insertOffset]]; 464 [layerObject addObjectsFromArray:list]; 465 [slayList insertObject:[NSMutableArray array] atIndex:[layerList count]-insertOffset]; 466 [layerList insertObject:layerObject atIndex:[layerList count]-insertOffset]; 467 } 468 } 469 /* create layers */ 470 else if ( layer == -1 ) 471 { 472 /* create layers from list of layers (LayerObjects) */ 473 if ([[list objectAtIndex:0] isKindOfClass:[LayerObject class]]) 474 { 475 for (i=0; i<(int)[list count]; i++) 476 { LayerObject *layerObject = [list objectAtIndex:i]; 477 478 [layerList insertObject:layerObject atIndex:[layerList count]-insertOffset]; 479 [layerObject createPerformanceMapWithFrame:[self bounds]]; 480 } 481 [self getSelection]; 482 } 483 /* create one new layer from single list of objects */ 484 else 485 { LayerObject *layerObject = [LayerObject layerObjectWithFrame:[self bounds]]; 486 487 [layerObject setString:[NSString stringWithFormat:@"Layer %d", (int)[layerList count]-insertOffset]]; 488 [layerObject addObjectsFromArray:list]; 489 [slayList insertObject:[NSMutableArray array] atIndex:[layerList count]-insertOffset]; 490 [layerList insertObject:layerObject atIndex:[layerList count]-insertOffset]; 491 } 492 } 493 /* add to layer at given index */ 494 else 495 { 496 if ( layer >= (int)[layerList count] ) 497 return; 498 [[layerList objectAtIndex:layer] addObjectsFromArray:[self singleList:list]]; 499 [self getSelection]; 500 } 501 502 [self fillAllObjects]; 503 504 [[NSNotificationCenter defaultCenter] postNotificationName:DocLayerListHasChanged object:self]; 505 if ( [self window] ) 506 [self drawAndDisplay]; 507} 508 509- (int)addLayerWithName:(NSString*)name type:(int)type tag:(int)tag list:(NSMutableArray*)array editable:(BOOL)editable 510{ LayerObject *layerObject; 511 int l; 512 513 /* check */ 514 for (l=0; l<(int)[layerList count]; l++) 515 { layerObject = [layerList objectAtIndex:l]; 516 517 if ( [[layerObject string] isEqualToString:name] ) /* name allready in use! */ 518 return NO; 519 /* only one layer of leveling or clipping type */ 520 if ( (type == LAYER_LEVELING || type == LAYER_CLIPPING) && [layerObject type] == type ) 521 return NO; 522 } 523 524 /* get location of new layer */ 525 for (l=[layerList count]-1; l>=0; l--) 526 { layerObject = [layerList objectAtIndex:l]; 527 528 if ([layerObject type] == LAYER_STANDARD) 529 { l += 1; 530 break; 531 } 532 } 533 if ( l == -1 ) 534 l = 0; 535 536 layerObject = [[[LayerObject alloc] initWithFrame:[self bounds]] autorelease]; 537 if (array) 538 [layerObject setList:array]; 539 [layerObject setString:name]; 540 [layerObject setTag:tag]; 541 [layerObject setType:type]; 542 [layerObject setEditable:editable]; 543 544 [layerList insertObject:layerObject atIndex:l]; 545 [slayList insertObject:[NSMutableArray array] atIndex:l]; 546 547 return l; 548} 549 550- (int)insertLayerWithName:(NSString*)name atIndex:(int)index type:(int)type tag:(int)tag list:(NSMutableArray*)array editable:(BOOL)editable 551{ LayerObject *layerObject; 552 int l; 553 554 /* check */ 555 for (l=0; l<(int)[layerList count]; l++) 556 { layerObject = [layerList objectAtIndex:l]; 557 558 if ( [[layerObject string] isEqualToString:name] ) /* name allready in use! */ 559 return NO; 560 /* only one layer of leveling or clipping type */ 561 if ( (type == LAYER_LEVELING || type == LAYER_CLIPPING) && [layerObject type] == type ) 562 return NO; 563 } 564 565 /* check location of new layer */ 566 for (l=[layerList count]-1; l>=0; l--) 567 { layerObject = [layerList objectAtIndex:l]; 568 569 if ([layerObject type] == LAYER_STANDARD) 570 { l += 1; 571 break; 572 } 573 } 574 if (index > l ) 575 index = l; 576 if ( index < 0 ) 577 index = 0; 578 579 layerObject = [[[LayerObject alloc] initWithFrame:[self bounds]] autorelease]; 580 if (array) 581 [layerObject setList:array]; 582 [layerObject setString:name]; 583 [layerObject setTag:tag]; 584 [layerObject setType:type]; 585 [layerObject setEditable:editable]; 586 587 [layerList insertObject:layerObject atIndex:index]; 588 [slayList insertObject:[NSMutableArray array] atIndex:index]; 589 590 return index; 591} 592 593- (DocView*)initWithFrame:(NSRect)frameRect 594{ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 595 596 [super initWithFrame:frameRect]; 597 [self createEditView]; 598 [self registerForDragging]; 599 600 [notificationCenter addObserver:self 601 selector:@selector(allLayersHaveChanged:) 602 name:PrefsAllLayersHaveChanged 603 object:nil]; 604 [notificationCenter addObserver:self 605 selector:@selector(cachingHasChanged:) 606 name:PrefsCachingHasChanged 607 object:nil]; 608 609 return self; 610} 611 612/* sets the document for the view 613 */ 614- (void)setDocument:docu 615{ 616 document = docu; 617} 618- (id)document 619{ 620 return document; 621} 622 623- (DocView*)initView 624{ LayerObject *layerObject; 625 NSMutableArray *slist; 626 627 [self setParameter]; 628 629 layerList = [[NSMutableArray allocWithZone:[self zone]] init]; // the layer list 630 layerObject = [[[LayerObject allocWithZone:[self zone]] init] autorelease]; 631 [layerObject createPerformanceMapWithFrame:[self bounds]]; 632 [layerList addObject:layerObject]; 633 634 slayList = [[NSMutableArray allocWithZone:[self zone]] init]; // the selected list 635 slist = [NSMutableArray array]; 636 [slayList addObject:slist]; 637 638 //[self addLayerWithName:LAYERCLIPPING_STRING type:LAYER_CLIPPING tag:0 list:nil editable:NO]; 639 640 origin = [[VCrosshairs allocWithZone:[self zone]] init]; 641 642 return self; 643} 644 645- (void)setParameter 646{ 647 doCaching = Prefs_Caching; 648 if (doCaching) 649 { cacheView = createBuffer([self bounds], NO); 650 cache = [cacheView window]; 651 //[cacheView setOpaque:YES]; 652 } 653 //[self setOpaque:YES]; 654 655 /* cache for moving objects (-moveObjects:) 656 * the size should come from preferences 657 */ 658#ifdef __APPLE__ 659 betaCache = nil; 660#else 661 betaCache = [[NSWindow allocWithZone:[self zone]] initWithContentRect:[self bounds] 662 styleMask:NSBorderlessWindowMask 663 backing:NSBackingStoreRetained defer:NO]; 664 [betaCache setAutodisplay:NO]; 665 if ([betaCache respondsToSelector:@selector(setOpaque:)]) 666 [betaCache setOpaque:NO]; 667 [[betaCache contentView] allocateGState]; 668#endif 669 670 scale = 1.0; // the scale factor 671 672 displayGraphic = YES; 673 674 if ( !statusDict ) 675 statusDict = [NSMutableDictionary new]; 676} 677 678- (NSMutableDictionary*)statusDict 679{ 680 if ( !statusDict ) 681 statusDict = [NSMutableDictionary new]; 682 return statusDict; 683} 684 685/* 686 * editView is essentially a dumb, FLIPPED (with extra emphasis on the 687 * flipped) subview of our GraphicView which completely covers it and 688 * which automatically sizes itself to always completely cover the 689 * GraphicView. It is necessary since growable Text objects only work 690 * when they are subviews of a flipped view. 691 * 692 * See VText for more details about why we need editView 693 * (it is purely a workaround for a limitation of the Text object). 694 */ 695- (FlippedView*)createEditView 696{ NSRect viewFrame = [self frame]; 697 698 [self setAutoresizesSubviews:YES]; 699 editView = [[FlippedView allocWithZone:[self zone]] initWithFrame: 700 (NSRect){{0, 0}, {viewFrame.size.width, viewFrame.size.height}}]; 701 //No resize, editView works on 100% 702 //[editView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; 703 [self addSubview:editView]; 704 705 return editView; 706} 707 708- (FlippedView*)editView 709{ 710 return editView; 711} 712 713- (BOOL)caching 714{ 715 return doCaching; 716} 717 718- (NSWindow*)cache 719{ 720 return cache; 721} 722 723- (void)setCaching:(BOOL)flag redraw:(BOOL)rd 724{ 725 doCaching = flag; 726 if (doCaching) 727 { NSRect rect; 728 int bits = NSBitsPerPixelFromDepth([NSWindow defaultDepthLimit]); 729 730 rect = [self frame]; 731 if ((rect.size.width * rect.size.height)*bits/8 < Prefs_CacheLimit) 732 { [self sizeCacheWindow:NSWidth(rect) :NSHeight(rect)]; 733 if (rd) 734 [self draw:rect]; 735 return; 736 } 737 } 738 [cache release]; 739 cache = nil; cacheView = nil; 740} 741 742/* change cache size 743 */ 744- (void)sizeCacheWindow:(float)width :(float)height 745{ int bits = NSBitsPerPixelFromDepth([NSWindow defaultDepthLimit]); 746 747 if ( doCaching && ((width * height)*bits/8 < Prefs_CacheLimit) ) 748 { 749 if (!cache) 750 { 751 cacheView = createBuffer([self frame], NO); 752 cache = [cacheView window]; 753 [cacheView scaleUnitSquareToSize:NSMakeSize([self frame].size.width/[self bounds].size.width, 754 [self frame].size.width/[self bounds].size.width)]; 755 } 756 [cache setContentSize:NSMakeSize(width, height)]; // limit 10000 on OpenStep !! 757 [cacheView setFrameSize:NSMakeSize(width, height)]; 758 } 759 else if (cache) 760 { [cache release]; 761 cache = nil; cacheView = nil; 762 } 763} 764 765/* zoom in or out 766 * modified: 2012-08-12 (pass newUnitSize instead of float, realign bounds and frame at 100%) 767 * FIXME: bounds/frame is screwed up when scaling to 150% or 300% and back to 100%. 768 * We realign at 100% now, 200% still sucks 769 */ 770- (void)scaleCacheWindow:(NSSize)newUnitSize 771{ int bits = NSBitsPerPixelFromDepth([NSWindow defaultDepthLimit]); 772 773 //printf("c1. u.w = %f b.w = %f f.w = %f\n", newUnitSize.width, cacheView->_bounds.size.width, cacheView->_frame.size.width); 774 /* the scrollview must allready be scaled */ 775 if ( ([self frame].size.width * [self frame].size.height)*bits/8 < Prefs_CacheLimit ) 776 { 777 if (!cache) 778 { cacheView = createBuffer([self bounds], NO); 779 cache = [cacheView window]; 780 781 /* the scrollview (frame, bounds) has already been scaled */ 782 [cacheView scaleUnitSquareToSize:NSMakeSize([self frame].size.width/[self bounds].size.width, 783 [self frame].size.width/[self bounds].size.width)]; 784 } 785 else 786 { 787 /* realign rounding issues befor scaling */ 788 /*if ( [cacheView frame].size.width != [cacheView bounds].size.width ) 789 { NSSize invUnitSize; 790 791 invUnitSize = NSMakeSize([cacheView bounds].size.width / [cacheView frame].size.width, 792 [cacheView bounds].size.height / [cacheView frame].size.height); 793 [cacheView scaleUnitSquareToSize:invUnitSize]; // reset to 100% 794 [cacheView setBoundsSize:[cacheView frame].size]; // bring coords back to normal 795 newUnitSize.width = (newUnitSize.width / invUnitSize.width); 796 newUnitSize.height = (newUnitSize.height / invUnitSize.height); 797 }*/ 798 [cacheView scaleUnitSquareToSize:newUnitSize]; 799 if ( Diff([cacheView frame].size.width, [cacheView bounds].size.width) < 0.001 ) 800 [cacheView setBoundsSize:[cacheView frame].size]; // bring coords back to normal 801 } 802 //printf("c2. u.w = %f b.w = %f f.w = %f\n", newUnitSize.width, cacheView->_bounds.size.width, cacheView->_frame.size.width); 803 } 804 else 805 { [cache release]; 806 cache = nil; cacheView = nil; 807 } 808 809 [[betaCache contentView] scaleUnitSquareToSize:newUnitSize]; 810} 811 812- (void)scaleUnitSquareToSize:(NSSize)_newUnitSize 813{ NSMutableDictionary *plist = [[(App*)NSApp currentDocument] docSettingsDict]; 814 815 scale *= _newUnitSize.width; 816 [plist setObject:propertyListFromFloat(scale) forKey:[NSString stringWithFormat:@"scaleFactor"]]; 817 //printf("v1. u.w = %f b.w = %f f.w = %f\n", _newUnitSize.width, self->_bounds.size.width, self->_frame.size.width); 818 [super scaleUnitSquareToSize:_newUnitSize]; 819 if ( Diff([self frame].size.width, [self bounds].size.width) < 0.001 ) // same in scaleCacheWindow ! 820 [self setBoundsSize:[self frame].size]; // bring coords back to normal 821 //printf("v2. u.w = %f b.w = %f f.w = %f\n", _newUnitSize.width, self->_bounds.size.width, self->_frame.size.width); 822 [self scaleCacheWindow:_newUnitSize]; 823 824 /* we are called before the frame is changed to fit the scale ! 825 * Here we keep the frame of the editview always 100%, whatever happens 826 * This could go to VText -createText: 827 */ 828 { NSRect newFrame = [self frame]; 829 830 newFrame.size.width = NSWidth (newFrame) * _newUnitSize.width / (scale); 831 newFrame.size.height = NSHeight(newFrame) * _newUnitSize.height / (scale); 832 if ( Diff(newFrame.size.width, [editView frame].size.width) > 0.1 ) // in case something rips this apart 833 { 834 NSLog(@"Note for scaleUnitSquareToSize: editView corrected to view size {%.1f %.1f} -> {%.1f %.1f}", 835 newFrame.size.width, newFrame.size.height, 836 [editView frame].size.width, [editView frame].size.height); 837 [editView setFrame:newFrame]; // workaround: editview is resized bad , may end up with zero size 838 } 839 //printf("unitSize = %f scale = %f newFrame = {%f %f}\n", _newUnitSize.width, scale, newFrame.size.width, newFrame.size.height); 840 } 841} 842 843/* 844 * sizeTo:: is called whenever the view is resized. It resizes the bitmap cache 845 * along with the view. It doesn't do anything if the new size is equal to the 846 * old one. 847 */ 848- (void)setFrameSize:(NSSize)_newSize 849{ int l; 850 NSRect bounds; 851 852 if ( _newSize.width == [self frame].size.width && _newSize.height == [self frame].size.height ) 853 return; 854 855 [super setFrameSize:_newSize]; // OpenStep: newSize/scale >= 10000 -> DPS errors ! 856 [self sizeCacheWindow:_newSize.width :_newSize.height]; 857 858 if ( [self gridIsEnabled] ) 859 [self resetGrid]; 860 861 /* resize performance map */ 862 bounds = [self bounds]; 863 for (l=0; l<(int)[layerList count]; l++) 864 { LayerObject *layerObject = [layerList objectAtIndex:l]; 865 866 [[layerObject performanceMap] resizeFrame:bounds initWithList:[layerObject list]]; 867 } 868} 869 870- (NSMutableArray*)layerList 871{ 872 return layerList; 873} 874- (NSMutableArray*)slayList 875{ 876 return slayList; 877} 878 879 880/* 881 * printing stuff 882 */ 883 884/* return YES for multi page document 885 * created: 2005-09-01 886 */ 887- (BOOL)isMultiPage 888{ int l; 889 890 for (l=0; l<(int)[layerList count]; l++) 891 if ( [(LayerObject*)[layerList objectAtIndex:l] type] == LAYER_PAGE ) 892 return YES; 893 return NO; 894} 895- (int)pageCount 896{ int l, cnt = 0; 897 898 for (l=0; l<(int)[layerList count]; l++) 899 if ( [(LayerObject*)[layerList objectAtIndex:l] type] == LAYER_PAGE ) 900 cnt++; 901 return cnt; 902} 903/* DEPRECATED since long ago */ 904/*- (BOOL)knowsPagesFirst:(NSInteger*)firstPageNum last:(NSInteger*)lastPageNum 905{ 906 if ([self isMultiPage]) 907 { 908 *firstPageNum = 1; 909 *lastPageNum = [self pageCount]; 910 return YES; 911 } 912 return NO; 913}*/ 914- (BOOL)knowsPageRange:(NSRangePointer)aRange 915{ 916 if ([self isMultiPage]) 917 { 918 *aRange = NSMakeRange(1, [self pageCount]); 919 return YES; 920 } 921 return NO; 922} 923- (NSRect)rectForPage:(NSInteger)pageNumber 924{ int l, cnt = 1; 925 926 /* enable page to print */ 927 for (l=0; l<(int)[layerList count]; l++) 928 { LayerObject *layerObject = [layerList objectAtIndex:l]; 929 930 if ( [(LayerObject*)layerObject type] == LAYER_PAGE ) 931 { 932 if ( cnt == pageNumber ) // turn on page to print 933 [layerObject setState:1]; 934 else // turn off all other pages 935 [layerObject setState:0]; 936 cnt++; 937 } 938 } 939 return [self bounds]; 940} 941 942 943/* modified: 2001-08-20 944 */ 945- (void)insertGraphic:g 946{ int l, cnt = [layerList count]; 947 948 for (l=0; l<cnt; l++) 949 { LayerObject *layerObject = [layerList objectAtIndex:l]; 950 NSMutableArray *slist = [slayList objectAtIndex:l]; 951 952 if ( [layerObject editable] ) 953 { 954 if ( [[layerObject list] indexOfObject:g] != NSNotFound ) 955 return; 956 [layerObject addObject:g]; 957 if ( [g isSelected] ) 958 [slist addObject:g]; 959 [self cache:[g extendedBoundsWithScale:scale]]; 960 [g setDirty:YES]; 961 [document setDirty:YES]; 962 return; 963 } 964 } 965} 966 967/* created: 01.10.1999 968 */ 969- (void)insertGraphic:g onLayer:(int)layerIx 970{ 971 if ( layerIx < (int)[layerList count] ) 972 { LayerObject *layer = [layerList objectAtIndex:layerIx]; 973 NSMutableArray *slist = [slayList objectAtIndex:layerIx]; 974 975 if ( [layer editable] ) 976 { 977 if ( [[layer list] indexOfObject:g] != NSNotFound ) 978 return; 979 [layer addObject:g]; 980 if ( [g isSelected] ) 981 [slist addObject:g]; 982 [g setDirty:YES]; 983 [document setDirty:YES]; 984 return; 985 } 986 } 987 else 988 NSLog(@"Layer %d beyond bounds!", layerIx); 989} 990 991/* created: 12.03.99 992 */ 993#define SORT_ROW_ULLR 0 994#define SORT_ROW_LLUR 1 995#define SORT_COL_ULLR 2 996#define SORT_COL_LLUR 3 997#define SORT_COL_URLL 4 998#define SORT_COL_LRUL 5 999#define SORT_ROW_URLL 6 1000#define SORT_ROW_LRUL 7 1001NSInteger sortPosition(id g1, id g2, void *context) 1002{ NSPoint p1 = [g1 bounds].origin, p2 = [g2 bounds].origin; 1003 int sort = *(int*)context; 1004 1005 if ( sort <= SORT_COL_LLUR ) 1006 { 1007 if ( sort==SORT_ROW_ULLR || sort==SORT_ROW_LLUR ) 1008 { 1009 if ( p1.y < p2.y ) 1010 return (sort==SORT_ROW_ULLR) ? NSOrderedDescending : NSOrderedAscending; 1011 if ( p1.y == p2.y ) 1012 { 1013 if ( p1.x < p2.x ) 1014 return NSOrderedAscending; 1015 if ( p1.x > p2.x ) 1016 return NSOrderedDescending; 1017 return NSOrderedSame; 1018 } 1019 return (sort==SORT_ROW_ULLR) ? NSOrderedAscending : NSOrderedDescending; 1020 } 1021 if ( p1.x /*+ TOLERANCE*/ < p2.x ) 1022 return NSOrderedAscending; 1023 if ( Diff(p1.x, p2.x) <= TOLERANCE ) 1024 { 1025 if ( p1.y < p2.y ) 1026 return (sort==SORT_COL_LLUR) ? NSOrderedAscending : NSOrderedDescending; 1027 if ( p1.y > p2.y ) 1028 return (sort==SORT_COL_LLUR) ? NSOrderedDescending : NSOrderedAscending; 1029 /*if ( p1.x < p2.x ) 1030 return NSOrderedAscending;*/ 1031 return NSOrderedSame; 1032 } 1033 return NSOrderedDescending; 1034 } 1035 else 1036 { 1037 if ( sort==SORT_ROW_URLL || sort==SORT_ROW_LRUL ) 1038 { 1039 if ( p1.y < p2.y ) 1040 return (sort==SORT_ROW_URLL) ? NSOrderedDescending : NSOrderedAscending; 1041 if ( p1.y == p2.y ) 1042 { 1043 if ( p1.x > p2.x ) 1044 return NSOrderedAscending; 1045 if ( p1.x < p2.x ) 1046 return NSOrderedDescending; 1047 return NSOrderedSame; 1048 } 1049 return (sort==SORT_ROW_URLL) ? NSOrderedAscending : NSOrderedDescending; 1050 } 1051 if ( p1.x /*+ TOLERANCE*/ > p2.x ) 1052 return NSOrderedAscending; 1053 if ( Diff(p1.x, p2.x) <= TOLERANCE ) 1054 { 1055 if ( p1.y < p2.y ) 1056 return (sort==SORT_COL_LRUL) ? NSOrderedAscending : NSOrderedDescending; 1057 if ( p1.y > p2.y ) 1058 return (sort==SORT_COL_LRUL) ? NSOrderedDescending : NSOrderedAscending; 1059 /*if ( p1.x > p2.x ) 1060 return NSOrderedAscending;*/ 1061 return NSOrderedSame; 1062 } 1063 return NSOrderedDescending; 1064 } 1065} 1066 1067/* string to array 1068 * sort into textGraphics (ordered as it is, ordered from UL to LR, ordered from LR to UL) 1069 */ 1070- (void)importASCII:(NSString*)string sort:(int)sort 1071{ NSScanner *scanner = [NSScanner scannerWithString:string]; 1072 NSCharacterSet *skipSet = [NSCharacterSet characterSetWithCharactersInString:@" \n\r"]; 1073 NSMutableArray *array = [NSMutableArray array], *textArray = [NSMutableArray array]; 1074 NSString *str; 1075 int l, cnt = [layerList count], i, iCnt; 1076 1077 /* TAB -> use TAB as separator, not ' ' */ 1078 if ([string rangeOfString:@"\t"].length != 0) 1079 skipSet = [NSCharacterSet characterSetWithCharactersInString:@"\t\n\r"]; 1080 [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@""]]; 1081 [scanner scanCharactersFromSet:skipSet intoString:NULL]; 1082 while ( ![scanner isAtEnd] ) 1083 { int location = [scanner scanLocation]; 1084 1085 /* up to location != ' ' */ 1086 if ( ![scanner scanUpToCharactersFromSet:skipSet intoString:&str] ) 1087 str = @""; 1088 /* '"' -> scan up to '"' */ 1089 if ( [str hasPrefix:@"\""] ) 1090 { 1091 [scanner setScanLocation:location+1]; 1092 if ( ![scanner scanUpToString:@"\"" intoString:&str] ) 1093 str = @""; 1094 [scanner scanString:@"\"" intoString:NULL]; 1095 } 1096 str = [str stringByReplacing:@"\\n" by:@"\n"]; 1097 str = [str stringByReplacing:@"\\t" by:@"\t"]; 1098 [scanner scanCharactersFromSet:skipSet intoString:NULL]; 1099 [array addObject:str]; 1100 } 1101 1102 for ( l=0; l<cnt; l++ ) 1103 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 1104 1105 if ( ![[layerList objectAtIndex:l] editable] ) 1106 continue; 1107 for ( i=0, iCnt=[list count]; i<iCnt; i++ ) 1108 { id g = [list objectAtIndex:i]; 1109 1110 if ( ![g respondsToSelector:@selector(replaceTextWithString:)] || [g isSerialNumber] || [g isLocked] ) 1111 continue; 1112 [textArray addObject:g]; 1113 [[layerList objectAtIndex:l] setDirty:YES calculate:NO]; 1114 } 1115 } 1116 1117 [textArray sortUsingFunction:sortPosition context:&sort]; 1118 1119 for ( i=0, iCnt=[textArray count]; i<iCnt && i<(int)[array count]; i++ ) 1120 [[textArray objectAtIndex:i] replaceTextWithString:[array objectAtIndex:i]]; 1121 1122 [document setDirty:YES]; 1123 [self drawAndDisplay]; 1124} 1125 1126- (void)moveSelectionToLayer:(int)index 1127{ int l, i; 1128 LayerObject *targetLayer = [layerList objectAtIndex:index]; 1129 NSMutableArray *targetSList = [slayList objectAtIndex:index]; 1130 1131 if (![targetLayer editable]) 1132 return; 1133 1134 for (l=[slayList count]-1; l>=0; l--) 1135 { NSMutableArray *slist = [slayList objectAtIndex:l]; 1136 LayerObject *layerObject = [layerList objectAtIndex:l]; 1137 1138 if (l == index || ![layerObject editable]) 1139 continue; 1140 for (i=[slist count]-1; i>=0; i--) // remove from old layer, set objects dirty 1141 { [layerObject removeObject:[slist objectAtIndex:i]]; 1142 [[slist objectAtIndex:i] setDirty:YES]; 1143 } 1144 [targetLayer addObjectsFromArray:slist]; // add to new layer 1145 [targetSList addObjectsFromArray:slist]; // add to selection list of new layer 1146 [slist removeAllObjects]; 1147 } 1148 1149 [document setDirty:YES]; 1150 [self drawAndDisplay]; 1151} 1152 1153- (void)setAllLayerDirty:(BOOL)flag 1154{ int l, layCnt; 1155 1156 layCnt = [layerList count]; 1157 for (l=0; l<layCnt; l++) 1158 { LayerObject *layerObject = [layerList objectAtIndex:l]; 1159 1160 if ([layerObject type] != LAYER_CLIPPING) 1161 [layerObject setDirty:YES calculate:NO]; 1162 } 1163 [document setDirty:YES]; 1164} 1165 1166- (VCrosshairs*)origin 1167{ 1168 return origin; 1169} 1170 1171/* convert point to and from virtual origin 1172 */ 1173- (NSPoint)pointRelativeOrigin:(NSPoint)p 1174{ NSPoint offset = [origin pointWithNum:0]; 1175 1176 p.x -= offset.x; 1177 p.y -= offset.y; 1178 return p; 1179} 1180- (NSPoint)pointAbsolute:(NSPoint)p 1181{ NSPoint offset = [origin pointWithNum:0]; 1182 1183 p.x += offset.x; 1184 p.y += offset.y; 1185 return p; 1186} 1187 1188- (id)clipObject 1189{ int l, i; 1190 1191 for ( l=0; l<(int)[layerList count]; l++ ) 1192 if ( [(LayerObject*)[layerList objectAtIndex:l] type] == LAYER_CLIPPING ) 1193 { LayerObject *layer = [layerList objectAtIndex:l]; 1194 NSMutableArray *cList = [layer list]; 1195 1196 if ( [cList count]>1 || ([cList count] && ![[cList objectAtIndex:0] isKindOfClass:[VRectangle class]]) ) 1197 { 1198 NSRunAlertPanel(@"", LAYERONLYFORRECTANGLE_STRING, OK_STRING, nil, nil); 1199 if ([cList count]>1) 1200 for (i=[cList count]-1; i>=1; i--) 1201 [layer removeObject:[cList objectAtIndex:i]]; 1202 else 1203 [layer removeObject:[cList objectAtIndex:0]]; 1204 return nil; 1205 } 1206 else if ( [cList count] ) 1207 return [cList objectAtIndex:0]; 1208 } 1209 1210 return nil; 1211} 1212 1213- (int)indexOfSelectedLayer { return indexOfSelectedLayer; } 1214- (void)selectLayerAtIndex:(int)ix { indexOfSelectedLayer = ix; } 1215 1216- (int)layerIndexOfGraphic:(VGraphic*)g 1217{ int l; 1218 1219 for ( l=[layerList count]-1; l>=0; l-- ) 1220 if ( [[[layerList objectAtIndex:l] list] containsObject:g] ) 1221 return l; 1222 NSLog(@"Graphic %@ not contained on any layer", [g title]); 1223 return -1; 1224} 1225- (LayerObject*)layerOfGraphic:(VGraphic*)g 1226{ int l; 1227 1228 if ([g isKindOfClass:[VCrosshairs class]]) 1229 return nil; 1230 for ( l=[layerList count]-1; l>=0; l-- ) 1231 if ( [[[layerList objectAtIndex:l] list] containsObject:g] ) 1232 return [layerList objectAtIndex:l]; 1233 NSLog(@"Graphic %@ not contained on any layer", [g title]); 1234 return nil; 1235} 1236 1237/* created: 1996-04-26 1238 * modified: 1239 * purpose: remove object from list 1240 */ 1241- (void)removeGraphic:g 1242{ int l; 1243 1244 for (l=[slayList count]-1; l>=0; l--) 1245 { LayerObject *layer = [layerList objectAtIndex:l]; 1246 NSMutableArray *slist = [slayList objectAtIndex:l]; 1247 1248 if (![[layer list] count] || ![layer editable]) 1249 continue; 1250 [layer removeObject:g]; 1251 [slist removeObject:g]; 1252 } 1253} 1254 1255/* 1256 * Resets slayList by going through the layerlist and locating all the Graphics 1257 * which respond YES to the isSelected method. 1258 */ 1259- (void)getSelection 1260{ int i, iCnt, l; 1261 VGraphic *graphic; 1262 1263 [slayList removeAllObjects]; 1264 for (l=0; l<(int)[layerList count]; l++) 1265 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 1266 NSMutableArray *slist = [NSMutableArray array]; 1267 1268 [slayList addObject:slist]; 1269 if (![(LayerObject*)[layerList objectAtIndex:l] state] || !(iCnt=[list count])) 1270 continue; 1271 for (i=0; i<iCnt; i++) 1272 { graphic = [list objectAtIndex:i]; 1273 if ([graphic isSelected]) 1274 [slist addObject:graphic]; 1275 } 1276 } 1277} 1278 1279/* 1280 * Returns the size of the control point scaled to reflect the 1281 * current scale. If the scaling were not done, a control point 1282 * would look like the USS Enterprise at 400%. (The aircraft 1283 * carrier.) 1284 * 1285 */ 1286- (float)controlPointSize 1287{ 1288 return KNOBSIZE * (1.0/scale); 1289} 1290 1291- (float)scaleFactor 1292{ 1293 return scale; 1294} 1295 1296 1297- (void)drawControls:(NSRect)rect 1298{ int l, i; 1299 1300 for ( l=[layerList count]-1; l>=0; l-- ) 1301 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 1302 1303 if ( ![list count] ) 1304 continue; 1305 for ( i=[list count]-1; i>=0; i-- ) 1306 { [[list objectAtIndex:i] drawKnobs:rect direct:NO scaleFactor:scale]; 1307 [[list objectAtIndex:i] drawControls:rect direct:NO scaleFactor:scale]; 1308 } 1309 } 1310 [VGraphic showFastKnobFills]; 1311} 1312 1313/* 1314 * drawSelf: composite cache and draw knobs 1315 * modified: 2012-04-13 (centerScanRect back again) 1316 * 2012-02-13 (centerScanRect removed as it destroys the result) 1317 * 2005-11-14 (apple workaround for composite) 1318 */ 1319- (void)drawRect:(NSRect)rect 1320{ NSRect r, vRect; 1321 1322 if ( !VHFIsDrawingToScreen() ) // we are printing 1323 { [self draw:rect]; // draw only the graphic objects 1324 return; 1325 } 1326 1327 if (NSIsEmptyRect(rect)) 1328 rect = [self bounds]; 1329 1330 vRect = [self visibleRect]; // limit redraw to visible area 1331 r = rect; 1332 r = NSIntersectionRect(vRect, r); 1333 if (cache) // copy cache 1334 { NSPoint toP; 1335 1336 r.origin.x = floor(r.origin.x); // 2012-02-13 1337 r.origin.y = floor(r.origin.y); 1338 r.size.width = ceil(r.size.width) + 1.0; 1339 r.size.height = ceil(r.size.height) + 1.0; 1340 r = [self centerScanRect:r]; // Das ist die Loesung gegen ein "Pixel-Versetzt"-Geschmoddel 1341 toP = r.origin; // destination point 1342#ifdef __APPLE__ // workaround to fix scaling of the composite source rectangle (frame) 1343 r.origin.x *= scale; 1344 r.origin.y *= scale; 1345 r.size.width *= scale; 1346 r.size.height *= scale; 1347#endif 1348 NSCopyBits([cacheView gState], r, toP); 1349 } 1350 else // draw directly to screen 1351 { 1352 [self draw:r]; 1353 } 1354 1355 /* module: draw stuff outside of cache windows (decoration) */ 1356 [[NSNotificationCenter defaultCenter] postNotificationName:DocViewDrawDecoration 1357 object:self userInfo:nil]; 1358 1359 /* draw control points and lines */ 1360 if ( VHFIsDrawingToScreen() && !scrolling ) 1361 [self drawControls:rect]; 1362 1363 /* FIXME, Apple: if cache is disabled, autodisplay makes everything redraw endlessly 1364 * probably this is related to text drawing 1365 * needsDisplay is always = YES, no matter what 1366 */ 1367 if ( ! cache ) 1368 { 1369/*# ifdef __APPLE__ // workaround for touch events flooding us with displayIfNeeded requests (SysDefined, subtype = 7) 1370 NSEvent *event = [[self window] currentEvent]; 1371 if ( [event type] == NSSystemDefined && [event subtype] == 7 ) // NSTouchPhaseBegan|NSTouchPhaseMoved|NSTouchPhaseStationary ) 1372 { 1373 printf("DocView -drawRect: %s, event = %s, rect = {%.0f %.0f %.0f %.0f}\n", 1374 [[self description] UTF8String], 1375 [[event description] UTF8String], 1376 rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); 1377 return; 1378 } 1379# endif*/ 1380 /*printf("DocView -drawRect: %s, event = %s, rect = {%.0f %.0f %.0f %.0f}\n", 1381 [[self description] UTF8String], 1382 [[[[self window] currentEvent] description] UTF8String], 1383 rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);*/ 1384 //printf("DocView -drawRect: needsDisplay=%d\n", [self needsDisplay]); 1385 [self setNeedsDisplayInRect:NSZeroRect]; 1386 [self setNeedsDisplay:NO]; // we just did, but this method is doing nothing !? 1387 //printf(" DocView -drawRect: needsDisplay=%d\n", [self needsDisplay]); 1388 } 1389} 1390 1391/* this only updates the controls, but doesn't redraw the cache 1392 */ 1393- (void)flatRedraw:(NSRect)rect 1394{ 1395 if (redrawEntireView) // something is too large to keep control over the size 1396 [self cache:rect]; 1397 else 1398 { 1399 [self lockFocus]; 1400 [self drawRect:rect]; 1401 [self unlockFocus]; 1402 [[self window] flushWindow]; 1403 } 1404} 1405 1406- (void)cache:(NSRect)rect 1407{ 1408 if ( [self window] && [[self window] windowNumber] >= 0 ) 1409 { 1410 if (redrawEntireView) 1411 rect = [self bounds]; 1412 if (cache) 1413 [self draw:rect]; 1414 [self lockFocus]; 1415 [self drawRect:rect]; 1416 [self unlockFocus]; 1417 [[self window] flushWindow]; 1418 } 1419} 1420 1421- (void)drawAndDisplay 1422{ 1423 [self cache:[self bounds]]; 1424} 1425 1426- (void)cacheGraphic:(VGraphic*)g 1427{ 1428 [((cache && VHFIsDrawingToScreen()) ? cacheView : self) lockFocus]; 1429 [g drawWithPrincipal:self]; 1430 //[VGraphic showFastKnobFills]; 1431 [((cache && VHFIsDrawingToScreen()) ? cacheView : self) unlockFocus]; 1432} 1433 1434- (BOOL)displayGraphic { return displayGraphic; } 1435- (BOOL)mustDrawPale { return mustDrawPale; } 1436- (void)setRedrawEntireView:(BOOL)flag { redrawEntireView = flag; return; } 1437- (BOOL)redrawEntireView { return redrawEntireView; } 1438 1439/* return template layer 1440 * created: 2005-09-01 1441 */ 1442- (LayerObject*)template:(LayerType)layerType 1443{ int l; 1444 1445 for (l=0; l<(int)[layerList count]; l++) 1446 { LayerObject *layerObject = [layerList objectAtIndex:l]; 1447 1448 if ([layerObject type] == layerType) 1449 return layerObject; 1450 } 1451 return nil; 1452} 1453/* return template object with name and template 1454 * created: 2005-11-07 (cast g to VText) 1455 * modified: 2009-03-19 (check for layerObject == nil) 1456 */ 1457- (VGraphic*)templateObjectWithName:(NSString*)name 1458 fromTemplate:(LayerObject*)layerObject 1459{ int i; 1460 NSArray *list = [layerObject list]; 1461 1462 if (!layerObject) 1463 return nil; 1464 for (i=0; i<(int)[list count]; i++) 1465 { VGraphic *g = [list objectAtIndex:i]; 1466 1467 if ([g isKindOfClass:[VText class]]) 1468 { VText *text = (VText*)g; 1469 NSRange range = [[text string] rangeOfString:name]; 1470 1471 if (range.length) 1472 return [[text copy] autorelease]; 1473 /*{ NSMutableString *string = [[[text string] mutableCopy] autorelease]; 1474 1475 text = [[text copy] autorelease]; 1476 [string replaceCharactersInRange:range withString:string]; 1477 [text replaceTextWithString:string]; 1478 return text; 1479 }*/ 1480 } 1481 } 1482 return nil; 1483} 1484 1485- (void)drawTemplate:(LayerObject*)template forLayer:(LayerObject*)layerObject 1486{ VText *text; 1487 int i; 1488 1489 /* Set Page Number and Count */ 1490 if ( (text = (VText*)[self templateObjectWithName:@"#PAGENUM#" fromTemplate:template]) ) 1491 { 1492 text = [[text copy] autorelease]; 1493 [text replaceSubstring:@"#PAGENUM#" withString:[layerObject string]]; 1494 [text replaceSubstring:@"#PAGECNT#" 1495 withString:[NSString stringWithFormat:@"%d", [self pageCount]]]; 1496 [text drawWithPrincipal:self]; 1497 } 1498 /* Set Date */ 1499 if ( (text = (VText*)[self templateObjectWithName:@"#DATE_" fromTemplate:template]) ) 1500 { NSString *str = [text string]; 1501 NSRange range1, range2; 1502 1503 range1 = [str rangeOfString:@"#DATE_"]; 1504 if (range1.length) 1505 range2 = [str rangeOfString:@"#" options:0 range:NSMakeRange(range1.location+1, [str length]-range1.location-1)]; 1506 if (range1.length && range2.length) 1507 { NSString *dateFormat = [str substringWithRange:NSMakeRange(range1.location+range1.length, range2.location-(range1.location+range1.length))]; 1508 NSString *dateStr = [[NSCalendarDate date] descriptionWithCalendarFormat:dateFormat]; 1509 1510 if (!dateStr) 1511 dateStr = [NSString stringWithFormat: @"Illegal date format: '%@'", dateFormat]; 1512 text = [[text copy] autorelease]; 1513 //str = [str substringWithRange:NSMakeRange(range1.location, range2.location+1-range1.location)]; 1514 //[text replaceSubstring:str withString:dateStr]; 1515 [text replaceCharactersInRange:NSMakeRange(range1.location, range2.location+1-range1.location) withString:dateStr]; 1516 [text drawWithPrincipal:self]; 1517 } 1518 } 1519 /* Draw other elements */ 1520 if ( [template type] == LAYER_TEMPLATE_1 || [template type] == LAYER_TEMPLATE_2 ) // for even/odd template only 1521 { 1522 for ( i=0; i<(int)[[template list] count]; i++ ) 1523 { VGraphic *g = [[template list] objectAtIndex:i]; 1524 1525 if ([g isKindOfClass:[VText class]]) 1526 { NSString *string = [(VText*)g string]; 1527 1528 if ( [string rangeOfString:@"#PAGE"].length || 1529 [string rangeOfString:@"#DATE_"].length ) 1530 continue; 1531 } 1532 [g drawWithPrincipal:self]; 1533 } 1534 } 1535} 1536 1537/* redraw cache contents or draw for printing 1538 * modified: 2009-13-19 (display #DATE_...#, display non-template elements on even/odd template layer) 1539 */ 1540- (void)draw:(NSRect)rect 1541{ int j, l, lCnt; 1542 1543 if (cache && VHFIsDrawingToScreen()) 1544 [cacheView lockFocus]; 1545 else 1546 [self lockFocus]; 1547 1548 VHFSetAntialiasing(Prefs_Antialias); 1549 1550 if (NSIsEmptyRect(rect)) 1551 rect = [self bounds]; 1552 1553 if (VHFIsDrawingToScreen()) 1554 { 1555 if (backgroundColor) 1556 { [backgroundColor set]; 1557 NSRectFill(rect); 1558 } 1559 else 1560 NSEraseRect(rect); 1561 } 1562 1563 NSRectClip(rect); 1564 1565 [self drawGrid]; 1566 1567 /* display graphic */ 1568 [NSBezierPath setDefaultLineWidth:(VHFIsDrawingToScreen() ? 1.0/scale : 0.1)]; 1569 if ( displayGraphic ) 1570 { int templateIx, templateIxOdd, templateIxEven; 1571 1572 templateIx = templateIxOdd = templateIxEven = [layerList count]; 1573 1574 if ( drawPale ) // set by modules, if we have to draw the graphic objects in pale colors 1575 mustDrawPale = YES; 1576 for ( l=0, lCnt=[layerList count]; l<lCnt; l++ ) 1577 { LayerObject *layerObject = [layerList objectAtIndex:l]; 1578 int pageNum = [[layerObject string] intValue]; // Note: this works only with real page numbers 1579 1580 if ( [layerObject invisible] == YES ) 1581 continue; // we dont draw graphics 1582 1583 if ( [layerObject type] == LAYER_TEMPLATE ) 1584 templateIx = l; 1585 else if ( [layerObject type] == LAYER_TEMPLATE_1 ) 1586 templateIxOdd = l; // needed to draw templates in correct order for the pages 1587 else if ( [layerObject type] == LAYER_TEMPLATE_2 ) 1588 templateIxEven = l; 1589 1590 /* draw template before page content */ 1591 if ([layerObject state] && [layerObject type] == LAYER_PAGE) 1592 { 1593 if (templateIx < l) 1594 [self drawTemplate:[self template:LAYER_TEMPLATE] forLayer:layerObject]; 1595 if (pageNum%2 != 0 && templateIxOdd < l) 1596 [self drawTemplate:[self template:LAYER_TEMPLATE_1] forLayer:layerObject]; 1597 if (pageNum%2 == 0 && templateIxEven < l) 1598 [self drawTemplate:[self template:LAYER_TEMPLATE_2] forLayer:layerObject]; 1599 } 1600 1601 [layerObject draw:rect inView:self]; 1602 1603 /* draw template after page content */ 1604 if ([layerObject state] && [layerObject type] == LAYER_PAGE) 1605 { 1606 if (templateIx > l) 1607 [self drawTemplate:[self template:LAYER_TEMPLATE] forLayer:layerObject]; 1608 if (pageNum%2 != 0 && templateIxOdd > l) 1609 [self drawTemplate:[self template:LAYER_TEMPLATE_1] forLayer:layerObject]; 1610 if (pageNum%2 == 0 && templateIxEven > l) 1611 [self drawTemplate:[self template:LAYER_TEMPLATE_2] forLayer:layerObject]; 1612 } 1613 } 1614 mustDrawPale = NO; 1615 } 1616 1617 /* draw additional stuff in modules */ 1618 [NSBezierPath setDefaultLineWidth:(VHFIsDrawingToScreen() ? 1.0/scale : 0.1)]; 1619 [[NSNotificationCenter defaultCenter] postNotificationName:DocViewDrawGraphicAdditions 1620 object:self userInfo:nil]; 1621 1622 /* batch production */ 1623 if ( tileOriginList && [tileOriginList count] ) 1624 { int cnt = [tileOriginList count]; 1625 1626 if (displayGraphic) // draw the rectangles only once 1627 { NSPoint masterP = [(TileObject*)[tileOriginList objectAtIndex:0] position]; 1628 1629 PSgsave(); 1630 [[NSColor blackColor] set]; 1631 [NSBezierPath setDefaultLineWidth:(VHFIsDrawingToScreen() ? 1.0/scale : 0.1)]; 1632 for (j=1; j<cnt; j++) 1633 { TileObject *obj = [tileOriginList objectAtIndex:j]; 1634 NSPoint objP = [obj position], p; 1635 1636 p = NSMakePoint(objP.x-masterP.x, objP.y-masterP.y); 1637 1638 /* draw rectangles for all tiles */ 1639 if (VHFIsDrawingToScreen()) // screen 1640 { 1641 [NSBezierPath strokeRect:NSMakeRect(objP.x, objP.y, tileSize.width, tileSize.height)]; 1642 //NSFrameRectWithWidth(NSMakeRect(objP.x, objP.y, tileSize.width, tileSize.height), 1.0/scale); 1643 } 1644 /* draw tiles */ 1645 else // printing 1646 { 1647#if defined(__APPLE__) || defined(GNUSTEP_BASE_VERSION) 1648 NSAffineTransform *xform = [NSAffineTransform transform]; 1649 PSgsave(); 1650 //p1 = [self convertPoint:p fromView:nil]; 1651 //[self setBoundsOrigin:NSMakePoint(-p1.x, -p1.y)]; // this fails with window size < document 1652 [xform translateXBy:p.x yBy:p.y]; 1653 [xform concat]; 1654#else // OpenStep 1655 PSgsave(); 1656 PStranslate(p.x, p.y); 1657#endif 1658 for ( l=0, lCnt=[layerList count]; l<lCnt; l++ ) 1659 { LayerObject *layerObject = [layerList objectAtIndex:l]; 1660 NSArray *list = [layerObject list]; 1661 int i, liCnt; 1662 1663 if ( [layerObject invisible] == YES ) 1664 continue; 1665 1666 if ([layerObject state] && [layerObject useForTile]) 1667 { 1668 for (i=0, liCnt=[list count]; i<liCnt; i++) 1669 { id g = [list objectAtIndex:i]; 1670 1671 if ([g respondsToSelector:@selector(isSerialNumber)] && [g isSerialNumber]) 1672 ; // [g drawSerialNumberAt:p withOffset:j]; 1673 else 1674 [g drawWithPrincipal:self]; 1675 } 1676 } 1677 } 1678//#ifdef __APPLE__ 1679 [self setBoundsOrigin:NSZeroPoint]; 1680//#endif 1681 PSgrestore(); 1682 } 1683 [serialNumber drawSerialNumberAt:p withOffset:j]; 1684 } 1685 PSgrestore(); 1686 } 1687 /* draw additional batch stuff in modules */ 1688 [[NSNotificationCenter defaultCenter] postNotificationName:DocViewDrawBatchAdditions 1689 object:self userInfo:nil]; 1690 } 1691 1692 /* origin - crosshairs */ 1693 if ( VHFIsDrawingToScreen() ) 1694 [origin drawWithPrincipal:self]; 1695 1696 if (cache && VHFIsDrawingToScreen()) 1697 [cacheView unlockFocus]; 1698 else 1699 [self unlockFocus]; 1700} 1701 1702 1703/* 1704 * Places the graphic centered at the given location on the page. 1705 */ 1706- (BOOL)placeGraphic:(VGraphic*)graphic at:(NSPoint)location 1707{ NSPoint offset; 1708 NSRect bbox; 1709 id change; 1710 1711 if ( graphic ) 1712 { 1713 [self deselectAll:self]; 1714 1715 bbox = [graphic extendedBoundsWithScale:[self scaleFactor]]; 1716 offset.x = location.x - bbox.origin.x - bbox.size.width/2.0; 1717 offset.y = location.y - bbox.origin.y - bbox.size.height/2.0; 1718 1719 [graphic moveBy:offset]; 1720 1721 change = [[CreateGraphicsChange alloc] initGraphicView:self graphic:graphic]; 1722 [change startChangeIn:self]; 1723 [graphic setSelected:YES]; 1724 [self insertGraphic:graphic]; 1725 [change endChange]; 1726 } 1727 1728 return YES; 1729} 1730 1731/* 1732 * Places the graphic centered at the given location on the page. 1733 */ 1734- (BOOL)placeList:(NSMutableArray*)aList at:(NSPoint)location 1735{ NSPoint offset; 1736 NSRect bbox; 1737 int i, l; 1738 1739 if (aList && [aList count]) 1740 { 1741 bbox = [self boundsOfArray:aList withKnobs:NO]; 1742 offset.x = location.x - bbox.origin.x - bbox.size.width/2.0; 1743 offset.y = location.y - bbox.origin.y - bbox.size.height/2.0; 1744 for (l=[layerList count]-1; l>=0; l--) 1745 { LayerObject *layerObject = [layerList objectAtIndex:l]; 1746 NSMutableArray *slist = [slayList objectAtIndex:l]; 1747 1748 [slist removeAllObjects]; 1749 1750 if (![(NSArray*)[aList objectAtIndex:l] count]) 1751 continue; 1752 1753 [[layerObject list] makeObjectsPerformSelector:@selector(moveBy:) withObject:(id)&offset]; 1754 1755 for (i=[(NSArray*)[aList objectAtIndex:l] count]-1; i>=0; i--) 1756 { VGraphic *g = [[aList objectAtIndex:l] objectAtIndex:i]; 1757 1758 [g setSelected:YES]; 1759 [layerObject addObject:g]; // push all objects to the 1st editable layer 1760 [slist addObject:g]; 1761 } 1762 } 1763 1764 bbox = [self boundsOfArray:aList withKnobs:YES]; 1765 [self cache:bbox]; 1766 1767 [aList release]; 1768 } 1769 1770 return YES; 1771} 1772 1773- (NSRect)boundsOfArray:(NSArray*)list 1774{ 1775 return [self boundsOfArray:list withKnobs:YES]; 1776} 1777- (NSRect)boundsOfArray:(NSArray*)list withKnobs:(BOOL)knobs 1778{ int i, l; 1779 NSRect rect, bbox = NSZeroRect; 1780 1781 if ( ![list count] ) 1782 return bbox; 1783 1784 /* layer list */ 1785 if ( [[list objectAtIndex:0] isKindOfClass:[LayerObject class]] ) 1786 { 1787 for (l=[list count]-1; l>=0; l--) 1788 { 1789 if ( [[[list objectAtIndex:l] list] count] ) 1790 { 1791 rect = [self boundsOfArray:[[list objectAtIndex:l] list] withKnobs:knobs]; 1792 bbox = (!bbox.size.width) ? rect : NSUnionRect(rect, bbox); 1793 } 1794 } 1795 return bbox; 1796 } 1797 /* slayList */ 1798 else if ( [[list objectAtIndex:0] isKindOfClass:[NSMutableArray class]] ) 1799 { 1800 for (l=[list count]-1; l>=0; l--) 1801 { 1802 if ( [(NSArray*)[list objectAtIndex:l] count] ) 1803 { 1804 rect = [self boundsOfArray:[list objectAtIndex:l] withKnobs:knobs]; 1805 bbox = (!bbox.size.width) ? rect : NSUnionRect(rect, bbox); 1806 } 1807 } 1808 return bbox; 1809 } 1810 1811 /* graphic list */ 1812 if (knobs) 1813 bbox = [[list objectAtIndex:0] extendedBoundsWithScale:[self scaleFactor]]; 1814 else 1815 bbox = [[list objectAtIndex:0] bounds]; 1816 for (i=[list count]-1; i>0; i--) 1817 { 1818 if (knobs) 1819 rect = [[list objectAtIndex:i] extendedBoundsWithScale:[self scaleFactor]]; 1820 else 1821 rect = [[list objectAtIndex:i] bounds]; 1822 bbox = NSUnionRect(rect, bbox); 1823 } 1824 1825 return bbox; 1826} 1827- (NSRect)coordBoundsOfArray:(NSArray*)list 1828{ int i, l; 1829 NSRect rect, bbox = NSZeroRect; 1830 1831 if ( ![list count] ) 1832 return bbox; 1833 1834 /* layer list */ 1835 if ( [[list objectAtIndex:0] isKindOfClass:[LayerObject class]] ) 1836 { 1837 for (l=[list count]-1; l>=0; l--) 1838 { 1839 if ( [[[list objectAtIndex:l] list] count] ) 1840 { 1841 rect = [self coordBoundsOfArray:[[list objectAtIndex:l] list]]; 1842 bbox = (!bbox.size.width) ? rect : NSUnionRect(rect, bbox); 1843 } 1844 } 1845 return bbox; 1846 } 1847 /* slayList */ 1848 else if ( [[list objectAtIndex:0] isKindOfClass:[NSMutableArray class]] ) 1849 { 1850 for (l=[list count]-1; l>=0; l--) 1851 { 1852 if ( [(NSArray*)[list objectAtIndex:l] count] ) 1853 { 1854 rect = [self coordBoundsOfArray:[list objectAtIndex:l]]; 1855 bbox = (!bbox.size.width) ? rect : NSUnionRect(rect, bbox); 1856 } 1857 } 1858 return bbox; 1859 } 1860 /* graphic list */ 1861 bbox = [[list objectAtIndex:0] coordBounds]; 1862 for (i=[list count]-1; i>0; i--) 1863 { 1864 rect = [[list objectAtIndex:i] coordBounds]; 1865 bbox = VHFUnionRect(rect, bbox); 1866 } 1867 1868 return bbox; 1869} 1870 1871- (void)scrollPointToVisible:(NSPoint)point 1872{ NSRect r; 1873 float tol = 5.0 / [self scaleFactor]; 1874 1875 r.origin.x = point.x - tol; 1876 r.origin.y = point.y - tol; 1877 r.size.width = r.size.height = 2*tol; 1878 1879 [self scrollRectToVisible:r]; 1880} 1881 1882/* 1883 * Scrolls to rectangle passed in if it is not in visible portion of the view. 1884 * If the rectangle is larger in width or height than the view, the scrollRectToVisible 1885 * method is not altogether consistent. As a result, the rectangle contains only 1886 * the image that was previously visible. 1887 */ 1888- (void)scrollToRect:(NSRect)toRect 1889{ NSRect visRect; 1890 1891 visRect = [self visibleRect]; 1892 if (!NSContainsRect(visRect , toRect)) 1893 { 1894 scrolling = YES; 1895 [[self window] disableFlushWindow]; 1896 [self scrollRectToVisible:toRect]; 1897 [[self window] enableFlushWindow]; 1898 scrolling = NO; 1899 1900 startTimer(&inTimerLoop); 1901 } 1902 else 1903 stopTimer(&inTimerLoop); 1904} 1905 1906/* 1907 * Constrain the point within the view. An offset is needed because when 1908 * an object is moved, it is often grabbed in the center of the object. If the 1909 * lower left offset and the upper right offset were not included then part of 1910 * the object could be moved off of the view. (In some applications, that might 1911 * be allowed but in this one the object is constrained to always lie in the 1912 * page.) 1913 */ 1914- (void)constrainPoint:(NSPoint *)aPt withOffset:(const NSSize*)llOffset :(const NSSize*)urOffset 1915{ NSPoint viewMin, viewMax; 1916 1917 viewMin.x = [self bounds].origin.x + llOffset->width; 1918 viewMin.y = [self bounds].origin.y + llOffset->height; 1919 1920 viewMax.x = [self bounds].origin.x + [self bounds].size.width - urOffset->width; 1921 viewMax.y = [self bounds].origin.y + [self bounds].size.height - urOffset->height; 1922 1923 aPt->x = MAX(viewMin.x, aPt->x); 1924 aPt->y = MAX(viewMin.y, aPt->y); 1925 1926 aPt->x = MIN(viewMax.x, aPt->x); 1927 aPt->y = MIN(viewMax.y, aPt->y); 1928} 1929 1930/* 1931 * Constrain a rectangle within the view. 1932 */ 1933- (void)constrainRect:(NSRect *)aRect 1934{ NSPoint viewMin, viewMax; 1935 1936 viewMin.x = [self bounds].origin.x; 1937 viewMin.y = [self bounds].origin.y; 1938 1939 viewMax.x = [self bounds].origin.x + [self bounds].size.width - aRect->size.width; 1940 viewMax.y = [self bounds].origin.y + [self bounds].size.height - aRect->size.height; 1941 1942 aRect->origin.x = MAX(viewMin.x, aRect->origin.x); 1943 aRect->origin.y = MAX(viewMin.y, aRect->origin.y); 1944 1945 aRect->origin.x = MIN(viewMax.x, aRect->origin.x ); 1946 aRect->origin.y = MIN(viewMax.y, aRect->origin.y); 1947} 1948 1949/* snap *p to point 1950 * return hit point in *p 1951 * 1952 * created: 1996-10-02 1953 * modified: 2012-02-13 1954 */ 1955- (BOOL)hitEdge:(NSPoint*)p spare:obj 1956{ int l, i; 1957 float snap = Prefs_Snap / [self scaleFactor]; 1958 float controlPointSize = [self controlPointSize]; 1959 BOOL gotHit = NO; 1960 NSPoint hitP = *p; 1961 double sqrDistBest = MAXFLOAT; 1962 1963 if (!snap) 1964 return NO; 1965 for (l=[layerList count]-1; l>=0; l--) 1966 { LayerObject *layerObj = [layerList objectAtIndex:l]; 1967 NSMutableArray *list = [layerObj list]; 1968 1969 //if ( ![layerObj state] || ![list count] ) 1970 // continue; 1971 if ( ![list count] ) 1972 continue; 1973 for ( i=[list count]-1; i>=0; i-- ) 1974 { VGraphic *g = [list objectAtIndex:i]; 1975 NSPoint snapPoint; 1976 1977 if ( [g hitEdge:*p fuzz:snap :&snapPoint :controlPointSize] && g != obj ) 1978 { //hitP = snapPoint; 1979 /* if we have more than one hit, we have to get the closest one ! */ 1980 if ( SqrDistPoints(snapPoint, *p) < sqrDistBest ) 1981 { hitP = snapPoint; 1982 sqrDistBest = SqrDistPoints(snapPoint, *p); 1983 gotHit = YES; 1984 } 1985 } 1986 } 1987 } 1988 if (gotHit) 1989 { vhfPlaySound(@"Pop"); 1990 *p = hitP; 1991 return YES; 1992 } 1993 return NO; 1994} 1995 1996/* 1997 * Redraws the graphic. The image from the alpha buffer is composited 1998 * into the window and then the changed object is drawn atop the 1999 * old image. A copy of the image is necessary because when the 2000 * window is scrolled the alpha buffer is also scrolled. When the 2001 * alpha buffer is scrolled, the old image might have to be redrawn. 2002 * As a result, a copy is created and the changes performed on the 2003 * copy. Care is taken to limit the amount of area that must be 2004 * composited and redrawn. A timer is started is the scrolling rect 2005 * moves outside the visible portion of the view. 2006 * 2007 * alternate: horicontal or vertical constrain 2008 * control: ? 2009 * 2010 * modified: 2008-12-01 (FIXME: rect_draw_apple is a hack) 2011 * 2007-05-08 (apple workaround for NSCopyBits) 2012 */ 2013- (BOOL)redrawObject:(id)obj :(int)pt_num :(NSRect*)redrawRect 2014{ BOOL tracking = YES; 2015 NSPoint pt, pt_last, pt_old, delta, pt_start; 2016 NSRect rect_start, rect_now, rect_last, rect_scroll, rect_vis, rect_draw_apple; 2017 NSEvent *event; 2018 float snap = Prefs_Snap / [self scaleFactor]; // snap distance 2019 NSPoint snapPoint, p3; // the point to snap to 2020 BOOL alternate, control; 2021 BOOL horizConstrain = NO, vertConstrain = NO; 2022 BOOL doSnap = NO; 2023 int l; 2024 DocWindow *window = (DocWindow*)[self window]; 2025 id change; 2026 2027#if 0 2028 [DPSGetCurrentContext() setOutputTraced:YES]; 2029#endif 2030 2031 if ( [obj isLocked] ) 2032 return NO; 2033 2034 /* The rect_scroll will cause scrolling whenever it goes outside the 2035 * visible portion of the view. 2036 */ 2037 rect_vis = [self visibleRect]; 2038 rect_scroll = [obj scrollRect:pt_num inView:self]; 2039 rect_scroll = NSIntersectionRect(rect_vis, rect_scroll); 2040 2041 rect_now = rect_start = [obj extendedBoundsWithScale:[self scaleFactor]]; 2042 *redrawRect = rect_last = rect_now; 2043 2044 pt_last = [obj pointWithNum:pt_num]; 2045 pt_start = pt_old = pt_last; 2046 2047 event = [[self window] nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask]; 2048 alternate = ([event modifierFlags] & NSAlternateKeyMask) ? YES : NO; 2049 control = ([event modifierFlags] & NSControlKeyMask) ? YES : NO; 2050 2051 [window setAutodisplay:NO]; 2052 2053 if ([event type] != NSLeftMouseUp) 2054 { 2055 /* arc center (and curve pts 1 and 2) will move and noticed single ! */ 2056 if (pt_num != [obj selectedKnobIndex]) 2057 change = [[DragPointGraphicsChange alloc] initGraphicView:self graphic:obj]; 2058 else // all other pts will noticed if obj is selected and the selectedKnobIndex is set 2059 change = [[MovePointGraphicsChange alloc] initGraphicView:self ptNum:pt_num moveAll:NO]; 2060 [change startChange]; 2061 if (pt_num != [obj selectedKnobIndex]) 2062 [change setPointNum:pt_num]; 2063 2064 while (tracking) 2065 { 2066 /* If its a timer event than use the last point. It will be converted to 2067 * into the view's coordinate so it will appear as a new point. 2068 */ 2069 pt = ([event type] == NSPeriodic) ? pt_old : (pt_old = [event locationInWindow]); 2070 2071 pt = [self convertPoint:pt fromView:nil]; 2072 [obj constrainPoint:&pt andNumber:pt_num toView:self]; 2073 2074 delta.x = pt.x - pt_last.x; 2075 delta.y = pt.y - pt_last.y; 2076 2077 if (delta.x || delta.y) 2078 { 2079 /* vertical/horizontal constrain 2080 */ 2081 if (alternate) 2082 { if (ABS(delta.x) > ABS(delta.y)) 2083 horizConstrain = YES; 2084 else 2085 vertConstrain = YES; 2086 alternate = NO; 2087 } 2088 if (horizConstrain) 2089 delta.y = 0.0; 2090 else if (vertConstrain) 2091 delta.x = 0.0; 2092 2093 doSnap = NO; 2094 if (snap) /* snap to point */ 2095 { float controlsize = [self controlPointSize]; 2096 int i; 2097 NSPoint hitP = pt; 2098 double sqrDistBest = MAXFLOAT; 2099 2100 /* find closest point to mouse */ 2101 for (l=[layerList count]-1; l>=0; l--) 2102 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 2103 2104 if (![(LayerObject*)[layerList objectAtIndex:l] state] || ![list count]) 2105 continue; 2106 for (i=[list count]-1; i >= 0; i--) 2107 { VGraphic *g = [list objectAtIndex:i]; 2108 2109 if ( [g hitEdge:pt fuzz:snap :&snapPoint :controlsize] && 2110 (g != obj || (g == obj && ([g isKindOfClass:[VPolyLine class]] || 2111 [g isKindOfClass:[VPath class]] || 2112 [g isKindOfClass:[VArc class]] || 2113 [g isKindOfClass:[VCurve class]] ))) ) 2114 { doSnap = YES; 2115 if ( SqrDistPoints(snapPoint, pt) < sqrDistBest ) 2116 { hitP = snapPoint; 2117 sqrDistBest = SqrDistPoints(snapPoint, pt); 2118 } 2119 //break; 2120 } 2121 } 2122 } 2123 if (doSnap) 2124 { snapPoint = hitP; 2125 vhfPlaySound(@"Pop"); 2126 if (!control) /* update delta */ 2127 { delta.x = snapPoint.x - pt_last.x; 2128 delta.y = snapPoint.y - pt_last.y; 2129 } 2130 } 2131 } 2132 if ( !doSnap ) /* snap to grid */ 2133 { 2134 snapPoint = [self grid:pt]; 2135 doSnap = YES; 2136 } 2137 2138 /* Change the point location and get the new bounds. */ 2139 if (doSnap) 2140 [obj movePoint:pt_num to:snapPoint]; 2141 else if ([obj isKindOfClass:[VArc class]]) 2142 [obj movePoint:pt_num to:pt]; 2143 else 2144 [obj movePoint:pt_num by:delta]; 2145 2146 rect_now = [obj extendedBoundsWithScale:[self scaleFactor]]; 2147 2148 /* move all other selected points by delta */ 2149 if (pt_num == [obj selectedKnobIndex]) 2150 { 2151 for (l=[layerList count]-1; l>=0; l--) 2152 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 2153 int i; 2154 2155 if (![[layerList objectAtIndex:l] editable] || ![list count]) 2156 continue; 2157 for (i=[list count]-1; i>=0; i--) 2158 { VGraphic *g = [list objectAtIndex:i]; 2159 2160 if ([g isSelected] && g!=obj && [g selectedKnobIndex] >= 0) 2161 { 2162 [g movePoint:[g selectedKnobIndex] by:delta]; 2163 rect_now = NSUnionRect(rect_now, [g extendedBoundsWithScale:[self scaleFactor]]); 2164 } 2165 } 2166 } 2167 } 2168 [obj getPoint:pt_num :&p3]; /* display coordinates */ 2169 [window displayCoordinate:p3 ref:NO]; 2170 2171 /* Change the scrolling rectangle. */ 2172 rect_scroll = NSOffsetRect(rect_scroll, delta.x, delta.y); 2173 [self scrollToRect:rect_scroll]; 2174 2175 /* Composite the old image and then redraw the new obj. */ 2176 rect_draw_apple = NSUnionRect(rect_vis, rect_scroll); // hack to work with Apple 2177 rect_draw_apple = NSUnionRect(rect_draw_apple, rect_last); 2178 rect_draw_apple = [self centerScanRect:rect_draw_apple]; 2179 if (cacheView) 2180 { NSRect r = rect_draw_apple; //[self centerScanRect:rect_draw_apple]; // was: rect_last 2181 2182# ifdef __APPLE__ // workaround to fix scaling of the composite source rectangle (frame) 2183 r.origin.x *= scale; 2184 r.origin.y *= scale; 2185 r.size.width *= scale; 2186 r.size.height *= scale; 2187# endif 2188 NSCopyBits([cacheView gState], r, rect_draw_apple.origin); // was: rect_last 2189 //PScomposite(NSMinX(rect_last), NSMinY(rect_last), NSWidth(rect_last), NSHeight(rect_last), [cacheView gState], NSMinX(rect_last), NSMinY(rect_last), NSCompositeCopy); 2190 } 2191 else 2192 [self drawRect:rect_draw_apple]; // was: rect_last 2193 2194 [(VGraphic*)obj drawWithPrincipal:self]; 2195 /* draw all other selected graphics where points moved by delta */ 2196 if (pt_num == [obj selectedKnobIndex]) 2197 { 2198 for (l=[layerList count]-1; l>=0; l--) 2199 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 2200 int i; 2201 2202 if (![[layerList objectAtIndex:l] editable] || ![list count]) 2203 continue; 2204 for (i=[list count]-1; i>=0; i--) 2205 { VGraphic *g = [list objectAtIndex:i]; 2206 2207 if ([g isSelected] && g!=obj && [g selectedKnobIndex] >= 0) 2208 [(VGraphic*)g drawWithPrincipal:self]; 2209 } 2210 } 2211 } 2212 /* so selected objects are shown selected while we scroll and so on */ 2213 if ( VHFIsDrawingToScreen() && !scrolling ) 2214 [self drawControls:NSZeroRect]; //rect_now 2215 2216 /* Flush the drawing so that it's consistent. */ 2217 [[self window] flushWindow]; 2218 PSWait(); 2219 2220 rect_last = rect_now; 2221 pt_last = pt; 2222 } 2223 else 2224 stopTimer(&inTimerLoop); 2225 2226 event = [[self window] nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask]; 2227 tracking = ([event type] != NSLeftMouseUp); 2228 } 2229 stopTimer(&inTimerLoop); 2230 2231 /*delta.x = rect_now.origin.x - rect_start.origin.x; 2232 delta.y = rect_now.origin.y - rect_start.origin.y; 2233 if ( delta.x || delta.y )*/ 2234 if (pt_last.x != pt_start.x || pt_last.y != pt_start.y) 2235 [[self layerOfGraphic:obj] updateObject:obj]; 2236 2237 [change endChange]; 2238 } 2239 2240 [window setAutodisplay:YES]; 2241 if (pt_last.x != pt_start.x || pt_last.y != pt_start.y) 2242 [document setDirty:YES]; 2243 2244 /* Figure outside region that has to be redrawn 2245 * (the union of the old and the new rectangles). 2246 */ 2247 *redrawRect = NSUnionRect(rect_now, *redrawRect); 2248 if (pt_last.x != pt_start.x || pt_last.y != pt_start.y) 2249 return YES; 2250 return NO; 2251} 2252 2253 2254/* 2255 * Moves the graphic object. If the selected graphic can fit in the beta 2256 * cache than the image is drawn into this buffer and then composited 2257 * to each new location. The image is redrawn at the new location 2258 * when the user releases the mouse button. If the selected graphic 2259 * cannot fit in the beta buffer than it is redrawn each time. This can 2260 * happen when the drawing view is scaled upwards. 2261 * 2262 * The offsets constrain the selected object to stay within the dimensions 2263 * of the view. 2264 * 2265 * modified: 2012-01-04 (no beep for locked objects, if mouse didn't move) 2266 * 2008-09-08 (2. apple workaround for NSCopyBits/drawRect) 2267 * 2007-05-08 (apple workaround for NSCopyBits) 2268 */ 2269- (BOOL)moveObject:obj :(NSEvent *)event :(NSRect*)redrawRect 2270{ BOOL tracking = YES, beta = NO; 2271 NSSize llOffset, urOffset; 2272 NSPoint pt, pt_last, pt_old, delta, delta_scroll, drawOffset, snapPoint; 2273 NSRect rect_now, rect_start, rect_last, rect_scroll, rect_vis, rect_draw_apple; 2274 NSRect rect_draw, rect_drawlast, rect_startdraw, rect_slaylist; 2275 id betaView = [betaCache contentView]; 2276 BOOL alternate; 2277 BOOL start = YES, horizConstrain = NO, vertConstrain = NO, doSnap = NO; 2278 int l, i; 2279 DocWindow *window = (DocWindow*)[self window]; 2280 float snap = Prefs_Snap / [self scaleFactor]; /* snap distance */ 2281 id snapObj = nil; 2282 float controlsize = [self controlPointSize]; 2283 2284#if 0 2285 [DPSGetCurrentContext() setOutputTraced:YES]; 2286#endif 2287 2288 alternate = ([event modifierFlags] & NSAlternateKeyMask) ? YES : NO; 2289 2290 pt_last = pt_old = [event locationInWindow]; 2291 pt_last = [self convertPoint:pt_last fromView:nil]; 2292 2293 event = [window nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask]; 2294 2295 /* Check whether the object can fit in the second buffer. */ 2296 rect_now = [betaView frame]; 2297 if (obj) 2298 { 2299 rect_start = [obj coordBounds]; 2300 rect_draw = [obj extendedBoundsWithScale:[self scaleFactor]]; 2301 2302 if ( [obj isLocked] ) 2303 { 2304 if ( [event type] != NSLeftMouseUp ) // check for mouse drag 2305 NSBeep(); //vhfPlaySound(@"Ping"); 2306 return NO; 2307 } 2308 if ([obj hitEdge:pt_last fuzz:snap :&snapPoint :controlsize]) 2309 //if ([obj hitControl:pt_last :&ptNum controlSize:controlsize]) 2310 { snapObj = obj; 2311 pt_last = snapPoint; 2312 //pt_last = [snapObj pointWithNum:ptNum]; 2313 } 2314 } 2315 else 2316 { 2317 for ( l=[slayList count]-1; l>=0; l-- ) // objects must not be locked 2318 { NSArray *slist = [slayList objectAtIndex:l]; 2319 for ( i=[slist count]-1; i>=0; i-- ) 2320 { id ob = [slist objectAtIndex:i]; 2321 2322 if ( [ob isLocked] ) 2323 { 2324 if ( [event type] != NSLeftMouseUp ) // check for mouse drag 2325 NSBeep(); //vhfPlaySound(@"Ping"); 2326 return NO; 2327 } 2328 if ([ob hitEdge:pt_last fuzz:snap :&snapPoint :controlsize]) 2329 { snapObj = ob; 2330 pt_last = snapPoint; 2331 } 2332 } 2333 } 2334 rect_slaylist = [self boundsOfArray:slayList withKnobs:YES]; 2335 [self deselectLockedLayers:YES lockedObjects:YES]; 2336 rect_start = [self boundsOfArray:slayList withKnobs:NO]; // [self coordBoundsOfArray:slayList]; 2337 rect_draw = [self boundsOfArray:slayList withKnobs:YES]; 2338 } 2339 drawOffset.x = (rect_draw.size.width - rect_start.size.width)/2.0; 2340 drawOffset.y = (rect_draw.size.height - rect_start.size.height)/2.0; 2341 drawOffset.x = MAX(1.0, ((int)drawOffset.x)); 2342 drawOffset.y = MAX(1.0, ((int)drawOffset.y)); 2343 rect_draw.origin.x = rect_start.origin.x - drawOffset.x; 2344 rect_draw.origin.y = rect_start.origin.y - drawOffset.y; 2345 rect_draw.size.width = rect_start.size.width + drawOffset.x*2.0; 2346 rect_draw.size.height = rect_start.size.height + drawOffset.y*2.0; 2347 2348//NSLog(@"drawOffset.o %.3f %.3f", drawOffset.x, drawOffset.y); 2349 2350 if (!snapObj) 2351 pt_last = [self grid:pt_last]; 2352 2353 if (betaView && 2354 rect_now.size.width > rect_draw.size.width * scale && // rect_start 2355 rect_now.size.height > rect_draw.size.height * scale) 2356 { 2357 [betaView setBoundsOrigin:NSMakePoint(rect_draw.origin.x, rect_draw.origin.y)]; // rect_start 2358 [betaView lockFocus]; 2359 [[NSColor colorWithDeviceWhite:1.0 alpha:0.0] set]; 2360 NSRectFill(rect_draw); // rect_start 2361 if (obj) 2362 [(VGraphic*)obj drawWithPrincipal:self]; 2363 else 2364 for ( l=[slayList count]-1; l>=0; l-- ) 2365 [[slayList objectAtIndex:l] makeObjectsPerformSelector:@selector(drawWithPrincipal:) 2366 withObject:self]; 2367 [betaView unlockFocus]; 2368 beta = YES; 2369 } 2370 2371 /* Get the scrolling rectangle. If it turns out to be the visible portion of the window 2372 * then reduce it a bit so that the user is not playing pong when trying to 2373 * move the image. 2374 */ 2375 rect_scroll = (obj) ? [obj scrollRect:-1 inView:self] : rect_start; 2376 rect_vis = [self visibleRect]; 2377#if 0 // 2012-04-13 2378 if (NSContainsRect(rect_scroll, rect_vis)) 2379 { rect_scroll = rect_vis; 2380 rect_scroll = NSInsetRect(rect_scroll, rect_scroll.size.width * .20 , rect_scroll.size.height * .20); 2381 } 2382 else 2383 { 2384 if (rect_scroll.size.width == 0.0) 2385 rect_scroll.size.width = 1.0; 2386 if (rect_scroll.size.height == 0.0) 2387 rect_scroll.size.height = 1.0; 2388 rect_scroll = NSIntersectionRect(rect_vis , rect_scroll); 2389 /*if ( !obj && !NSContainsRect(rect_vis, rect_start) ) // rect_start not inside rect_vis - new part 2390 { float val = Abs(Min(rect_vis.size.width, rect_start.size.width)/Max(rect_vis.size.width, rect_start.size.width)); 2391 2392 val = Min(val, Abs(Min(rect_vis.size.height, rect_start.size.height)/Max(rect_vis.size.height, rect_start.size.height))); 2393 val = (val < 0.4) ? (0.3) : ((1.0-val)/2.0); 2394 rect_scroll = NSInsetRect(rect_scroll , rect_scroll.size.width * val , rect_scroll.size.height * val); 2395 }*/ 2396 } 2397#endif 2398 /* 2012-04-13 - size */ 2399 rect_scroll.size.width = rect_scroll.size.height = Min(rect_vis.size.width/5.0, rect_vis.size.height/5.0); 2400 2401 *redrawRect = rect_startdraw = rect_drawlast = rect_draw; 2402 2403 rect_now = rect_last = rect_start; 2404 delta_scroll.x = rect_scroll.origin.x - rect_now.origin.x; 2405 delta_scroll.y = rect_scroll.origin.y - rect_now.origin.y; 2406 2407 /* Calculate where the mouse point falls relative to the object. */ 2408 if (obj == origin) 2409 { float margin = 0.0; // ceil([self controlPointSize]); 2410 2411 llOffset.width = pt_last.x - (rect_start.origin.x + rect_start.size.width/2.0) - margin; 2412 llOffset.height = pt_last.y - (rect_start.origin.y + rect_start.size.height/2.0) - margin; 2413 urOffset.width = rect_start.origin.x + rect_start.size.width/2.0 - pt_last.x - margin; 2414 urOffset.height = rect_start.origin.y + rect_start.size.height/2.0 - pt_last.y - margin; 2415 } 2416 else 2417 { llOffset.width = pt_last.x - rect_start.origin.x; 2418 llOffset.height = pt_last.y - rect_start.origin.y; 2419 urOffset.width = rect_start.origin.x + rect_start.size.width - pt_last.x; 2420 urOffset.height = rect_start.origin.y + rect_start.size.height - pt_last.y; 2421 } 2422 2423 //event = [window nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask]; 2424 delta.x = delta.y = 0; 2425 2426 [window setAutodisplay:NO]; // No because nextEvent would draw (flicker's without betaView) 2427 scrolling = YES; // don't draw knobs 2428 if ([event type] != NSLeftMouseUp) 2429 { 2430 while (tracking) 2431 { 2432 pt = ([event type] == NSPeriodic) ? pt_old : (pt_old = [event locationInWindow]); 2433 pt = [self convertPoint:pt fromView:nil]; 2434 [self constrainPoint:&pt withOffset:&llOffset :&urOffset]; 2435 [self constrainPoint:&pt_last withOffset:&llOffset :&urOffset]; 2436 2437 /* snap to point */ 2438 //doSnap = [self hitEdge:&pt spare:snapObj]; // FIXME: [layer state] 2439 if (snapObj) 2440 { NSPoint hitP = pt; 2441 double sqrDistBest = MAXFLOAT; 2442 2443 doSnap = NO; 2444 for (l=[layerList count]-1; l>=0 && !doSnap; l--) 2445 { LayerObject *layerObj = [layerList objectAtIndex:l]; 2446 NSMutableArray *list = [layerObj list]; 2447 2448 if ( ![layerObj state] || ![list count] ) 2449 continue; 2450 for (i=[list count]-1; i>=0; i--) 2451 { VGraphic *g = [list objectAtIndex:i]; 2452 2453 if ([g hitEdge:pt fuzz:snap :&snapPoint :controlsize] && g != snapObj) 2454 { doSnap = YES; 2455 if ( SqrDistPoints(snapPoint, pt) < sqrDistBest ) 2456 { hitP = snapPoint; 2457 sqrDistBest = SqrDistPoints(snapPoint, pt); 2458 } 2459 //pt = snapPoint; 2460 //break; 2461 } 2462 } 2463 } 2464 if ( doSnap ) 2465 { vhfPlaySound(@"Pop"); 2466 pt = hitP; 2467 } 2468 } 2469 /* snap to grid */ 2470 if (!doSnap) 2471 pt = [self grid:pt]; 2472 delta.x = pt.x - pt_last.x; 2473 delta.y = pt.y - pt_last.y; 2474 2475 if ( (start && (delta.x + delta.y > 2.0)) || (delta.x || delta.y) ) 2476 { 2477 start = NO; 2478 2479 /* vertical/horizontal constrain 2480 */ 2481 if (alternate) 2482 { if (ABS(delta.x) > ABS(delta.y)) 2483 horizConstrain = YES; 2484 else 2485 vertConstrain = YES; 2486 alternate = NO; 2487 } 2488 if (horizConstrain) 2489 delta.y = 0.0; 2490 else if (vertConstrain) 2491 delta.x = 0.0; 2492 2493 [window displayCoordinate:pt ref:NO]; 2494 2495 rect_now = NSOffsetRect(rect_now, delta.x, delta.y); 2496 if (obj != origin) 2497 [self constrainRect:&rect_now]; 2498 rect_draw.origin.x = rect_now.origin.x - drawOffset.x; 2499 rect_draw.origin.y = rect_now.origin.y - drawOffset.y; 2500 2501#if 0 // 2012-04-13 2502 rect_scroll.origin.x = rect_now.origin.x + delta_scroll.x; 2503 rect_scroll.origin.y = rect_now.origin.y + delta_scroll.y; 2504#endif 2505 /* 2012-04-13 - origin */ 2506 rect_scroll.origin = NSMakePoint(pt.x - rect_scroll.size.width /2.0, 2507 pt.y - rect_scroll.size.height/2.0); 2508 //[self scrollPointToVisible:pt]; 2509 [self scrollToRect:rect_scroll]; 2510 2511 /* Copy the old image into the window. If using the second buffer, copy 2512 * it atop the old buffer. Otherwise, translate and redraw. 2513 */ 2514 rect_draw_apple = NSUnionRect(rect_vis, rect_scroll); 2515 rect_draw_apple = NSUnionRect(rect_draw_apple, rect_drawlast); 2516 if (cacheView) 2517 { NSRect r = rect_draw_apple; // rect_drawlast - reicht bei OpenStep 2518 2519# ifdef __APPLE__ // workaround to fix scaling of the composite source rectangle (frame) 2520 r = [self centerScanRect:rect_draw_apple]; // nicht bei OpenStep ! 2521 r.origin.x *= scale; 2522 r.origin.y *= scale; 2523 r.size.width *= scale; 2524 r.size.height *= scale; 2525# endif 2526 NSCopyBits([cacheView gState], r, rect_draw_apple.origin); // rect_drawlast is sufficient for OpenStep only 2527 } 2528 else 2529 [self drawRect:rect_draw_apple]; // rect_drawlast is sufficient for OpenStep only 2530 if (beta) 2531 PScomposite(NSMinX(rect_startdraw), NSMinY(rect_startdraw), NSWidth(rect_startdraw), NSHeight(rect_startdraw), [betaView gState], NSMinX(rect_draw), NSMinY(rect_draw), NSCompositeSourceOver); 2532 else 2533 { NSPoint oldOrigin = [[NSView focusView] bounds].origin; 2534 2535 //[[NSView focusView] setBoundsOrigin: NSMakePoint(rect_start.origin.x - rect_now.origin.x, 2536 // rect_start.origin.y - rect_now.origin.y)]; 2537 [[NSView focusView] setBoundsOrigin: 2538 NSMakePoint(rect_startdraw.origin.x - rect_draw.origin.x, 2539 rect_startdraw.origin.y - rect_draw.origin.y)]; 2540 if (obj) 2541 [(VGraphic*)obj drawWithPrincipal:self]; 2542 else 2543 for ( l=[slayList count]-1; l>=0; l-- ) 2544 [[slayList objectAtIndex:l] makeObjectsPerformSelector:@selector(drawWithPrincipal:) 2545 withObject:self]; 2546 [[NSView focusView] setBoundsOrigin:oldOrigin]; 2547 } 2548 2549 [window flushWindow]; 2550 PSWait(); 2551 2552 rect_drawlast = rect_draw; 2553 rect_last = rect_now; 2554 pt_last = pt; 2555 } 2556 else 2557 stopTimer(&inTimerLoop); 2558 2559 /* workaround for GNUsteps slow image processing */ 2560#ifdef GNUSTEP_BASE_VERSION 2561 { NSEvent *lastEvent = nil; 2562 2563 do 2564 { 2565 if ((event = [window nextEventMatchingMask: 2566 NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask 2567 untilDate:[NSDate date] 2568 inMode:NSEventTrackingRunLoopMode dequeue:YES])) 2569 lastEvent = event; 2570 } 2571 while (event); 2572 if (!lastEvent) 2573 event = [window nextEventMatchingMask: 2574 NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask]; 2575 else 2576 { 2577 event = lastEvent; 2578 [window discardEventsMatchingMask: 2579 NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask 2580 beforeEvent:event]; 2581 } 2582 } 2583#else 2584 event = [window nextEventMatchingMask: 2585 NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask]; 2586#endif 2587 2588 tracking = ([event type] != NSLeftMouseUp); 2589 } 2590 stopTimer(&inTimerLoop); 2591 2592 delta.x = rect_now.origin.x - rect_start.origin.x; 2593 delta.y = rect_now.origin.y - rect_start.origin.y; 2594 if ( delta.x || delta.y ) 2595 { 2596 if (obj) 2597 { [obj moveBy:delta]; 2598 [[self layerOfGraphic:obj] updateObject:obj]; 2599 } 2600 else 2601 [self moveGraphicsBy:delta andDraw:NO]; 2602 } 2603 } 2604 scrolling = NO; 2605 [window setAutodisplay:YES]; 2606 if ( delta.x || delta.y ) 2607 [document setDirty:YES]; 2608 2609 *redrawRect = (obj) ? NSUnionRect(rect_draw, *redrawRect) : NSUnionRect(rect_draw, rect_slaylist); 2610 2611 if (!floor(delta.x) && !floor(delta.y)) 2612 return NO; 2613 2614 /* wait a msec to allow correct redraw with performance map */ 2615 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0/1000.0]]; 2616 2617 return YES; 2618} 2619 2620/* 2621 * Rotates the graphic object. If the selected graphic 2622 * cannot fit in the beta buffer than it is redrawn each time. This can 2623 * happen when the drawing view is scaled upwards. 2624 * 2625 * The offsets constrain the selected object to stay within the dimensions 2626 * of the view. 2627 * 2628 * modified: 2007-05-08 (apple workaround for NSCopyBits) 2629 */ 2630- (BOOL)rotateObject:(VGraphic*)obj :(NSEvent *)event :(NSRect*)redrawRect 2631{ BOOL tracking = YES; 2632 NSSize llOffset, urOffset; 2633 NSPoint p, pt_start, pt, pt_last, pt_old, delta, delta_scroll; 2634 NSRect rect_now, rect_start, rect_last, rect_scroll, rect_vis; 2635 float angle = 0.0, startAngle, dx, dy, av; 2636 BOOL alternate, control; 2637 BOOL horizConstrain = NO, vertConstrain = NO; 2638 id change; 2639 2640#if 0 2641 [DPSGetCurrentContext() setOutputTraced:YES]; 2642#endif 2643 2644 alternate = ([event modifierFlags] & NSAlternateKeyMask) ? YES : NO; 2645 control = ([event modifierFlags] & NSControlKeyMask) ? YES : NO; 2646 2647 rect_start = [obj extendedBoundsWithScale:[self scaleFactor]]; 2648 rect_last = *redrawRect = [obj maximumBounds]; 2649 2650 /*if (cacheView) // copy cached image to screen 2651 PScomposite(NSMinX(rect_start), NSMinY(rect_start), NSWidth(rect_start), NSHeight(rect_start), 2652 [cacheView gState], NSMinX(rect_start), NSMinY(rect_start), NSCompositeCopy);*/ 2653 2654 /* Get the scrolling rectangle. If it turns out to be the visible portion of the window 2655 * then reduce it a bit so that the user is not playing pong when trying to 2656 * move the image. 2657 */ 2658 rect_scroll = [obj scrollRect:-1 inView:self]; 2659 rect_vis = [self visibleRect]; 2660 if (NSContainsRect(rect_scroll , rect_vis)) 2661 { rect_scroll = rect_vis; 2662 rect_scroll = NSInsetRect(rect_scroll , rect_scroll.size.width * .20 , rect_scroll.size.height * .20); 2663 } 2664 else 2665 rect_scroll = NSIntersectionRect(rect_vis , rect_scroll); 2666 2667 rect_now = rect_start; 2668 delta_scroll.x = rect_scroll.origin.x - rect_now.origin.x; 2669 delta_scroll.y = rect_scroll.origin.y - rect_now.origin.y; 2670 2671 pt_last = pt_old = [event locationInWindow]; 2672 pt_last = [self convertPoint:pt_last fromView:nil]; 2673 pt_start = pt_last; 2674 2675 p = [obj center]; 2676 dx = pt_start.x - p.x; dy = pt_start.y - p.y; 2677 av = sqrt(dx*dx + dy*dy); 2678 /* calculate angle between mouse and center of object */ 2679 startAngle = Asin(dy/av); 2680 startAngle = (dx<0) ? 180.0-startAngle : startAngle; 2681 startAngle = -startAngle; 2682 2683 /* Calculate where the mouse point falls relative to the object. */ 2684 llOffset.width = pt_last.x - rect_start.origin.x; 2685 llOffset.height = pt_last.y - rect_start.origin.y; 2686 urOffset.width = rect_start.origin.x + rect_start.size.width - pt_last.x; 2687 urOffset.height = rect_start.origin.y + rect_start.size.height - pt_last.y; 2688 2689 event = [[self window] nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask]; 2690 if ([event type] != NSLeftMouseUp) 2691 { 2692 while (tracking) 2693 { 2694 pt = ([event type] == NSPeriodic) ? pt_old : (pt_old = [event locationInWindow]); 2695 2696 pt = [self convertPoint:pt fromView:nil]; 2697 [self constrainPoint:&pt withOffset:&llOffset :&urOffset]; 2698 [self constrainPoint:&pt_last withOffset:&llOffset :&urOffset]; 2699 delta.x = pt.x - pt_last.x; 2700 delta.y = pt.y - pt_last.y; 2701 2702 if (delta.x || delta.y) 2703 { 2704 if (alternate && !control) 2705 { 2706 if (ABS(delta.x) > ABS(delta.y)) 2707 horizConstrain = YES; 2708 else 2709 vertConstrain = YES; 2710 alternate = NO; 2711 } 2712 if (horizConstrain) 2713 delta.y = 0.0; 2714 else if (vertConstrain) 2715 delta.x = 0.0; 2716 2717 rect_scroll.origin.x = rect_now.origin.x + delta_scroll.x; 2718 rect_scroll.origin.y = rect_now.origin.y + delta_scroll.y; 2719 [self scrollToRect:rect_scroll]; 2720 2721 /* Copy the old image into the window. Then translate and redraw. 2722 */ 2723 if (cacheView) 2724 { NSRect r = [self centerScanRect:rect_last]; 2725 2726# ifdef __APPLE__ // workaround to fix scaling of the composite source rectangle (frame) 2727 r.origin.x *= scale; 2728 r.origin.y *= scale; 2729 r.size.width *= scale; 2730 r.size.height *= scale; 2731# endif 2732 NSCopyBits([cacheView gState], r, rect_last.origin); 2733 } 2734 else 2735 [self drawRect:rect_last]; 2736 2737 { float dx, dy, av; 2738 2739 //dx = pt.x - pt_start.x; dy = pt.y - pt_start.y; /* get the center of the object instead */ 2740 p = [obj center]; 2741 dx = pt.x - p.x; dy = pt.y - p.y; 2742 av = sqrt(dx*dx + dy*dy); 2743 /* calculate angle between mouse and center of object */ 2744 angle = Asin(dy/av); 2745 angle = (dx<0) ? 180.0-angle : angle; 2746 angle = -angle; 2747 angle = angle - startAngle; 2748 if (angle < 360.0 && angle > -360.0) 2749 [obj drawAtAngle:angle in:self]; 2750 } 2751 2752 /* Flush the drawing so that it's consistent. */ 2753 [[self window] flushWindow]; 2754 PSWait(); 2755 2756 // rect_last = rect_now; 2757 pt_last = pt; 2758 } 2759 else 2760 stopTimer(&inTimerLoop); 2761 2762 event = [[self window] nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask]; 2763 2764 tracking = ([event type] != NSLeftMouseUp); 2765 } 2766 stopTimer(&inTimerLoop); 2767 2768 change = [[RotateGraphicsChange alloc] initGraphicView:self angle:angle center:[obj center]]; 2769 [change startChange]; 2770 [obj rotate:angle]; 2771 [[self layerOfGraphic:obj] updateObject:obj]; 2772 [change endChange]; 2773 [document setDirty:YES]; 2774 } 2775 2776 //redrawRect = NSUnionRect(&rect_now, redrawRect); 2777 2778 if (!angle) 2779 return NO; 2780 return YES; 2781} 2782 2783/* Check to see whether a control point has been hit. */ 2784- (BOOL) checkControl:(const NSPoint *) p :(int *) pt_num 2785{ BOOL hit; 2786 float controlsize/*, hitsetting*/; 2787 // NXRect hitRect; 2788 int i, l; 2789 2790 controlsize = [self controlPointSize]; 2791 // hitsetting = [superview hitSetting]; 2792 // NXSetRect(&hitRect, p->x - hitsetting/2, p->y - hitsetting/2, hitsetting, hitsetting); 2793 2794 for (l=[layerList count]-1; l>=0; l--) 2795 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 2796 2797 if (![[layerList objectAtIndex:l] editable] || ![list count]) 2798 continue; 2799 for (i=[list count]-1; i>=0; i--) 2800 { id obj = [list objectAtIndex:i]; 2801 2802 if ((hit = [obj hitControl:*p :pt_num controlSize:controlsize])) 2803 return hit; 2804 } 2805 } 2806 2807 return NO; 2808} 2809 2810- (BOOL)magnify 2811{ 2812 return magnifyMode; 2813} 2814- (void)setMagnify:(BOOL)flag 2815{ 2816 magnifyMode = flag; 2817 if (!flag) 2818 [(App*)NSApp setCurrent2DTool:self]; 2819} 2820 2821 2822/* 2823 * First Responder 2824 */ 2825 2826- (void)mouseMoved:(NSEvent*)event 2827{ //NSPoint pc = [self mouseLocationOutsideOfEventStream], p; 2828 NSPoint pc = [event locationInWindow], p; 2829 NSRect rect; 2830 2831 p = [[self superview] convertPoint:pc fromView:nil]; 2832 rect = [(NSClipView*)[self superview] bounds]; 2833 if ( ![self mouse:p inRect:rect] /*!NSPointInRect(p , rect)*/ ) 2834 return; 2835 p = [self convertPoint:pc fromView:nil]; 2836 [(DocWindow*)[self window] displayCoordinate:p ref:NO]; 2837} 2838 2839/* modified: 2012-01-05 2840 * 2841 * If the docview is zooming, then scale the drawing view. Else 2842 * check for hit detection on the bezier or the control points. 2843 */ 2844- (void)mouseDown:(NSEvent *)event 2845{ BOOL redraw = YES; 2846 NSPoint p; 2847 NSRect drawRect = NSZeroRect; 2848 int pt_num, i, l, toolIx; 2849 BOOL shift, control, gotHit = NO, compositeAll = NO, editable = NO; 2850 VGraphic *g = nil; 2851 DocWindow *window = (DocWindow*)[self window]; 2852 2853 /* You only need to do the following line in a mouseDown: method if 2854 * you receive this message because one of your subviews gets the 2855 * mouseDown: and does not respond to it (thus, it gets passed up the 2856 * responder chain to you). In this case, our editView receives the 2857 * mouseDown:, but doesn't do anything about it, and when it comes 2858 * to us, we want to become the first responder. 2859 * 2860 * Normally you won't have a subview which doesn't do anything with 2861 * mouseDown:, in which case, you need only return YES from the 2862 * method acceptsFirstResponder (see that method below) and will NOT 2863 * need to do the following makeFirstResponder:. In other words, 2864 * don't put the following line in your mouseDown: implementation! 2865 * 2866 * Wichtig, damit der View als First Responder des Window funktioniert 2867 */ 2868 if ([window firstResponder] != self) 2869 { 2870 if ([event clickCount] < 2) 2871 [window makeFirstResponder:self]; 2872 else 2873 { [[window firstResponder] mouseDown:event]; 2874 return; 2875 } 2876 } 2877 2878 if (magnifyMode) 2879 { [self dragMagnify:event]; 2880 return; 2881 } 2882 2883 shift = ([event modifierFlags] & NSShiftKeyMask) ? YES : NO; 2884 control = ([event modifierFlags] & NSControlKeyMask) ? YES : NO; 2885 2886 p = [event locationInWindow]; 2887 [self lockFocus]; 2888 p = [self convertPoint:p fromView:nil]; 2889 2890 [window displayCoordinate:p ref:YES]; 2891 2892 [window endEditingFor:nil]; /* end editing of text */ 2893 2894 /* check whether we are editable 2895 */ 2896 for ( l=[layerList count]-1; l>=0; l-- ) 2897 { 2898 if ( [[layerList objectAtIndex:l] editable] ) 2899 { editable = YES; 2900 break; 2901 } 2902 } 2903 2904 /* 2905 * create (edit) 2906 */ 2907 if ( editable && displayGraphic ) 2908 { 2909 switch ( toolIx = [(App*)NSApp current2DTool] ) 2910 { 2911 /* if we are inside an existing text we edit this text instead of creating a new one */ 2912 case TOOL2D_TEXT: 2913 [self deselectAll:nil]; 2914 for (l=[layerList count]-1; l>=0; l--) // if we hit a text -> we edit this text 2915 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 2916 2917 if ( ![[layerList objectAtIndex:l] editable] ) 2918 continue; 2919 for ( i=[list count]-1; i>=0; i-- ) 2920 { g = [list objectAtIndex:i]; 2921 2922 if ( [g isKindOfClass:[VText class]] && [g hit:p fuzz:2.0/scale] ) 2923 { [(VText*)g edit:event in:editView]; 2924 gotHit = YES; 2925 redraw = NO; 2926 l = 0; 2927 [document setDirty:YES]; 2928 break; 2929 } 2930 } 2931 } 2932 if (!gotHit) 2933 { g = [[[VText allocWithZone:[self zone]] init] autorelease]; 2934 if ( [g create:event in:self] ) 2935 { id change; 2936 2937 change = [[CreateGraphicsChange alloc] initGraphicView:self graphic:g]; 2938 [change startChangeIn:self]; 2939 [(VText*)g edit:NULL in:editView]; 2940 [self insertGraphic:g]; 2941 gotHit = YES; 2942 redraw = NO; 2943 [change endChange]; 2944 } 2945 //else 2946 //[g release]; 2947 } 2948 break; 2949 case TOOL2D_LINE: 2950 g = [VLine line]; 2951 [g setWidth:Prefs_LineWidth]; 2952 case TOOL2D_MARK: 2953 if ( toolIx == TOOL2D_MARK ) 2954 g = [[[VMark allocWithZone:[self zone]] init] autorelease]; 2955 case TOOL2D_WEB: 2956 if ( toolIx == TOOL2D_WEB ) 2957 g = [[[VWeb allocWithZone:[self zone]] init] autorelease]; 2958 case TOOL2D_ARC: 2959 if ( toolIx == TOOL2D_ARC ) 2960 { g = [VArc arc]; 2961 [g setWidth:Prefs_LineWidth]; 2962 } 2963 case TOOL2D_THREAD: 2964 if ( toolIx == TOOL2D_THREAD ) 2965 g = [[[VThread allocWithZone:[self zone]] init] autorelease]; 2966 case TOOL2D_SINKING: 2967 if ( toolIx == TOOL2D_SINKING ) 2968 g = [[[VSinking allocWithZone:[self zone]] init] autorelease]; 2969 case TOOL2D_RECT: 2970 if ( toolIx == TOOL2D_RECT ) 2971 { g = [[[VRectangle allocWithZone:[self zone]] init] autorelease]; 2972 [g setWidth:Prefs_LineWidth]; 2973 } 2974 case TOOL2D_CURVE: 2975 if (toolIx == TOOL2D_CURVE ) 2976 { g = [VCurve curve]; 2977 [g setWidth:Prefs_LineWidth]; 2978 } 2979 case TOOL2D_PATH: 2980 if ( toolIx == TOOL2D_PATH ) 2981 g = [VPath path]; 2982 case TOOL2D_POLYLINE: 2983 if ( toolIx == TOOL2D_POLYLINE ) 2984 { g = [VPolyLine polyLine]; 2985 [g setWidth:Prefs_LineWidth]; 2986 } 2987 [self deselectAll:self]; 2988 2989/*if ([g isKindOfClass:[VLine class]]) 2990{ 2991 g = [VLine3D line3D]; 2992 [g setVertices:NSMakePoint(10.0, 10.0) :NSMakePoint(50.0, 50.0)]; 2993 [g setZLevel:10.0 :50.0]; 2994 [g setSelected:YES]; 2995 [self insertGraphic:g]; 2996 gotHit = YES; 2997 redraw = NO; 2998 break; 2999}*/ 3000 if ( [g create:event in:self] ) 3001 { id change; 3002 3003 change = [[CreateGraphicsChange alloc] initGraphicView:self graphic:g]; 3004 [change startChangeIn:self]; 3005 [g setSelected:YES]; 3006 [self insertGraphic:g]; 3007 gotHit = YES; 3008 redraw = NO; 3009 [change endChange]; 3010 } 3011 break; 3012 3013 /* split objects at mouse position 3014 */ 3015 case TOOL2D_SCISSOR: // knife tool 3016 for (l=[layerList count]-1; l>=0; l--) // if we hit an object -> split it 3017 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 3018 3019 if ( ![[layerList objectAtIndex:l] editable] ) 3020 continue; 3021 for ( i=[list count]-1; i>=0; i-- ) 3022 { g = [list objectAtIndex:i]; 3023 3024 if ( [g isSelected] && [g isPathObject] && [g hit:p fuzz:2.0/scale] ) 3025 { 3026 [self splitObject:g atPoint:p redraw:YES]; 3027 gotHit = YES; 3028 redraw = NO; 3029 l = 0; 3030 [document setDirty:YES]; 3031 break; 3032 } 3033 } 3034 } 3035 break; 3036 /* add point to object at mouse position 3037 */ 3038 case TOOL2D_ADDPOINT: // add tool 3039 for (l=[layerList count]-1; l>=0; l--) // if we hit an Path or PolyLine -> add point to it 3040 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 3041 3042 if ( ![[layerList objectAtIndex:l] editable] ) 3043 continue; 3044 for ( i=[list count]-1; i>=0; i-- ) 3045 { g = [list objectAtIndex:i]; 3046 3047 if ( [g isSelected] && ([g isKindOfClass:[VPath class]] || [g isKindOfClass:[VPolyLine class]]) 3048 && [g hit:p fuzz:2.0/scale] ) 3049 { 3050 [self addPointTo:g atPoint:p redraw:YES]; 3051 gotHit = YES; 3052 redraw = NO; 3053 l = 0; 3054 [document setDirty:YES]; 3055 break; 3056 } 3057 } 3058 } 3059 break; 3060 } 3061 } 3062 3063 /* check selected objects 3064 */ 3065 if ( displayGraphic && !gotHit ) 3066 { 3067 for ( l=[layerList count]-1; l>=0; l-- ) 3068 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 3069 NSMutableArray *slist = [slayList objectAtIndex:l]; 3070 3071 if ( /*![[layerList objectAtIndex:l] editable] ||*/ ![list count] ) 3072 continue; 3073 for ( i=[list count]-1; i>=0; i-- ) 3074 { id obj = [list objectAtIndex:i]; 3075 3076 if ([obj isSelected]) /* object is selected */ 3077 { 3078 if ( [[layerList objectAtIndex:l] editable] && [event clickCount] >= 2 && 3079 [obj respondsToSelector:@selector(edit:in:)] && [obj hit:p fuzz:2.0/scale] ) 3080 { [self deselectAll:nil]; 3081 [obj edit:NULL in:editView]; 3082 gotHit = YES; 3083 redraw = NO; 3084 l = 0; break; 3085 } 3086 else if (shift && [obj hit:p fuzz:2.0/scale]) /* deselect object */ 3087 { 3088 gotHit = YES; 3089 drawRect = [obj extendedBoundsWithScale:[self scaleFactor]]; 3090 [obj setSelected:NO]; 3091 [slist removeObject:obj]; 3092 [self drawRect:drawRect]; 3093 l = 0; break; 3094 } 3095 else if ([[layerList objectAtIndex:l] editable] && [(App*)NSApp current2DTool] != TOOL2D_ROTATE && 3096 [obj hitControl:p :&pt_num controlSize:[self controlPointSize]]) // move control 3097 { NSPoint p3; 3098 3099 [obj getPoint:pt_num :&p3]; 3100 [window displayCoordinate:p3 ref:YES]; 3101 gotHit = YES; 3102 if (![self redrawObject:obj :pt_num :&drawRect]) 3103 redraw = NO; 3104 l = 0; break; 3105 } 3106 else if ([[layerList objectAtIndex:l] editable] && [obj hit:p fuzz:2.0/scale]) // move object 3107 { 3108 gotHit = YES; 3109 if ([(App*)NSApp current2DTool] == TOOL2D_ROTATE) 3110 { if (![self rotateObject:obj :event :&drawRect]) 3111 redraw = NO; 3112 } 3113 else if (![self moveObject:nil :event :&drawRect]) 3114 redraw = NO; 3115 [document setDirty:YES]; 3116 l = 0; break; 3117 } 3118 } 3119 } 3120 } 3121 } 3122 3123 /* we don't hit any selected object but we hit the origin -> move origin 3124 */ 3125 if ( !shift && !gotHit && [origin hit:p fuzz:2.0/scale] ) // move origin 3126 { [self moveObject:origin :event :&drawRect]; 3127 gotHit = compositeAll = YES; 3128 } 3129 3130 /* we don't hit any object which is selected and shift is not pressed 3131 * so we deselect all selected objects 3132 */ 3133 if (!shift && !gotHit) 3134 { 3135 redraw = NO; 3136 [self deselectAll:self]; 3137 } 3138 3139 /* we don't hit any object which is selected 3140 * so we check objects which are not selected 3141 */ 3142 if (!gotHit) 3143 { 3144 for (l=[layerList count]-1; l>=0; l--) 3145 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 3146 NSMutableArray *slist = [slayList objectAtIndex:l]; 3147 3148 /* we only select on editable layers, except with CAM module */ 3149 if ( ![list count] || ![(LayerObject*)[layerList objectAtIndex:l] state] 3150 || (!Prefs_SelectNonEditable && ![[layerList objectAtIndex:l] editable]) ) 3151 continue; 3152 for (i=[list count]-1; i>=0; i--) 3153 { id obj = [list objectAtIndex:i]; 3154 3155 if (![obj isSelected]) // object is not selected 3156 { 3157 if ([obj hit:p fuzz:2.0/scale]) // select object 3158 { 3159 gotHit = YES; 3160 drawRect = [obj extendedBoundsWithScale:scale]; 3161 [obj setSelected:YES]; 3162 if ([obj respondsToSelector:@selector(font)]) 3163 [[NSFontManager sharedFontManager] setSelectedFont:[obj font] isMultiple:NO]; 3164 [slist addObject:obj]; 3165 [window flushWindow]; 3166 PSWait(); 3167 3168 if ( displayGraphic && [[layerList objectAtIndex:l] editable] ) 3169 { 3170 if ([(App*)NSApp current2DTool] == TOOL2D_ROTATE) 3171 { 3172 if ([self rotateObject:obj :event :&drawRect]) 3173 redraw = YES; 3174 } 3175 else if ([self moveObject:nil :event :&drawRect]) 3176 redraw = YES; 3177 } 3178 //if (!redraw) 3179 // [self drawRect:drawRect]; 3180 3181 l=-1; break; 3182 } 3183 } 3184 } 3185 } 3186 } 3187 3188 if (!gotHit) 3189 { [window flushWindow]; 3190 drawRect = [self dragSelect:event]; 3191 } 3192 3193 /* inform modules about mouse down */ 3194 [[NSNotificationCenter defaultCenter] postNotificationName:DocViewMouseDown 3195 object:self 3196 userInfo:[NSDictionary dictionaryWithObject:event 3197 forKey:@"event"]]; 3198 3199 [[(App*)NSApp inspectorPanel] loadList:slayList]; 3200 3201 /* redraw */ 3202 if (tileOriginList) 3203 compositeAll = YES; // make cache update in rect, but composite entire bounds 3204 if (redraw) 3205 [self cache:(compositeAll) ? [self bounds] : drawRect]; 3206 else if ( compositeAll || drawRect.size.width || drawRect.size.height ) // 2008-05-03 3207 [self flatRedraw:(compositeAll) ? [self bounds] : drawRect]; 3208 [self unlockFocus]; 3209 [window flushWindow]; 3210} 3211 3212- (void)keyDown:(NSEvent *)event 3213{ NSString *chars = [event characters]; 3214 3215 //NSLog(@"keyCode:%d", [event keyCode]); 3216 3217 if ([chars length]) 3218 { unichar character = [[event characters] characterAtIndex:0]; 3219 3220 //NSLog(@"key:%d %x", character, character); 3221 3222 /* delete */ 3223 if ( character == NSBackspaceCharacter || // backspace (0x8) 3224 character == NSDeleteFunctionKey || // delete (0xf728) 3225 character == NSDeleteCharacter ) // backspace OS (0x7f) 3226 [[document documentView] delete:self]; 3227 } 3228} 3229 3230/* modified: 2006-07-12 3231 * Allows the user the drag out a box to select all objects either 3232 * intersecting the box, or fully contained within the box (depending 3233 * on the state of the ALTERNATE key). After the selection is made, 3234 * the slayList is updated. 3235 */ 3236#define DRAG_MASK (NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask) 3237- (NSRect)dragSelect:(NSEvent *)event 3238{ int i, l; 3239 NSWindow *window = (DocWindow*)[self window]; 3240 NSPoint p, last, start; 3241 NSRect visibleRect, region, oldRegion, drawRect; 3242 BOOL mustContain, shift, canScroll, oldRegionSet = NO; 3243 NSMutableArray *list; 3244 float enlargeSize = 1.0/[self scaleFactor]; 3245 3246 p = start = [event locationInWindow]; 3247 start = [self convertPoint:start fromView:nil]; 3248 last = start; 3249 3250 shift = ([event modifierFlags] & NSShiftKeyMask) ? YES : NO; 3251 mustContain = ([event modifierFlags] & NSAlternateKeyMask) ? YES : NO; 3252 3253 [self lockFocus]; 3254 3255 visibleRect = [self visibleRect]; 3256 canScroll = !NSEqualRects(visibleRect , [self bounds]); 3257 if (canScroll) 3258 startTimer(&inTimerLoop); 3259 3260 region.origin.x = region.origin.y = 0.0; 3261 region.size.width = region.size.height = 0.0; 3262 event = [window nextEventMatchingMask:DRAG_MASK]; 3263 while ([event type] != NSLeftMouseUp) 3264 { 3265 if ([event type] == NSPeriodic) 3266 //[event locationInWindow] = p; 3267 event = periodicEventWithLocationSetToPoint(event, p); 3268 p = [event locationInWindow]; 3269 p = [self convertPoint:p fromView:nil]; 3270 if (p.x != last.x || p.y != last.y) 3271 { 3272 region.origin.x = Min(p.x, start.x); 3273 region.origin.y = Min(p.y, start.y); 3274 region.size.width = Max(p.x, start.x) - region.origin.x; 3275 region.size.height = Max(p.y, start.y) - region.origin.y; 3276 [[self window] disableFlushWindow]; 3277 if (oldRegionSet) 3278 { oldRegion = NSInsetRect(oldRegion , -enlargeSize , -enlargeSize); 3279 [self drawRect:oldRegion]; 3280 } 3281 if (canScroll) 3282 { [self scrollRectToVisible:region]; 3283 [self scrollPointToVisible:p]; 3284 } 3285 [[NSColor lightGrayColor] set]; 3286 [NSBezierPath setDefaultLineWidth:1.0/scale]; 3287 [NSBezierPath strokeRect:region]; 3288 //NSFrameRectWithWidth(region, 1.0/scale); 3289 [window enableFlushWindow]; 3290 [window flushWindow]; 3291 oldRegion = region; oldRegionSet = YES; 3292 last = p; 3293 PSWait(); 3294 } 3295 p = [event locationInWindow]; 3296 event = [[self window] nextEventMatchingMask:DRAG_MASK]; 3297 } 3298 3299 if (canScroll) 3300 stopTimer(&inTimerLoop); 3301 3302 drawRect = region; 3303 if (region.size.width > 0.0 && region.size.height > 0.0) 3304 { 3305 for ( l=[layerList count]-1; l>=0; l-- ) 3306 { LayerObject *layerObject = [layerList objectAtIndex:l]; 3307 3308 list = [layerObject list]; 3309 if ( ![layerObject state] || ![list count] 3310 || (!Prefs_SelectNonEditable && ![layerObject editable]) ) 3311 continue; 3312 3313 for ( i = [list count] - 1; i >= 0; i-- ) 3314 { VGraphic *g = [list objectAtIndex:i]; 3315 NSRect rect = [g coordBounds]; // may have zero width or height ! 3316 3317 rect.size.width = MAX(rect.size.width, 0.001); 3318 rect.size.height = MAX(rect.size.height, 0.001); 3319 /* bounds check */ 3320 if ( ( mustContain && NSContainsRect(region, rect)) || 3321 (!mustContain && !NSIsEmptyRect(NSIntersectionRect(region, rect))) ) 3322 { VRectangle *gRect; 3323 3324 /* check for graphic intersection 3325 * we are satisfied with the bounds check above for all but path-objects and groups 3326 */ 3327 //if (mustContain || NSContainsRect(region, rect) || [g intersectsRect:region]) 3328 gRect = [VRectangle rectangle]; 3329 [gRect setVertices:region.origin :NSMakePoint(region.size.width, region.size.height)]; 3330 if ( mustContain || NSContainsRect(region, rect) 3331 || (![g isPathObject] && ![g isKindOfClass:[VGroup class]]) 3332 || [gRect sqrDistanceGraphic:g] <= ([g width]/2.0)*([g width]/2.0) ) 3333 { 3334 [g setSelected:(shift && [g isSelected]) ? NO : YES]; 3335 drawRect = NSUnionRect(rect, drawRect); 3336 } 3337 } 3338 } 3339 } 3340 } 3341 3342 if (drawRect.size.width > 0.0 && drawRect.size.height > 0.0) 3343 { 3344 [self getSelection]; 3345 drawRect = NSInsetRect(drawRect, -enlargeSize, -enlargeSize); 3346 //[self flatRedraw:drawRect]; // will be redrawed in mouseDown 3347 } 3348 [self unlockFocus]; 3349 3350 /* post notification */ 3351 if (region.size.width > 0.0 && region.size.height > 0.0) 3352 { NSDictionary *userInfo; 3353 3354 userInfo = [NSDictionary dictionaryWithObject:propertyListFromNSRect(region) 3355 forKey:@"region"]; 3356 [[NSNotificationCenter defaultCenter] postNotificationName:DocViewDragSelect 3357 object:self userInfo:userInfo]; 3358 } 3359 return drawRect; 3360} 3361 3362/* modified: 2009-09-22 (init region.origin) 3363 */ 3364- (void)dragMagnify:(NSEvent *)event 3365{ NSPoint p, last, start; 3366 NSRect visibleRect, region, oldRegion; 3367 BOOL mustContain, shift, canScroll, oldRegionSet = NO; 3368 DocWindow *window = (DocWindow*)[self window]; 3369 3370 region.origin = p = start = [event locationInWindow]; 3371 start = [self convertPoint:start fromView:nil]; 3372 last = start; 3373 3374 shift = ([event modifierFlags] & NSShiftKeyMask) ? YES : NO; 3375 mustContain = ([event modifierFlags] & NSAlternateKeyMask) ? YES : NO; 3376 3377 [self lockFocus]; 3378 3379 visibleRect = [self visibleRect]; 3380 canScroll = !NSEqualRects(visibleRect, [self bounds]); 3381 if (canScroll) 3382 startTimer(&inTimerLoop); 3383 3384 region.size.width = region.size.height = 0.0; 3385 event = [window nextEventMatchingMask:DRAG_MASK]; 3386 while ([event type] != NSLeftMouseUp) 3387 { 3388 if ([event type] == NSPeriodic) 3389 event = periodicEventWithLocationSetToPoint(event, p); 3390 p = [event locationInWindow]; 3391 p = [self convertPoint:p fromView:nil]; 3392 if (p.x != last.x || p.y != last.y) 3393 { 3394 region.origin.x = Min(p.x, start.x); 3395 region.origin.y = Min(p.y, start.y); 3396 region.size.width = Max(p.x, start.x) - region.origin.x; 3397 region.size.height = Max(p.y, start.y) - region.origin.y; 3398 [[self window] disableFlushWindow]; 3399 if (oldRegionSet) 3400 { oldRegion = NSInsetRect(oldRegion , -1.0 , -1.0); 3401 [self drawRect:oldRegion]; 3402 } 3403 if (canScroll) 3404 { [self scrollRectToVisible:region]; 3405 [self scrollPointToVisible:p]; 3406 } 3407 [[NSColor lightGrayColor] set]; 3408 [NSBezierPath setDefaultLineWidth:1.0/scale]; 3409 [NSBezierPath strokeRect:region]; 3410 //NSFrameRectWithWidth(region, 1.0/scale); 3411 [window enableFlushWindow]; 3412 [window flushWindow]; 3413 oldRegion = region; oldRegionSet = YES; 3414 last = p; 3415 PSWait(); 3416 } 3417 p = [event locationInWindow]; 3418 event = [[self window] nextEventMatchingMask:DRAG_MASK]; 3419 } 3420 3421 if (canScroll) 3422 stopTimer(&inTimerLoop); 3423 3424 [self unlockFocus]; 3425 3426 [self setMagnify:NO]; 3427 3428 //[[document scrollView] magnifyRegion:region]; 3429 [(TileScrollView*)[[self superview] superview] magnifyRegion:region]; 3430} 3431 3432- (BOOL)isSelectionEditable 3433{ int l; 3434 3435 for ( l=[layerList count]-1; l>=0; l-- ) 3436 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3437 3438 if ( ![[layerList objectAtIndex:l] editable] || ![slist count] ) 3439 continue; 3440 return YES; 3441 } 3442 return NO; 3443} 3444 3445- (void)delete:(id)sender 3446{ int i, l, selectedObjectsCnt = 0, selectedKnobObjectsToRemoveCnt = 0, selectedKnobObjectsCnt = 0; 3447 VGraphic *graphic; 3448 id change; 3449 NSRect rect, drawRect = NSZeroRect; 3450 3451 if ( ![self isSelectionEditable] ) 3452 return; 3453 3454 for ( l=[slayList count]-1; l>=0; l-- ) 3455 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3456 3457 selectedObjectsCnt += [slist count]; 3458 3459 if ([slist count] == 1) 3460 { VGraphic *gs = [slist objectAtIndex:0]; 3461 3462 if( ([gs isKindOfClass:[VPolyLine class]] || [gs isKindOfClass:[VPath class]]) 3463 && [gs selectedKnobIndex] >= 0 && [gs numPoints] <= 2 ) 3464 selectedKnobObjectsToRemoveCnt++; 3465 3466 if( ([gs isKindOfClass:[VPolyLine class]] || [gs isKindOfClass:[VPath class]]) 3467 && [gs selectedKnobIndex] >= 0 ) 3468 selectedKnobObjectsCnt++; 3469 } 3470 } 3471 if ( selectedObjectsCnt == 1 && ((selectedKnobObjectsCnt == 1 && selectedKnobObjectsToRemoveCnt == 1) || 3472 !selectedKnobObjectsCnt) ) 3473 selectedObjectsCnt++; // little hack 3474 3475 if ( selectedObjectsCnt > 1 ) // we have to remove the selected graphics 3476 { 3477 change = [[DeleteGraphicsChange alloc] initGraphicView:self]; 3478 [change startChange]; 3479 for ( l=[layerList count]-1; l>=0; l-- ) 3480 { LayerObject *layerObject = [layerList objectAtIndex:l]; 3481 NSMutableArray *slist = [slayList objectAtIndex:l]; 3482 3483 if (![layerObject editable] || ![slist count]) 3484 continue; 3485 3486 /* 1st object in list might have been used for the consecutive paste */ 3487 if (originalPaste == [slist objectAtIndex:0]) 3488 originalPaste = nil; 3489 3490 for (i=[slist count]-1; i>=0; i--) 3491 { 3492 graphic = [slist objectAtIndex:i]; 3493 rect = [graphic extendedBoundsWithScale:[self scaleFactor]]; 3494 drawRect = (!drawRect.size.width) ? rect : NSUnionRect(rect, drawRect); 3495/* if (( [graphic isKindOfClass:[VPolyLine class]] && [graphic selectedKnobIndex] >= 0 ) || 3496 ( [graphic isKindOfClass:[VPath class]] && [graphic selectedKnobIndex] >= 0 )) 3497 { 3498 if (![(VPolyLine*)graphic removePointWithNum:[graphic selectedKnobIndex]]) 3499 { [layerObject removeObject:graphic]; // we have removed the last point of graphic 3500 [slist removeObject:graphic]; 3501 } 3502 } 3503 else*/ 3504 { [layerObject removeObject:graphic]; 3505 [slist removeObject:graphic]; 3506 } 3507 } 3508 3509 /* have to recalculate all output, if we remove something from clipping layer */ 3510 if ([layerObject type] == LAYER_CLIPPING) 3511 [layerObject setDirty:YES]; 3512 } 3513 [document setDirty:YES]; 3514 [self cache:drawRect]; 3515 [change endChange]; 3516 } 3517 else // we have to remove only the point of the one selected graphic 3518 { 3519 change = [[RemovePointGraphicsChange alloc] initGraphicView:self]; 3520 [change startChange]; 3521 for ( l=[layerList count]-1; l>=0; l-- ) 3522 { LayerObject *layerObject = [layerList objectAtIndex:l]; 3523 NSMutableArray *slist = [slayList objectAtIndex:l]; 3524 3525 if (![layerObject editable] || ![slist count]) 3526 continue; 3527 3528 /* 1st object in list might have been used for the consecutive paste */ 3529 if (originalPaste == [slist objectAtIndex:0]) 3530 originalPaste = nil; 3531 3532 for (i=[slist count]-1; i>=0; i--) 3533 { 3534 graphic = [slist objectAtIndex:i]; 3535 rect = [graphic extendedBoundsWithScale:[self scaleFactor]]; 3536 drawRect = (!drawRect.size.width) ? rect : NSUnionRect(rect, drawRect); 3537 if (( [graphic isKindOfClass:[VPolyLine class]] && [graphic selectedKnobIndex] >= 0 ) || 3538 ( [graphic isKindOfClass:[VPath class]] && [graphic selectedKnobIndex] >= 0 )) 3539 { 3540 if ( ![(VPolyLine*)graphic removePointWithNum:[graphic selectedKnobIndex]] ) 3541 NSLog(@"- delete: we remove the last point of graphic"); 3542 } 3543 } 3544 3545 /* have to recalculate all output, if we remove something from clipping layer */ 3546 if ([layerObject type] == LAYER_CLIPPING) 3547 [layerObject setDirty:YES]; 3548 } 3549 [document setDirty:YES]; 3550 [self cache:drawRect]; 3551 [change endChange]; 3552 } 3553} 3554 3555/* 3556 * Selects all the items in the layerlist. 3557 */ 3558- (void)selectAll:(id)sender redraw:(BOOL)redraw 3559{ int i, iCnt, l; 3560 VGraphic *g; 3561 3562 for ( l=[layerList count]-1; l>=0; l-- ) 3563 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3564 NSMutableArray *list = [[layerList objectAtIndex:l] list]; 3565 3566 [slist removeAllObjects]; 3567 /* only select layers which are displayed and editable, exception: CAM-Module */ 3568 if ( ![(LayerObject*)[layerList objectAtIndex:l] state] || !(iCnt=[list count]) 3569 || (!Prefs_SelectNonEditable && ![[layerList objectAtIndex:l] editable]) ) 3570 continue; 3571 3572 for (i=0; i<iCnt; i++) 3573 { 3574 g = [list objectAtIndex:i]; 3575 [g setSelected:YES]; 3576 [slist addObject:g]; 3577 } 3578 } 3579 if ( redraw ) 3580 [self flatRedraw:[self bounds]]; 3581 [[(App*)NSApp inspectorPanel] loadList:slayList]; 3582} 3583- (void)selectAll:(id)sender 3584{ 3585 [self selectAll:sender redraw:YES]; 3586} 3587 3588/* 3589 * Deselects all the items in the slayList. 3590 */ 3591- (void)deselectAll:sender redraw:(BOOL)redraw 3592{ NSRect sbounds; 3593 int l; 3594 BOOL deselected = NO; 3595 3596 if (redraw) 3597 sbounds = [self boundsOfArray:slayList]; 3598 for ( l=[slayList count]-1; l>=0; l-- ) 3599 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3600 3601 if ( [slist count] > 0 ) 3602 { int i; 3603 3604 for ( i=[slist count]-1; i>=0; i-- ) 3605 [[slist objectAtIndex:i] setSelected:NO]; 3606 [slist removeAllObjects]; 3607 deselected = YES; 3608 } 3609 } 3610 if ( redraw && deselected ) 3611 { 3612 [self flatRedraw:sbounds]; 3613 if ( sender != self ) 3614 [[self window] flushWindow]; 3615 } 3616} 3617- (void)deselectAll:sender 3618{ 3619 [self deselectAll:sender redraw:YES]; 3620} 3621 3622- (void)deselectLockedLayers:(BOOL)lockedLayers lockedObjects:(BOOL)lockedObjects 3623{ int l, i; 3624 3625 for ( l=[slayList count]-1; l>=0; l-- ) 3626 { LayerObject *layer = [layerList objectAtIndex:l]; 3627 NSMutableArray *slist = [slayList objectAtIndex:l]; 3628 3629 if ( [slist count] > 0 ) 3630 { 3631 for ( i=[slist count]-1; i>=0; i-- ) 3632 { VGraphic *g = [slist objectAtIndex:i]; 3633 3634 if ((lockedLayers && ![layer editable]) || (lockedObjects && [g isLocked])) 3635 { 3636 [g setSelected:NO]; 3637 [slist removeObjectAtIndex:i]; 3638 } 3639 } 3640 } 3641 } 3642} 3643 3644/* 3645 * Selects all the items in the layerList equal to those in slayList. 3646 */ 3647#define MAXCLASSES 10 3648- (void)selectEqual:sender 3649{ int gcnt, scnt, i, j, l, c=0; 3650 id classes[MAXCLASSES]; 3651 3652 if (![slayList count]) 3653 return; 3654 3655 for (l=[layerList count]-1; l>=0; l--) 3656 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3657 NSMutableArray *list = [[layerList objectAtIndex:l] list]; 3658 3659 if ( ![list count]) 3660 continue; 3661 3662 if ( !(scnt=[slist count]) ) 3663 continue; 3664 for ( i=0; i<scnt; i++ ) 3665 { id sg = [slist objectAtIndex:i]; 3666 3667 for ( j=0; j<c; j++ ) 3668 if ( [sg isMemberOfClass:classes[j]] ) 3669 { j = MAXCLASSES+1; 3670 break; 3671 } 3672 if ( j > MAXCLASSES ) /* not in class list */ 3673 continue; 3674 3675 classes[c++] = [sg class]; 3676 } 3677 3678 gcnt = [list count]; 3679 for (i=0; i<gcnt; i++) 3680 { id gg = [list objectAtIndex:i]; 3681 3682 if (![gg isSelected]) 3683 { 3684 for (j=0; j<c; j++) 3685 if ([gg isMemberOfClass:classes[j]]) 3686 break; 3687 if (j >= c) /* not in class list */ 3688 continue; 3689 [gg setSelected:YES]; 3690 [slist addObject:gg]; 3691 } 3692 } 3693 } 3694 3695 [self flatRedraw:[self bounds]]; 3696} 3697 3698/* 3699 * Selects all the items in the layerList equal to those in slayList. 3700 */ 3701#define MAXCOLORS 20 3702- (void)selectColor:sender 3703{ int gcnt, scnt, i, j, l, c=0, fs[MAXCOLORS]; 3704 float ws[MAXCOLORS]; 3705 NSColor *cols[MAXCOLORS]; 3706 NSColor *fcols[MAXCOLORS]; 3707 NSColor *ecols[MAXCOLORS]; 3708 3709 if (![slayList count]) 3710 return; 3711 3712 for (l=[layerList count]-1; l>=0; l--) 3713 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3714 NSMutableArray *list = [[layerList objectAtIndex:l] list]; 3715 3716 if (![list count]) 3717 continue; 3718 3719 if (!(scnt=[slist count])) 3720 continue; 3721 for (i=0; i<scnt; i++) 3722 { VGraphic *sg = [slist objectAtIndex:i]; 3723 float sgw = [sg width]; 3724 int sgf = [sg filled]; 3725 3726 //if ([sg isKindOfClass:[VGroup class]]) // VGroup perhaps filled but no fillColor ! 3727 // continue; 3728 for (j=0; j<c; j++) 3729 if (((((ws[j] && sgw) || (!fs[j] && !sgf)) && [cols[j] isEqual:[sg color]]) 3730 || (!ws[j] && !sgw && fs[j] && fs[j] == sgf)) && 3731 ((fs[j] >= 1 && fs[j] == sgf && [fcols[j] isEqual:[(VPath*)sg fillColor]]) || (!fs[j] && !sgf)) && 3732 ((fs[j] > 1 && fs[j] == sgf && [ecols[j] isEqual:[(VPath*)sg endColor ]]) || (fs[j] <= 1 && fs[j] == sgf))) 3733 //if ( [cols[j] isEqual:[sg color]] ) 3734 { j = MAXCOLORS+1; 3735 break; 3736 } 3737 if (j > MAXCOLORS) /* not in class list */ 3738 continue; 3739 3740 ws[c] = [sg width]; 3741 cols[c] = [sg color]; 3742 fs[c++] = [sg filled]; // NO if nothing to fill 3743 if ([sg respondsToSelector:@selector(fillColor)]) 3744 { 3745 fcols[c-1] = [(VPath*)sg fillColor]; 3746 ecols[c-1] = [(VPath*)sg endColor]; 3747 } 3748 } 3749 if (!c) 3750 return; 3751 3752 gcnt = [list count]; 3753 for (i=0; i<gcnt; i++) 3754 { VGraphic *gg = [list objectAtIndex:i]; 3755 3756 if (![gg isSelected]) 3757 { float ggw = [gg width]; 3758 int ggf = [gg filled]; 3759 3760 for ( j=0; j<c; j++ ) 3761 if (((((ws[j] && ggw) || (!fs[j] && !ggf)) && 3762 [cols[j] isEqual:[gg color]]) || (!ws[j] && !ggw && fs[j] && fs[j] == ggf)) && 3763 ((fs[j] >= 1 && fs[j] == ggf && [fcols[j] isEqual:[(VPath*)gg fillColor]]) || (!fs[j] && !ggf)) && 3764 ((fs[j] > 1 && fs[j] == ggf && [ecols[j] isEqual:[(VPath*)gg endColor ]]) || (fs[j] <= 1 && fs[j] == ggf))) 3765 //if ( [colors[j] isEqual:[gg color]] ) 3766 break; 3767 if (j >= c) /* not in class list */ 3768 continue; 3769 [gg setSelected:YES]; 3770 [slist addObject:gg]; 3771 } 3772 } 3773 } 3774 3775 [self flatRedraw:[self bounds]]; 3776} 3777 3778- (void)bringToFront:sender 3779{ int scnt, i, l; 3780 id change; 3781 3782 if (![slayList count]) 3783 return; 3784 3785 change = [[BringToFrontGraphicsChange alloc] initGraphicView:self]; 3786 [change startChange]; 3787 for (l=[layerList count]-1; l>=0; l--) 3788 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3789 LayerObject *layerObject = [layerList objectAtIndex:l]; 3790 3791 if (![[layerObject list] count]) 3792 continue; 3793 3794 if (!(scnt=[slist count])) 3795 continue; 3796 for (i=0; i<scnt; i++) 3797 { id sg = [slist objectAtIndex:i]; 3798 3799 [layerObject removeObject:sg]; 3800 [layerObject addObject:sg]; 3801 } 3802 } 3803 [self drawAndDisplay]; 3804 [change endChange]; 3805} 3806- (void)bringForward:sender 3807{ int scnt, i, l; 3808 id change; 3809 3810 if (![slayList count]) 3811 return; 3812 3813 change = [[BringToFrontGraphicsChange alloc] initGraphicView:self]; 3814 [change startChange]; 3815 for (l=[layerList count]-1; l>=0; l--) 3816 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3817 LayerObject *layerObject = [layerList objectAtIndex:l]; 3818 3819 if (![[layerObject list] count]) 3820 continue; 3821 3822 if (!(scnt=[slist count])) 3823 continue; 3824 for (i=0; i<scnt; i++) 3825 { id sg = [slist objectAtIndex:i]; 3826 int location = [[layerObject list] indexOfObject:sg]; 3827 3828 [layerObject removeObject:sg]; 3829 [layerObject insertObject:sg atIndex: 3830 (location+1 > (int)[[layerObject list] count]) ? ((int)[[layerObject list] count]-1) 3831 : (location+1)]; 3832 } 3833 } 3834 [self drawAndDisplay]; 3835 [change endChange]; 3836} 3837 3838- (void)sendToBack:sender 3839{ int scnt, i, l; 3840 id change; 3841 3842 if (![slayList count]) 3843 return; 3844 3845 change = [[SendToBackGraphicsChange alloc] initGraphicView:self]; 3846 [change startChange]; 3847 for (l=[layerList count]-1; l>=0; l--) 3848 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3849 LayerObject *layerObject = [layerList objectAtIndex:l]; 3850 3851 if (![[layerObject list] count]) 3852 continue; 3853 if (!(scnt=[slist count])) 3854 continue; 3855 for (i=0; i<scnt; i++) 3856 { id sg = [slist objectAtIndex:i]; 3857 3858 [layerObject removeObject:sg]; 3859 [layerObject insertObject:sg atIndex:0]; 3860 } 3861 } 3862 [self drawAndDisplay]; 3863 [change endChange]; 3864} 3865- (void)sendBackward:sender 3866{ int scnt, i, l; 3867 id change; 3868 3869 if (![slayList count]) 3870 return; 3871 3872 change = [[SendToBackGraphicsChange alloc] initGraphicView:self]; 3873 [change startChange]; 3874 for (l=[layerList count]-1; l>=0; l--) 3875 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3876 LayerObject *layerObject = [layerList objectAtIndex:l]; 3877 3878 if (![[layerObject list] count]) 3879 continue; 3880 if (!(scnt=[slist count])) 3881 continue; 3882 for (i=0; i<scnt; i++) 3883 { id sg = [slist objectAtIndex:i]; 3884 int location = [[layerObject list] indexOfObject:sg]; 3885 3886 [layerObject removeObject:sg]; 3887 [layerObject insertObject:sg atIndex:(location-1 < 0) ? 0 : (location-1)]; 3888 } 3889 } 3890 [self drawAndDisplay]; 3891 [change endChange]; 3892} 3893 3894- (void)changeFont:(id)sender 3895{ int iCnt, i, l; 3896 3897 if (![slayList count]) 3898 return; 3899 3900 for (l=[layerList count]-1; l>=0; l--) 3901 { NSMutableArray *slist = [slayList objectAtIndex:l]; 3902 3903 if (!(iCnt=[slist count])) 3904 continue; 3905 3906 for (i=0; i<iCnt; i++) 3907 { id sg = [slist objectAtIndex:i]; 3908 3909 if ( [sg isKindOfClass:[VText class]] ) 3910 { [sg setFont:[sender convertFont:[sg font]]]; 3911 [[layerList objectAtIndex:l] setDirty:YES]; 3912 } 3913 } 3914 } 3915 3916 [self drawAndDisplay]; 3917} 3918 3919/* turn on/off coordinate display of the document window 3920 * created: 2007-05-04 3921 */ 3922- (void)toggleCoordDisplay:sender 3923{ 3924 [(DocWindow*)[document window] enableCoordDisplay:([(NSMenuItem*)sender tag] ? YES : NO)]; 3925} 3926 3927- (void)displayDirections:sender 3928{ 3929 if ([sender respondsToSelector:@selector(tag)]) 3930 showDirection = [(NSMenuItem*)sender tag] ? YES : NO; 3931 [self drawAndDisplay]; 3932} 3933- (BOOL)showDirection { return showDirection; } 3934- (void)setDirectionForLayer:(LayerObject*)layerObject 3935{ int i, cnt; 3936 BOOL ccw = YES; /* outside correction = ccw */ 3937 NSArray *list = [layerObject list]; 3938 3939 if ( [layerObject side] == CUT_INSIDE ) 3940 ccw = !ccw; 3941 if ( [layerObject revertDirection] ) 3942 ccw = !ccw; 3943 3944 /* set direction for objects */ 3945 for ( i=0, cnt=[list count]; i<cnt; i++ ) 3946 { id g = [list objectAtIndex:i]; 3947 3948 if ( [g respondsToSelector:@selector(setDirectionCCW:)] ) 3949 [g setDirectionCCW:ccw]; 3950 } 3951 [self drawAndDisplay]; 3952} 3953 3954/*BOOL vhfUpdateMenuItem(id <NSMenuItem> menuItem, NSString *zeroItem, NSString *oneItem, BOOL state) 3955{ 3956 if (state) 3957 { 3958 if ([menuItem tag] != 0) 3959 { 3960 [menuItem setTitleWithMnemonic:zeroItem]; 3961 [menuItem setTag:0]; 3962 [menuItem setEnabled:NO]; // causes it to get redrawn 3963 } 3964 } 3965 else if ([menuItem tag] != 1) 3966 { 3967 [menuItem setTitleWithMnemonic:oneItem]; 3968 [menuItem setTag:1]; 3969 [menuItem setEnabled:NO]; // causes it to get redrawn 3970 } 3971 return YES; 3972}*/ 3973 3974/* Can be called to see if the specified action is valid on this view now. 3975 * It returns NO if the GraphicView knows that action is not valid now, 3976 * otherwise it returns YES. Note the use of the Pasteboard change 3977 * count so that the GraphicView does not have to look into the Pasteboard 3978 * every time paste: is validated. 3979 * 3980 * created: 1996-10-19 3981 * modified: 2011-04-07 (pathSetStartPoint) 3982 * 2007-07-25 (optimizeMoves queries removed) 3983 */ 3984//- (BOOL)validateMenuItem:(id <NSMenuItem>)anItem 3985- (BOOL)validateMenuItem:(NSMenuItem*)anItem 3986{ int i, iCnt, l, lCnt, cnt; 3987 SEL action = [anItem action]; 3988 static BOOL pboardHasPasteableType = NO; 3989 static int cachedPasteboardChangeCount = -1; 3990 3991 if ( VHFSelectorIsEqual(action, @selector(bringToFront:)) || 3992 VHFSelectorIsEqual(action, @selector(bringForward:)) ) 3993 { lCnt = [layerList count]; 3994 for (l=0; l<lCnt; l++) 3995 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 3996 NSMutableArray *slist = [slayList objectAtIndex:l]; 3997 3998 if ( [[layerList objectAtIndex:l] editable] ) 3999 /*&& (!Prefs_OptimizeMoves || ![self respondsToSelector:@selector(optimizeMoves:)]) )*/ 4000 { 4001 if ((iCnt = [slist count]) && (cnt=[list count]) > iCnt) 4002 { 4003 for (i=1; i<=iCnt; i++) 4004 if ([slist objectAtIndex:iCnt-i] != [list objectAtIndex:cnt-i]) 4005 return YES; 4006 } 4007 } 4008 } 4009 return NO; 4010 } 4011 else if ( VHFSelectorIsEqual(action, @selector(sendToBack:)) || 4012 VHFSelectorIsEqual(action, @selector(sendBackward:)) ) 4013 { lCnt = [layerList count]; 4014 for (l=0; l<lCnt; l++) 4015 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 4016 NSMutableArray *slist = [slayList objectAtIndex:l]; 4017 4018 if ( [[layerList objectAtIndex:l] editable] ) 4019 /*&& (!Prefs_OptimizeMoves || ![self respondsToSelector:@selector(optimizeMoves:)]) )*/ 4020 { 4021 if ((iCnt = [slist count]) && (int)[list count] > iCnt) 4022 { 4023 for (i=0; i<iCnt; i++) 4024 if ([slist objectAtIndex:i] != [list objectAtIndex:i]) 4025 return YES; 4026 } 4027 } 4028 } 4029 return NO; 4030 } 4031 else if ( VHFSelectorIsEqual(action, @selector(toggleGrid:)) ) 4032 { 4033 return (gridSpacing > 0) ? vhfUpdateMenuItem(anItem, HIDE_GRID, SHOW_GRID, [self gridIsEnabled]) : NO; 4034 } 4035 /* we need at least two selected objects */ 4036 else if ( VHFSelectorIsEqual(action, @selector(group:)) 4037 // || VHFSelectorIsEqual(action == @selector(align:)) 4038 || VHFSelectorIsEqual(action, @selector(join:)) ) 4039 { 4040 for (l=0, lCnt = [slayList count]; l<lCnt; l++) 4041 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4042 4043 if ( [[layerList objectAtIndex:l] editable] && [slist count] > 1 ) 4044 return YES; 4045 } 4046 return NO; 4047 } 4048 /* we need at least two selected and one filled objects */ 4049 else if ( VHFSelectorIsEqual(action, @selector(punch:)) ) 4050 { 4051 for (l=0, lCnt = [slayList count]; l<lCnt; l++) 4052 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4053 4054 /* we need at least one filled graphic */ 4055 if ( [[layerList objectAtIndex:l] editable] && [slist count] > 1 ) 4056 for (i=[slist count]-1; i>=0; i--) 4057 { 4058 if ( [[slist objectAtIndex:i] filled]) 4059 return YES; 4060 else if ([[slist objectAtIndex:i] isKindOfClass:[VGroup class]]) 4061 { int j, gCnt = [[slist objectAtIndex:i] countRecursive]; 4062 4063 for (j=0; j < gCnt; j++) 4064 if ([[[slist objectAtIndex:i] recursiveObjectAtIndex:j] filled]) 4065 return YES; 4066 } 4067 } 4068 } 4069 return NO; 4070 } 4071 /* we need a selected group */ 4072 else if ( VHFSelectorIsEqual(action, @selector(ungroup:)) ) 4073 { 4074 for (l=0, lCnt = [slayList count]; l<lCnt; l++) 4075 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4076 4077 if ( [[layerList objectAtIndex:l] editable] && [slist count] ) 4078 for (i=[slist count]-1; i>=0; i--) 4079 if ( [[slist objectAtIndex:i] isMemberOfClass:[VGroup class]] ) 4080 return YES; 4081 } 4082 return NO; 4083 } 4084 /* we need a selected path */ 4085 else if ( VHFSelectorIsEqual(action, @selector(split:)) ) 4086 { 4087 for (l=0, lCnt = [slayList count]; l<lCnt; l++) 4088 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4089 4090 if ( [[layerList objectAtIndex:l] editable] && [slist count] && 4091 ([[slist objectAtIndex:0] isMemberOfClass:[VPath class]] || 4092 [[slist objectAtIndex:0] isMemberOfClass:[VTextPath class]] || 4093 [[slist objectAtIndex:0] isMemberOfClass:[VImage class]]) ) 4094 return YES; 4095 } 4096 return NO; 4097 } 4098 /* we need a selected text */ 4099 else if ( VHFSelectorIsEqual(action, @selector(flatten:)) ) 4100 { 4101 for (l=0, lCnt = [slayList count]; l<lCnt; l++) 4102 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4103 4104 if ( [[layerList objectAtIndex:l] editable] && [slist count] && 4105 ([[slist objectAtIndex:0] isMemberOfClass:[VText class]] || 4106 [[slist objectAtIndex:0] isMemberOfClass:[VTextPath class]]) ) 4107 return YES; 4108 } 4109 return NO; 4110 } 4111 /* we need a text being edited */ 4112 else if ( VHFSelectorIsEqual(action, @selector(addLink:)) ) 4113 { 4114 if ( [[self window] fieldEditor:NO forObject:nil] == [[self window] firstResponder] ) 4115 return YES; 4116 return NO; 4117 } 4118 /* bindTextToPath: we need one selected text and one of path, line, arc, curve */ 4119 else if ( VHFSelectorIsEqual(action, @selector(bindTextToPath:)) ) 4120 { 4121 for ( l=0, lCnt = [slayList count]; l<lCnt; l++ ) 4122 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4123 4124 if ( [[layerList objectAtIndex:l] editable] && [slist count]==2 && 4125 ([[slist objectAtIndex:0] isMemberOfClass:[VText class]] || 4126 [[slist objectAtIndex:1] isMemberOfClass:[VText class]]) && 4127 ([VTextPath canBindToObject:[slist objectAtIndex:0]] || 4128 [VTextPath canBindToObject:[slist objectAtIndex:1]]) ) 4129 return YES; 4130 } 4131 return NO; 4132 } 4133 /* pathSetStartPoint: we need one selected path */ 4134 else if ( VHFSelectorIsEqual(action, @selector(pathSetStartPoint:)) ) 4135 { 4136 for ( l=0, lCnt = [slayList count]; l<lCnt; l++ ) 4137 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4138 4139 if ( [[layerList objectAtIndex:l] editable] && [slist count]==1 && 4140 [[slist objectAtIndex:0] isMemberOfClass:[VPath class]] ) 4141 return YES; 4142 } 4143 return NO; 4144 } 4145 /* we need at least one selected object */ 4146 else if ( VHFSelectorIsEqual(action, @selector(copy:)) ) 4147 { 4148 for (l=0, lCnt = [slayList count]; l<lCnt; l++) 4149 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4150 4151 if ( [slist count] ) 4152 return YES; 4153 } 4154 return NO; 4155 } 4156 /* we need at least one selected object on an editable layer */ 4157 else if ( VHFSelectorIsEqual(action, @selector(mirror:)) 4158 || VHFSelectorIsEqual(action, @selector(rotateG:)) 4159 || VHFSelectorIsEqual(action, @selector(reverse:)) 4160 || VHFSelectorIsEqual(action, @selector(buildContour:)) 4161 || VHFSelectorIsEqual(action, @selector(delete:)) 4162 || VHFSelectorIsEqual(action, @selector(selectColor:)) 4163 || VHFSelectorIsEqual(action, @selector(selectEqual:)) 4164 || VHFSelectorIsEqual(action, @selector(cut:)) 4165 || VHFSelectorIsEqual(action, @selector(transform:)) ) 4166 { 4167 for (l=0, lCnt = [slayList count]; l<lCnt; l++) 4168 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4169 4170 if ( [[layerList objectAtIndex:l] editable] && [slist count] ) 4171 return YES; 4172 } 4173 return NO; 4174 } 4175 /* we need at least one unselected object */ 4176 else if ( VHFSelectorIsEqual(action, @selector(selectAll:)) ) 4177 { 4178 for (l=0, lCnt = [layerList count]; l<lCnt; l++) 4179 { NSMutableArray *list = [[layerList objectAtIndex:l] list]; 4180 NSMutableArray *slist = [slayList objectAtIndex:l]; 4181 4182 if ( /*[[layerList objectAtIndex:l] editable] &&*/ ([slist count] != [list count]) ) 4183 return YES; 4184 } 4185 return NO; 4186 } 4187 /* we need something to paste */ 4188 else if ( VHFSelectorIsEqual(action, @selector(paste:)) ) 4189 { NSPasteboard *pb = [NSPasteboard generalPasteboard]; 4190 4191 cnt = [pb changeCount]; 4192 if (cnt != cachedPasteboardChangeCount) 4193 { cachedPasteboardChangeCount = cnt; 4194 pboardHasPasteableType = (e2CenonPasteType([pb types]) != NULL); 4195 } 4196 return pboardHasPasteableType; 4197 } 4198 else if ( VHFSelectorIsEqual(action, @selector(displayDirections:)) ) 4199 return vhfUpdateMenuItem(anItem, HIDE_DIRECTION, SHOW_DIRECTION, [self showDirection]); 4200 else if ( VHFSelectorIsEqual(action, @selector(toggleCoordDisplay:)) ) 4201 return vhfUpdateMenuItem(anItem, HIDE_COORDS, SHOW_COORDS, 4202 [(DocWindow*)[document window] hasCoordDisplay]); 4203 4204 [[NSNotificationCenter defaultCenter] postNotificationName:DocViewUpdateMenuItem 4205 object:anItem userInfo:statusDict]; 4206 4207 return YES; 4208} 4209 4210/* created: 1995-12-03 4211 * modified: 2005-03-12 (Performance map recreation) 4212 * purpose: group objects 4213 * allocate new group object 4214 * add slayList to group 4215 * remove objects in slayList from list 4216 */ 4217- (void)group:sender 4218{ VGroup *group; 4219 int i, iCnt, l; 4220 id change; 4221 4222 /* deselect everything which will not be part of the group(s) */ 4223 for (l=[layerList count]-1; l>=0; l--) 4224 { LayerObject *layer = [layerList objectAtIndex:l]; 4225 NSMutableArray *slist = [slayList objectAtIndex:l]; 4226 4227 if (![[layer list] count] || ![layer editable] || [slist count] < 2) 4228 [slist removeAllObjects]; 4229 } 4230 4231 /* group */ 4232 change = [[GroupGraphicsChange alloc] initGraphicView:self]; 4233 [change startChange]; 4234 for (l=[layerList count]-1; l>=0; l--) 4235 { LayerObject *layer = [layerList objectAtIndex:l]; 4236 NSMutableArray *slist = [slayList objectAtIndex:l], *llist = [layer list]; 4237 PerformanceMap *lpm; 4238 4239 if (![[layer list] count] || ![layer editable]) 4240 continue; 4241 if ([slist count] < 2) // we need at least two objects for a group 4242 continue; 4243 4244 /* 1st object in list may be used for the consecutive paste */ 4245 originalPaste = nil; 4246 4247 group = [VGroup group]; 4248 [change noteGroup:group layer:layer]; 4249 for (i=0, iCnt = [llist count]; i<iCnt; i++) 4250 { id obj = [llist objectAtIndex:i]; 4251 4252 if ([slist indexOfObject:obj] != NSNotFound) 4253 [group addObject:obj]; 4254 } 4255 for (i=[slist count]-1; i>=0; i--) 4256 [layer removeObject:[slist objectAtIndex:i]]; 4257 4258 /* hier Neuaufbau der performanceMap der layer - weil Group sonst evtl viel zu oft gemalt wird, 4259 * wenn viel kleines zur Gruppe wird (viel kleines, viele maps), 4260 * neuaufbau macht im ganzen weniger maps ! 4261 */ 4262 if ( (lpm = [layer performanceMap]) ) 4263 [lpm sortNewInFrame:[lpm bounds] initWithList:llist]; 4264 4265 [layer addObject:group]; 4266 [slist removeAllObjects]; 4267 [slist addObject:group]; 4268 [group setSelected:YES]; 4269 } 4270 [change endChange]; 4271 4272 [document setDirty:YES]; 4273 [self drawAndDisplay]; 4274 [[(App*)NSApp inspectorPanel] loadList:slayList]; 4275} 4276 4277/* created: 1995-12-03 4278 * modified: 2012-01-05 4279 * purpose: ungroup objects 4280 * ungroup all groups in slayList 4281 * remove the groups from list 4282 * select all ungrouped objects 4283 */ 4284- (void)ungroup:sender 4285{ int i, iCnt, l; 4286 NSMutableArray *newSlist; 4287 id change; 4288 4289 change = [[UngroupGraphicsChange alloc] initGraphicView:self]; 4290 [change startChange]; 4291 for (l=[slayList count]-1; l>=0; l--) 4292 { LayerObject *layerObject = [layerList objectAtIndex:l]; 4293 NSMutableArray *slist = [slayList objectAtIndex:l]; 4294 4295 if (![slist count] || ![layerObject editable]) 4296 continue; 4297 4298 newSlist = [NSMutableArray array]; 4299 for (i=[slist count]-1; i>=0; i--) 4300 { id g = [slist objectAtIndex:i]; 4301 4302 if ([g isMemberOfClass:[VGroup class]]) 4303 { 4304 [g ungroupTo:newSlist]; 4305 [layerObject removeObject:g]; 4306 } 4307 else 4308 [g setSelected:NO]; 4309 } 4310 [slayList replaceObjectAtIndex:l withObject:newSlist]; 4311 for (i=0, iCnt = [newSlist count]; i<iCnt; i++) 4312 [layerObject addObject:[newSlist objectAtIndex:i]]; 4313 } 4314 [change endChange]; 4315 4316 [document setDirty:YES]; 4317 [self drawAndDisplay]; 4318} 4319 4320/* created: 1995-09-19 4321 * modified: 2012-02-28 (copy path - for correct undo) 4322 * purpose: join two objects 4323 */ 4324- (void)joinSelection:(id)change messages:(BOOL)messages 4325{ int i, iCnt, l; 4326 PerformanceMap *lpm; 4327 4328 for ( l=[slayList count]-1; l>=0; l-- ) 4329 { LayerObject *layerObject = [layerList objectAtIndex:l]; 4330 NSMutableArray *slist = [slayList objectAtIndex:l]; 4331 VGraphic *obj1 = 0, *obj2 = 0; 4332 BOOL complex = NO, filled = NO; 4333 4334 if ( ![slist count] || ![layerObject editable] ) 4335 continue; 4336 if ( [slist count] < 2 ) 4337 { 4338 if ( messages ) 4339 NSRunAlertPanel(@"", SELECT2FORJOIN_STRING, OK_STRING, nil, nil); 4340 return; 4341 } 4342 4343 /* 1st object in list may be used for the consecutive paste */ 4344 originalPaste = nil; 4345 4346 /* join image and path to clip image from path 4347 */ 4348 if ( [slist count] == 2 && ([[slist objectAtIndex:0] isMemberOfClass:[VImage class]] || 4349 [[slist objectAtIndex:1] isMemberOfClass:[VImage class]]) ) 4350 { id imageObj, clipObj; 4351 4352 imageObj = ([[slist objectAtIndex:0] isMemberOfClass:[VImage class]]) ? 4353 [slist objectAtIndex:0] : [slist objectAtIndex:1]; 4354 clipObj = ([[slist objectAtIndex:0] isMemberOfClass:[VImage class]]) ? 4355 [slist objectAtIndex:1] : [slist objectAtIndex:0]; 4356 if (([clipObj isMemberOfClass:[VPath class]] && [clipObj closed]) || 4357 ([clipObj isMemberOfClass:[VArc class]] && Abs([clipObj angle]) == 360.0) || 4358 ([clipObj isMemberOfClass:[VPolyLine class]] && 4359 SqrDistPoints([clipObj pointWithNum:0], [clipObj pointWithNum:MAXINT]) < TOLERANCE) || 4360 [clipObj isMemberOfClass:[VRectangle class]]) 4361 { VImage *nImageObj = [imageObj copy]; // for undo - should not change the pointer of objects 4362 4363 [change notePathBefore:imageObj]; 4364 [nImageObj join:clipObj]; 4365 [slist removeObject:clipObj]; 4366 [slist removeObject:imageObj]; 4367 [layerObject removeObject:clipObj]; 4368 [layerObject removeObject:imageObj]; 4369 [layerObject addObject:nImageObj]; 4370 [nImageObj setSelected:YES]; 4371 [slist addObject:nImageObj]; 4372 [change notePath:nImageObj]; 4373 } 4374 return; 4375 } 4376 4377 for (i=0, iCnt = [slist count]; i<iCnt; i++) 4378 { id obj = [slist objectAtIndex:i]; 4379 4380 if ( [obj isMemberOfClass:[VPath class]] || [obj isMemberOfClass:[VCurve class]] || 4381 [obj isMemberOfClass:[VLine class]] || [obj isMemberOfClass:[VArc class]] || 4382 [obj isMemberOfClass:[VRectangle class]] || [obj isMemberOfClass:[VPolyLine class]] ) 4383 { 4384 if (!obj1) 4385 obj1 = obj; 4386 else if (!obj2) 4387 obj2 = obj; 4388 else 4389 complex = YES; 4390 if ( [obj filled] ) 4391 filled = YES; 4392 } 4393 else 4394 { [slist removeObject:obj]; 4395 i--; iCnt--; 4396 } 4397 } 4398 if ( ![obj1 isMemberOfClass:[VPath class]] && ![obj1 isMemberOfClass:[VPolyLine class]] && 4399 ([obj2 isMemberOfClass:[VPath class]] || [obj2 isMemberOfClass:[VPolyLine class]]) ) 4400 { id o = obj1; obj1 = obj2; obj2 = o; } 4401 4402 /* if both polylines are closed - we must join both in one path ! - set complex = YES */ 4403 if ( [obj1 isMemberOfClass:[VPolyLine class]] && !filled && !complex && 4404 [obj2 isMemberOfClass:[VPolyLine class]] ) 4405 { NSPoint p0, p1; 4406 4407 p0 = [obj1 pointWithNum:0]; 4408 p1 = [obj1 pointWithNum:[(VPolyLine*)obj1 ptsCount]-1]; 4409 if ( Diff(p0.x, p1.x) <= TOLERANCE && Diff(p0.y, p1.y) <= TOLERANCE ) 4410 { 4411 p0 = [obj2 pointWithNum:0]; 4412 p1 = [obj2 pointWithNum:[(VPolyLine*)obj2 ptsCount]-1]; 4413 if ( Diff(p0.x, p1.x) <= TOLERANCE && Diff(p0.y, p1.y) <= TOLERANCE ) 4414 complex = YES; 4415 } 4416 } 4417 4418 if ( [slist count] < 2 ) 4419 { 4420 if ( messages ) 4421 NSRunAlertPanel(@"", SELECT2FORJOIN_STRING, OK_STRING, nil, nil); 4422 } 4423 else if ( [obj1 isMemberOfClass:[VPath class]] ) 4424 { VPath *area = [obj1 copy]; // for undo - should not change the pointer of objects 4425 4426 [change notePathBefore:obj1]; 4427 [layerObject addObject:area]; 4428 if (complex) 4429 { 4430 for (i=[slist count]-1; i>=0; i--) 4431 [layerObject removeObject:[slist objectAtIndex:i]]; 4432 4433 [slist removeObject:obj1]; // sonst ist der doppelt 4434 [area join:slist]; 4435 4436 /* hier Neuaufbau der performanceMap der layer */ 4437 if ( (lpm = [layerObject performanceMap]) ) 4438 [lpm sortNewInFrame:[lpm bounds] initWithList:[layerObject list]]; 4439 4440 [slist removeAllObjects]; 4441 [area setSelected:YES]; 4442 [slist addObject:area]; 4443 } 4444 else 4445 { [area join:obj2]; 4446 [slist removeObject:obj1]; 4447 [slist removeObject:obj2]; 4448 [layerObject removeObject:obj1]; 4449 [layerObject removeObject:obj2]; 4450 [area setSelected:YES]; 4451 [slist addObject:area]; 4452 } 4453 [change notePath:area]; 4454 } 4455 else if ( [obj1 isMemberOfClass:[VPolyLine class]] && !filled && !complex && 4456 ([obj2 isMemberOfClass:[VPolyLine class]] || [obj2 isMemberOfClass:[VLine class]]) ) 4457 { VPolyLine *area = [obj1 copy]; // for undo - should not change the pointer of objects 4458 4459 [change notePathBefore:obj1]; 4460 [area join:obj2]; 4461 [slist removeObject:obj1]; 4462 [slist removeObject:obj2]; 4463 [layerObject removeObject:obj1]; 4464 [layerObject removeObject:obj2]; 4465 [layerObject addObject:area]; 4466 [change notePath:area]; 4467 [area setSelected:YES]; 4468 [slist addObject:area]; 4469 } 4470 else /* build new area */ 4471 { VPath *area = [VPath path]; 4472 4473 [change notePath:area]; 4474 [area setColor:[(VGraphic*)obj1 color]]; 4475 [area setFillColor:[(VGraphic*)obj1 color]]; 4476 [area setWidth:[obj1 width]]; 4477 [area setFilled:filled]; 4478 if (complex) 4479 { 4480 for (i=[slist count]-1; i>=0; i--) 4481 if ([slist objectAtIndex:i] != area ) 4482 [layerObject removeObject:[slist objectAtIndex:i]]; 4483 4484 /* hier Neuaufbau der performanceMap der layer */ 4485 if ( (lpm = [layerObject performanceMap]) ) 4486 [lpm sortNewInFrame:[lpm bounds] initWithList:[layerObject list]]; 4487 4488 [area join:slist]; 4489 [slist removeAllObjects]; 4490 } 4491 else 4492 { [area join:obj1]; 4493 [area join:obj2]; 4494 [layerObject removeObject:obj1]; 4495 [layerObject removeObject:obj2]; 4496 [slist removeObject:obj1]; 4497 [slist removeObject:obj2]; 4498 } 4499 [area setSelected:YES]; 4500 [layerObject addObject:area]; 4501 [slist addObject:area]; 4502 } 4503 } 4504} 4505- (void)join:sender 4506{ id change; 4507 NSRect drawRect; 4508 4509 change = [[JoinGraphicsChange alloc] initGraphicView:self]; 4510 [change startChange]; 4511 [self joinSelection:change messages:YES]; 4512 [change endChange]; 4513 4514 drawRect = [self boundsOfArray:slayList]; 4515 [self cache:drawRect]; 4516 [document setDirty:YES]; 4517 [[(App*)NSApp inspectorPanel] loadList:slayList]; 4518} 4519 4520/* created: 1995-09-19 4521 * modified: 2012-02-29 (noteList:list added) 4522 * purpose: split selected objects 4523 */ 4524- (void)split:sender 4525{ int i, l; 4526 NSRect rect, drawRect; 4527 BOOL start = YES; 4528 id change; 4529 4530 change = [[SplitGraphicsChange alloc] initGraphicView:self]; 4531 [change startChange]; 4532 for (l=[layerList count]-1; l>=0; l--) 4533 { LayerObject *layer = [layerList objectAtIndex:l]; 4534 NSMutableArray *slist = [slayList objectAtIndex:l]; 4535 4536 if (![layer editable] || ![slist count]) 4537 continue; 4538 if (start) 4539 { drawRect = [[slist objectAtIndex:[slist count]-1] extendedBoundsWithScale:[self scaleFactor]]; 4540 start = NO; 4541 } 4542 for (i=[slist count]-1; i>=0; i--) 4543 { id obj = [slist objectAtIndex:i]; 4544 4545 rect = [obj extendedBoundsWithScale:[self scaleFactor]]; 4546 drawRect = NSUnionRect(rect, drawRect); 4547 4548 if ( [obj isSelected] && [obj respondsToSelector:@selector(splitTo:)] && 4549 (![obj isKindOfClass:[VImage class]] || [(VImage*)obj clipPath]) ) // image: test for clipPath 4550 { int i, location = [[layer list] indexOfObject:obj]; 4551 NSMutableArray *list = [NSMutableArray array]; 4552 4553 [slist removeObject:obj]; 4554 [obj retain]; 4555 [layer removeObject:obj]; 4556 [obj splitTo:list]; 4557 for ( i = [list count] - 1; i >= 0; i-- ) 4558 [layer insertObject:[list objectAtIndex:i] atIndex:location]; 4559 [obj release]; 4560 [change noteList:list]; 4561 } 4562 } 4563 } 4564 [self getSelection]; 4565 [change endChange]; 4566 4567 [self cache:drawRect]; 4568 [document setDirty:YES]; 4569} 4570 4571/* created: 1996-09-27 4572 * modified: 2006-06-07 (remove VText from selection) 4573 * purpose: remove hidden areas from selected elements 4574 */ 4575- (void)punch:sender 4576{ int i, iCnt, l; 4577 NSRect drawRect; 4578 NSMutableArray *tmpList = [NSMutableArray array]; 4579 id change; 4580 4581 /* deselect unfit objects (VText) */ 4582 for ( l=[layerList count]-1; l>=0; l-- ) 4583 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4584 4585 for ( i=[slist count]-1; i>=0; i-- ) 4586 { id g = [slist objectAtIndex:i]; 4587 4588 if ([g isKindOfClass:[VText class]]) 4589 { 4590 [[slist objectAtIndex:i] setSelected:NO]; 4591 [slist removeObjectAtIndex:i]; 4592 } 4593 } 4594 } 4595 4596 change = [[PunchGraphicsChange alloc] initGraphicView:self]; 4597 [change startChange]; 4598 for ( l=[layerList count]-1; l>=0; l-- ) 4599 { LayerObject *layerObject = [layerList objectAtIndex:l]; 4600 NSMutableArray *slist = [slayList objectAtIndex:l]; 4601 4602 if ( ![layerObject editable] || ![slist count] ) 4603 continue; 4604 4605 /* 1st object in list may be used for the consecutive paste */ 4606 originalPaste = nil; 4607 4608 /* build list of selected objects in the order of list */ 4609 for (i=0, iCnt = [[layerObject list] count]; i<iCnt; i++) 4610 { id obj = [[layerObject list] objectAtIndex:i]; 4611 if ([obj isSelected]) 4612 [tmpList addObject:obj]; 4613 } 4614 /* remove objects from layer list */ 4615 for ( i=[slist count]-1; i>=0; i-- ) 4616 { [layerObject removeObject:[slist objectAtIndex:i]]; 4617 [slist removeObjectAtIndex:i]; 4618 } 4619 /* unite elements in slist */ 4620 [[[HiddenArea new] autorelease] removeHiddenAreas:tmpList]; 4621 //[change noteNewObjects:tmpList]; 4622 /* add objects from tmpList to list */ 4623 for ( i=0, iCnt = [tmpList count]; i<iCnt; i++ ) 4624 { id g = [tmpList objectAtIndex:i]; 4625 4626 [g setSelected:YES]; 4627 [layerObject addObject:g]; 4628 [slist addObject:g]; 4629 } 4630 /* remove objects to allow reuse for next layer */ 4631 [tmpList removeAllObjects]; 4632 } 4633 [change endChange]; 4634 4635 [self getSelection]; 4636 drawRect = [self boundsOfArray:slayList]; 4637 [self cache:drawRect]; 4638 [document setDirty:YES]; 4639} 4640 4641- (void)mirror:sender 4642{ int i, l; 4643 NSRect rect, drawRect; 4644 NSPoint p; 4645 id change; 4646 4647 /* // Debugging of intersections 4648 { id list = [[layerList objectAtIndex:0] list]; 4649 NSPoint *pa; 4650 4651 i = [[list objectAtIndex:0] getIntersections:&pa with:[list objectAtIndex:1]]; 4652 NSLog(@"i:%d", i); 4653 return; 4654 }*/ 4655 4656 rect = [self coordBoundsOfArray:slayList]; 4657 p.x = rect.origin.x + rect.size.width / 2.0; 4658 p.y = rect.origin.y + rect.size.height / 2.0; 4659 4660 drawRect = [self boundsOfArray:slayList]; 4661 change = [[MirrorGraphicsChange alloc] initGraphicView:self center:p]; 4662 [change startChange]; 4663 for (l=[slayList count]-1; l>=0; l--) 4664 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4665 LayerObject *layer = [layerList objectAtIndex:l]; 4666 4667 if (![layer editable]) 4668 continue; 4669 //drawRect = [[slayList objectAt:0] extendedBoundsWithScale:[self scaleFactor]]; 4670 for (i=[slist count]-1; i>=0; i--) 4671 { VGraphic *g = [slist objectAtIndex:i]; 4672 4673 //rect = [g extendedBoundsWithScale:[self scaleFactor]]; 4674 //NXUnionRect(&rect, &drawRect); 4675 [g mirrorAround:p]; 4676 [layer updateObject:g]; 4677 //rect = [g extendedBoundsWithScale:[self scaleFactor]]; 4678 //NXUnionRect(&rect, &drawRect); 4679 } 4680 } 4681 [change endChange]; 4682 4683 rect = [self boundsOfArray:slayList]; 4684 drawRect = NSUnionRect(rect , drawRect); 4685 [self cache:drawRect]; 4686 [document setDirty:YES]; 4687 [[(App*)NSApp inspectorPanel] loadList:slayList]; 4688} 4689 4690- (void)rotateG:sender 4691{ 4692 [self rotate:90.0]; 4693} 4694 4695/*- (void)transform:sender 4696{ 4697 [(App*)NSApp showTransformPanel:self]; 4698}*/ 4699 4700/* vectorize images 4701 * modified: 2011-04-06 4702 */ 4703- (void)vectorizeWithTolerance:(float)maxError 4704 createCurves:(BOOL)createCurves 4705 fill:(BOOL)fillResult 4706 replaceSource:(BOOL)removeSource 4707{ int i, l; 4708 NSRect rect, drawRect; 4709 id path; 4710 id change; 4711 4712 /* deselect everything that is not image */ 4713 for (l=[slayList count]-1; l>=0; l--) 4714 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4715 LayerObject *layer = [layerList objectAtIndex:l]; 4716 4717 if ([layer editable] && [slist count]) 4718 { 4719 for (i=[slist count]-1; i>=0; i--) 4720 { VGraphic *g = [slist objectAtIndex:i]; 4721 4722 if ( ! [g isKindOfClass:[VImage class]] ) 4723 { [slist removeObjectAtIndex:i]; 4724 i--; 4725 } 4726 } 4727 } 4728 } 4729 4730 drawRect = [self boundsOfArray:slayList]; 4731 4732 change = [[ContourGraphicsChange alloc] initGraphicView:self]; 4733 [change startChange]; 4734 [change setRemoveSource:removeSource]; 4735 for (l=[slayList count]-1; l>=0; l--) 4736 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4737 LayerObject *layer = [layerList objectAtIndex:l]; 4738 4739 if ([layer editable] && [slist count]) 4740 { 4741 for (i=[slist count]-1; i>=0; i--) 4742 { VGraphic *g = [slist objectAtIndex:i]; 4743 4744 if ( [g isKindOfClass:[VImage class]] ) 4745 { path = [g contour:0.0]; 4746 if ( createCurves ) 4747 path = [[VCurveFit sharedInstance] fitGraphic:path maxError:maxError]; 4748 if ( fillResult ) 4749 [path setFilled:YES]; 4750 [slist replaceObjectAtIndex:i withObject:path]; 4751 [path setSelected:YES]; 4752 [layer insertObject:path atIndex:[[layer list] indexOfObject:g]+1]; 4753 if ( removeSource ) 4754 [layer removeObject:g]; 4755 else 4756 [g setSelected:NO]; 4757 } 4758 } 4759 } 4760 } 4761 [change endChange]; 4762 4763 rect = [self boundsOfArray:slayList]; 4764 drawRect = NSUnionRect(rect, drawRect); 4765 [self cache:drawRect]; // update cache 4766 [document setDirty:YES]; 4767} 4768 4769- (void)scaleG:(float)x :(float)y 4770{ int i, l; 4771 NSRect rect, drawRect; 4772 NSPoint scaleCenter; 4773 id change; 4774 4775 if ( ![slayList count] ) 4776 return; 4777 4778 rect = [self boundsOfArray:slayList withKnobs:NO]; 4779 scaleCenter.x = rect.origin.x + rect.size.width / 2.0; 4780 scaleCenter.y = rect.origin.y + rect.size.height / 2.0; 4781 4782 drawRect = [self boundsOfArray:slayList]; 4783 change = [[ScaleGraphicsChange alloc] initGraphicView:self xScale:x yScale:y center:scaleCenter]; 4784 [change startChange]; 4785 for (l=[slayList count]-1; l>=0; l--) 4786 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4787 4788 if (![[layerList objectAtIndex:l] editable]) 4789 continue; 4790 for (i=[slist count]-1; i>=0; i--) 4791 { VGraphic *g = [slist objectAtIndex:i]; 4792 4793 [g scale:x :y withCenter:scaleCenter]; 4794 [[layerList objectAtIndex:l] updateObject:g]; 4795 } 4796 } 4797 [change endChange]; 4798 4799 rect = [self boundsOfArray:slayList]; 4800 drawRect = NSUnionRect(rect, drawRect); 4801 [self cache:drawRect]; 4802 [document setDirty:YES]; 4803} 4804- (void)scaleGTo:(float)x :(float)y 4805{ int i, l; 4806 NSRect rect, drawRect; 4807 NSPoint scaleCenter; 4808 id change; 4809 4810 if ( ![slayList count] ) 4811 return; 4812 4813 //rect = [self boundsOfArray:slayList withKnobs:NO]; 4814 //scaleCenter.x = rect.origin.x + rect.size.width / 2.0; 4815 //scaleCenter.y = rect.origin.y + rect.size.height / 2.0; 4816 4817 drawRect = [self boundsOfArray:slayList]; 4818 //change = [[ScaleGraphicsChange alloc] initGraphicView:self xScale:x yScale:y center:scaleCenter]; 4819 change = [[DimensionsGraphicsChange alloc] initGraphicView:self]; 4820 [change startChange]; 4821 for (l=[slayList count]-1; l>=0; l--) 4822 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4823 4824 if (![[layerList objectAtIndex:l] editable]) 4825 continue; 4826 for (i=[slist count]-1; i>=0; i--) 4827 { VGraphic *g = [slist objectAtIndex:i]; 4828 NSRect r = [g coordBounds]; 4829 float sx, sy; 4830 4831 sx = x / r.size.width; 4832 sy = (y != 0.0) ? y / r.size.height : sx; 4833 scaleCenter = r.origin; 4834 //scaleCenter.x = r.origin.x + r.size.width / 2.0; 4835 //scaleCenter.y = r.origin.y + r.size.height / 2.0; 4836 [g scale:sx :sy withCenter:scaleCenter]; 4837 [[layerList objectAtIndex:l] updateObject:g]; 4838 } 4839 } 4840 [change endChange]; 4841 4842 rect = [self boundsOfArray:slayList]; 4843 drawRect = NSUnionRect(rect, drawRect); 4844 [self cache:drawRect]; 4845 [document setDirty:YES]; 4846} 4847 4848- (void)reverse:sender 4849{ int i, l; 4850 NSRect drawRect; 4851 4852 drawRect = [self boundsOfArray:slayList]; 4853 4854 for (l=[slayList count]-1; l>=0; l--) 4855 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4856 4857 if (![[layerList objectAtIndex:l] editable]) 4858 continue; 4859 for (i=[slist count]-1; i>=0; i--) 4860 { VGraphic *g = [slist objectAtIndex:i]; 4861 4862 [g changeDirection]; 4863 [[layerList objectAtIndex:l] updateObject:g]; 4864 } 4865 } 4866 4867 [self cache:drawRect]; 4868 [document setDirty:YES]; 4869} 4870 4871- (void)pathSetStartPoint:sender 4872{ int i, l; 4873 NSRect drawRect; 4874 4875 drawRect = [self boundsOfArray:slayList]; 4876 4877 for (l=[slayList count]-1; l>=0; l--) 4878 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4879 4880 if (![[layerList objectAtIndex:l] editable] || [slist count] > 1 ) 4881 continue; 4882 for (i=[slist count]-1; i>=0; i--) 4883 { VPath *g = [slist objectAtIndex:i]; 4884 4885 if ( [g respondsToSelector:@selector(pointWithNumBecomeStartPoint:)] ) 4886 { [g pointWithNumBecomeStartPoint:[g selectedKnobIndex]]; 4887 [[layerList objectAtIndex:l] updateObject:g]; 4888 } 4889 } 4890 } 4891 4892 [self cache:drawRect]; 4893 [document setDirty:YES]; 4894} 4895 4896/* Build Outline of stroked or outline objects, vectorize images 4897 * modified: 2011-04-06 (-setRemoveSource: added, [path setSelected:YES] added) 4898 */ 4899- (void)buildContour:sender 4900{ int i, l; 4901 NSRect rect, drawRect; 4902 id path; 4903 float width; 4904 id change; 4905 BOOL removeSource = [(App*)NSApp contourRemoveSource]; 4906 4907 /* show panel to get width */ 4908 if (![(App*)NSApp showContourPanel:self]) 4909 return; 4910 width = [(App*)NSApp contour]*2.0; // die unit wird in apContour -contour beruecksichtig 4911 4912 drawRect = [self boundsOfArray:slayList]; 4913 4914 change = [[ContourGraphicsChange alloc] initGraphicView:self]; 4915 [change startChange]; 4916 [change setRemoveSource:removeSource]; 4917 for (l=[slayList count]-1; l>=0; l--) 4918 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4919 LayerObject *layer = [layerList objectAtIndex:l]; 4920 4921 if ([layer editable] && [slist count]) 4922 { 4923 /* hier lokales useRaster nehmen */ 4924 if ( [(App*)NSApp contourUseRaster] ) 4925 { PathContour *pathContour = [PathContour new]; 4926 4927 for (i=[slist count]-1; i>=0; i--) 4928 { VGraphic *g = [slist objectAtIndex:i]; 4929 4930 if ( [g isKindOfClass:[VPath class]] ) 4931 path = [pathContour contourPath:(VPath*)g width:width]; 4932 else if ( [g respondsToSelector:@selector(contour:)] ) 4933 { path = [g contour:width]; 4934 4935 //if ( [g isKindOfClass:[VImage class]] ) // turn lines into curves 4936 // path = [[VCurveFit sharedInstance] fitGraphic:path maxError:2.0]; 4937 } 4938 else 4939 continue; // skip graphic 4940 4941 [slist replaceObjectAtIndex:i withObject:path]; 4942 [path setSelected:YES]; 4943 [layer insertObject:path atIndex:[[layer list] indexOfObject:g]+1]; 4944 /* source removen oder nicht */ 4945 if ( removeSource ) 4946 [layer removeObject:g]; 4947 else 4948 [g setSelected:NO]; 4949 } 4950 [PathContour release]; 4951 } 4952 else 4953 { 4954 for (i=[slist count]-1; i>=0; i--) 4955 { VGraphic *g = [slist objectAtIndex:i]; 4956 4957 if ( [g respondsToSelector:@selector(contour:)] ) 4958 { path = [g contour:width]; 4959 //if ( [g isKindOfClass:[VImage class]] ) // turn lines into curves 4960 // path = [[VCurveFit sharedInstance] fitGraphic:path maxError:2.0]; 4961 [slist replaceObjectAtIndex:i withObject:path]; 4962 [path setSelected:YES]; 4963 [layer insertObject:path atIndex:[[layer list] indexOfObject:g]+1]; 4964 /* source removen oder nicht */ 4965 if ( removeSource ) 4966 [layer removeObject:g]; 4967 else 4968 [g setSelected:NO]; 4969 } 4970 } 4971 } 4972 } 4973 } 4974 //[change noteNewObjects:slist]; 4975 [change endChange]; 4976 4977 rect = [self boundsOfArray:slayList]; 4978 drawRect = NSUnionRect(rect, drawRect); 4979 [self cache:drawRect]; /* update cache */ 4980 [document setDirty:YES]; 4981} 4982 4983- (void)flatten:sender 4984{ int i, l, ix; 4985 NSRect drawRect = [self boundsOfArray:slayList]; 4986 id change; 4987 4988 change = [[GroupGraphicsChange alloc] initGraphicView:self]; 4989 [change startChange]; 4990 4991 for (l=[slayList count]-1; l>=0; l--) 4992 { NSMutableArray *slist = [slayList objectAtIndex:l]; 4993 LayerObject *layer = [layerList objectAtIndex:l]; 4994 4995 if (![[layerList objectAtIndex:l] editable]) 4996 continue; 4997 for (i=[slist count]-1; i>=0; i--) 4998 { id fg, g = [slist objectAtIndex:i]; 4999 5000 fg = [g pathRepresentation]; 5001 [change noteGroup:fg layer:layer]; 5002 [slist replaceObjectAtIndex:i withObject:fg]; 5003 ix = [[layer list] indexOfObject:g]; 5004 [layer removeObject:g]; 5005 [layer insertObject:fg atIndex:ix]; 5006 //[list replaceObjectAtIndex:[list indexOfObject:g] withObject:fg]; 5007 } 5008 } 5009 5010 [change endChange]; 5011 5012 // [self getBBox:&rect of:slayList]; 5013 // NXUnionRect(&rect, &drawRect); 5014 [self cache:drawRect]; 5015 [document setDirty:YES]; 5016} 5017 5018/* bind text to path 5019 * create TextPath, remove text and path object from lists 5020 */ 5021- (void)bindTextToPath:sender 5022{ int l, i; 5023 NSRect drawRect = [self boundsOfArray:slayList]; 5024 id change; 5025 5026 /* deselect everything which will not be part of the group(s) */ 5027 for (l=[layerList count]-1; l>=0; l--) 5028 { LayerObject *layer = [layerList objectAtIndex:l]; 5029 NSMutableArray *slist = [slayList objectAtIndex:l]; 5030 BOOL textOk = NO, pathOk = NO; 5031 5032 if (![layer editable] || [slist count] < 2) 5033 [slist removeAllObjects]; 5034 for (i=0; i<(int)[slist count]; i++) 5035 { VGraphic *g = [slist objectAtIndex:i]; 5036 5037 /* remove all texts but the first text object */ 5038 if ([g isMemberOfClass:[VText class]]) 5039 { 5040 if (textOk) { 5041 [slist removeObjectAtIndex:i]; i--; } 5042 textOk = YES; 5043 } 5044 /* remove all path objects but the first path object */ 5045 else if ([g isPathObject]) 5046 { 5047 if (pathOk) { 5048 [slist removeObjectAtIndex:i]; i--; } 5049 pathOk = YES; 5050 } 5051 /* remove everything else */ 5052 else { 5053 [slist removeObjectAtIndex:i]; i--; } 5054 } 5055 } 5056 5057 /* create textPath */ 5058 change = [[GroupGraphicsChange alloc] initGraphicView:self]; 5059 [change startChange]; 5060 for ( l=[slayList count]-1; l>=0; l-- ) 5061 { NSMutableArray *slist = [slayList objectAtIndex:l]; 5062 LayerObject *layer = [layerList objectAtIndex:l]; 5063 VText *text; 5064 VGraphic *path; 5065 VTextPath *textPath; 5066 5067 if ( ![layer editable] || [slist count]<2 ) 5068 continue; 5069 if ( [[slist objectAtIndex:0] isMemberOfClass:[VText class]] ) 5070 { text = [slist objectAtIndex:0]; 5071 path = [slist objectAtIndex:1]; 5072 } 5073 else 5074 { text = [slist objectAtIndex:1]; 5075 path = [slist objectAtIndex:0]; 5076 } 5077 5078 textPath = [VTextPath textPathWithText:text path:path]; 5079 [change noteGroup:textPath layer:layer]; 5080 [slist removeObject:text]; [slist removeObject:path]; 5081 [layer removeObject:text]; [layer removeObject:path]; 5082 [slist addObject:textPath]; [layer addObject:textPath]; 5083 } 5084 [change endChange]; 5085 5086 [self cache:drawRect]; 5087 [document setDirty:YES]; 5088} 5089 5090/* created: 2010-06-13 5091 */ 5092-(void)addLink:sender 5093{ NSTextView *fe = (NSTextView*)[[self window] fieldEditor:NO forObject:nil]; 5094 NSRange range = [fe selectedRange]; 5095 NSObject *linkObject; 5096 NSMutableDictionary *linkAttributes; 5097 5098 if (range.length == 0) 5099 return; 5100 linkObject = [NSURL URLWithString:[[[fe textStorage] string] substringWithRange:range]]; 5101 if ( !linkObject ) 5102 linkObject = [[[fe textStorage] string] substringWithRange:range]; 5103 linkAttributes = [NSMutableDictionary dictionaryWithObject:linkObject 5104 forKey:NSLinkAttributeName]; 5105 [linkAttributes setObject:[NSColor blueColor] forKey:NSForegroundColorAttributeName]; 5106 [linkAttributes setObject:[NSNumber numberWithBool: YES] forKey:NSUnderlineStyleAttributeName]; 5107 [[fe textStorage] addAttributes:linkAttributes range:range]; 5108 //[[fe window] resetCursorRects]; 5109} 5110 5111/* 5112 * Writes out the layerList and the flags. 5113 * No need to write out the slayList since it can be regenerated from the layerList. 5114 * We also ensure that no Text object that might be a subview of the 5115 * editView gets written out by removing all subviews of the editView. 5116 */ 5117- (void)encodeWithCoder:(NSCoder *)aCoder 5118{ int version = [DocView version]; 5119 BOOL tile = (tileOriginList) ? YES : NO; 5120 5121 [aCoder encodeValuesOfObjCTypes:"i", &version]; 5122 [aCoder encodeValuesOfObjCTypes:"@", &layerList]; 5123 [aCoder encodeValuesOfObjCTypes:"@", &origin]; 5124 [aCoder encodeValuesOfObjCTypes:"c{NSPoint=ff}c{NSPoint=ff}", &tile, &tileDistance, &tileLimitSize, &tileLimits]; 5125 [aCoder encodeValuesOfObjCTypes:"cif", &gridIsEnabled, &gridUnit, &gridSpacing]; 5126} 5127/* 5128 * Reads in the list and the flags, and regenerates the slayList from the list. 5129 */ 5130- (id)initWithCoder:(NSCoder *)aDecoder 5131{ int version; 5132 BOOL tile = NO; 5133 5134 [layerList release]; 5135 5136 [aDecoder decodeValuesOfObjCTypes:"i", &version]; 5137 [aDecoder decodeValuesOfObjCTypes:"@", &layerList]; 5138 [aDecoder decodeValuesOfObjCTypes:"@", &origin]; 5139 if ( version >= 6 ) 5140 [aDecoder decodeValuesOfObjCTypes:"c{NSPoint=ff}c{NSPoint=ff}", &tile, &tileDistance, &tileLimitSize, &tileLimits]; 5141 else if ( version >=5 ) 5142 [aDecoder decodeValuesOfObjCTypes:"c{NSPoint=ff}", &tile, &tileDistance]; 5143 else 5144 [aDecoder decodeValuesOfObjCTypes:"c{ff}", &tile, &tileDistance]; 5145 if ( version >= 3 ) 5146 [aDecoder decodeValuesOfObjCTypes:"cif", &gridIsEnabled, &gridUnit, &gridSpacing]; 5147 5148 [self setParameter]; 5149 [self resetGrid]; 5150 5151 { int l, i; 5152 5153 slayList = [[NSMutableArray allocWithZone:[self zone]] init]; // the selected list 5154 for (l=[layerList count]-1; l>=0; l--) 5155 { NSMutableArray *slist = [NSMutableArray array]; 5156 LayerObject *layerObject = [layerList objectAtIndex:l]; 5157 NSMutableArray *list = [layerObject list]; 5158 5159 [layerObject createPerformanceMapWithFrame:[self bounds]]; // create performance map 5160 [slayList addObject:slist]; // build slist 5161 [[NSNotificationCenter defaultCenter] postNotificationName:DocLayerListHasChanged 5162 object:self]; 5163 5164 /* copy clipping rectangle to clipping layer */ 5165 if ( version < 4 ) // < 31.01.00 versions without clipping layer 5166 for ( i=[list count]-1; i>=0; i-- ) 5167 if ( [[list objectAtIndex:i] isMemberOfClass:[ClipRectangle class]] ) 5168 { NSPoint o, s; 5169 ClipRectangle *cr = [list objectAtIndex:i]; 5170 VRectangle *r = [VRectangle rectangle]; 5171 5172 [self addLayerWithName:LAYERCLIPPING_STRING type:LAYER_CLIPPING 5173 tag:0 list:nil editable:NO]; 5174 [[NSNotificationCenter defaultCenter] postNotificationName:DocLayerListHasChanged 5175 object:self]; 5176 [cr getVertices:&o :&s]; 5177 [r setVertices:o :s]; 5178 [[layerList objectAtIndex:[layerList count]-1] addObject:r]; 5179 [layerObject removeObject:cr]; 5180 break; 5181 } 5182 } 5183 } 5184 [self getSelection]; 5185 if (tile) 5186 { 5187 if (!tileLimits.x || !tileLimits.y) 5188 tile = NO; 5189 [self setTileWithLimits:tileLimits limitSize:tileLimitSize distance:tileDistance 5190 moveToOrigin:NO]; 5191 } 5192 5193 return self; 5194} 5195/* used to import document (see VGroup and Document) 5196 */ 5197+ (id)readList:(id)stream inDirectory:(NSString*)directory 5198{ NSMutableArray *list; 5199 int version; 5200 5201 if ( [stream isKindOfClass:[NSUnarchiver class]] ) 5202 { 5203 [stream decodeValuesOfObjCTypes:"i", &version]; 5204 [stream decodeValuesOfObjCTypes:"@", &list]; 5205 } 5206 else 5207 list = arrayFromPropertyList([stream objectForKey:@"layerList"], directory, [self zone]); 5208 // other stuff ignored 5209 5210 return list; 5211} 5212 5213/* archiving with property list 5214 */ 5215- (void)allowGraphicsToWriteFilesIntoDirectory:(NSString *)directory 5216{ int l, i; 5217 5218 for ( l=[layerList count]-1; l>=0; l-- ) 5219 { LayerObject *layerObject = [layerList objectAtIndex:l]; 5220 NSMutableArray *list = [layerObject list]; 5221 5222 for (i = [list count]-1; i >= 0; i--) 5223 [[list objectAtIndex:i] writeFilesToDirectory:directory]; 5224 } 5225} 5226- (id)propertyList 5227{ NSMutableDictionary *plist = [NSMutableDictionary dictionaryWithCapacity:9]; 5228 5229 [plist setObject:propertyListFromArray(layerList) forKey:@"layerList"]; 5230 5231 if (backgroundColor) 5232 [plist setObject:propertyListFromNSColor(backgroundColor) forKey:@"bgColor"]; 5233 [plist setObject:[origin propertyList] forKey:@"origin"]; 5234 5235 if (tileOriginList) [plist setObject:@"YES" forKey:@"tile"]; 5236 [plist setObject:propertyListFromNSPoint(tileDistance) forKey:@"tileDistance"]; 5237 if (tileLimitSize) [plist setObject:@"YES" forKey:@"tileLimitSize"]; 5238 [plist setObject:propertyListFromNSPoint(tileLimits) forKey:@"tileLimits"]; 5239 5240 if (gridIsEnabled) [plist setObject:@"YES" forKey:@"gridIsEnabled"]; 5241 [plist setObject:propertyListFromInt(gridUnit) forKey:@"gridUnit"]; 5242 [plist setObject:propertyListFromFloat(gridSpacing) forKey:@"gridSpacing"]; 5243 5244 return plist; 5245} 5246- (id)initFromPropertyList:(id)plist inDirectory:(NSString *)directory 5247{ id plistObject, obj; 5248 BOOL tile; 5249 NSString *className; 5250 5251 [layerList release]; 5252 layerList = arrayFromPropertyList([plist objectForKey:@"layerList"], directory, [self zone]); 5253 5254 backgroundColor = colorFromPropertyList([plist objectForKey:@"bgColor"], [self zone]); 5255 5256 plistObject = [plist objectForKey:@"origin"]; 5257 className = [plistObject objectForKey:@"Class"]; 5258 obj = [NSClassFromString(className) allocWithZone:[self zone]]; 5259 if (!obj) // load old projects (< 3.50 beta 13) 5260 obj = [NSClassFromString(newClassName(className)) allocWithZone:[self zone]]; 5261 origin = [obj initFromPropertyList:plistObject inDirectory:directory]; 5262 5263 tile = ([plist objectForKey:@"tile"] ? YES : NO); 5264 tileDistance = pointFromPropertyList([plist objectForKey:@"tileDistance"]); 5265 tileLimitSize = ([plist objectForKey:@"tileLimitSize"] ? YES : NO); 5266 tileLimits = pointFromPropertyList([plist objectForKey:@"tileLimits"]); 5267 5268 gridIsEnabled = ([plist objectForKey:@"gridIsEnabled"] ? YES : NO); 5269 gridUnit = [plist floatForKey:@"gridUnit"]; 5270 gridSpacing = [plist floatForKey:@"gridSpacing"]; 5271 5272 [self setParameter]; 5273 [self resetGrid]; 5274 5275 /* create selected list 5276 * set tool for layer 5277 */ 5278 { int l; 5279 5280 slayList = [[NSMutableArray allocWithZone:[self zone]] init]; // the selected list 5281 for ( l=[layerList count]-1; l>=0; l-- ) 5282 { NSMutableArray *slist = [NSMutableArray array]; 5283 LayerObject *layerObject = [layerList objectAtIndex:l]; 5284 5285 [layerObject createPerformanceMapWithFrame:[self bounds]]; // create performance map 5286 [slayList addObject:slist]; // build slist 5287 } 5288 } 5289 [self getSelection]; 5290 if (tile) 5291 [self setTileWithLimits:tileLimits limitSize:tileLimitSize distance:tileDistance 5292 moveToOrigin:NO]; 5293 5294 return self; 5295} 5296 5297 5298 5299/* notification that we have to set our dirty flag 5300 */ 5301- (void)allLayersHaveChanged:(NSNotification*)sender 5302{ 5303 [self setAllLayerDirty:YES]; 5304} 5305/* notification that we have to update the caching 5306 */ 5307- (void)cachingHasChanged:(NSNotification*)sender 5308{ 5309 [self setCaching:Prefs_Caching redraw:YES]; 5310} 5311 5312 5313 5314- (void)dealloc 5315{ 5316 [[NSNotificationCenter defaultCenter] removeObserver:self]; 5317 5318 [backgroundColor release]; 5319 [origin release]; origin = nil; 5320 5321 [layerList release]; layerList = nil; 5322 [slayList release]; 5323 5324 [tileOriginList release]; 5325 5326 if (![editView superview]) 5327 [editView release]; 5328 [cache release]; 5329 5330 if (numGridRectsX) 5331 { NSZoneFree([self zone], gridListX); 5332 NSZoneFree([self zone], gridListY); 5333 } 5334 5335 [super dealloc]; 5336} 5337 5338@end 5339