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