1/** <title>NSTextAttachment</title>
2
3   <abstract>Classes to represent text attachments.</abstract>
4
5   NSTextAttachment is used to represent text attachments. When inline,
6   text attachments appear as the value of the NSAttachmentAttributeName
7   attached to the special character NSAttachmentCharacter.
8
9   NSTextAttachment uses an object obeying the NSTextAttachmentCell
10   protocol to get input from the user and to display an image.
11
12   NSTextAttachmentCell is a simple subclass of NSCell which provides
13   the NSTextAttachment protocol.
14
15   Copyright (C) 2000 Free Software Foundation, Inc.
16
17   Author: Fred Kiefer <FredKiefer@gmx.de>
18   Date: June 2000
19
20   This file is part of the GNUstep GUI Library.
21
22   This library is free software; you can redistribute it and/or
23   modify it under the terms of the GNU Lesser General Public
24   License as published by the Free Software Foundation; either
25   version 2 of the License, or (at your option) any later version.
26
27   This library is distributed in the hope that it will be useful,
28   but WITHOUT ANY WARRANTY; without even the implied warranty of
29   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
30   Lesser General Public License for more details.
31
32   You should have received a copy of the GNU Lesser General Public
33   License along with this library; see the file COPYING.LIB.
34   If not, see <http://www.gnu.org/licenses/> or write to the
35   Free Software Foundation, 51 Franklin Street, Fifth Floor,
36   Boston, MA 02110-1301, USA.
37*/
38
39#import "AppKit/NSCell.h"
40#import "AppKit/NSFileWrapper.h"
41#import "AppKit/NSFileWrapperExtensions.h"
42#import "AppKit/NSImage.h"
43#import "AppKit/NSEvent.h"
44#import "AppKit/NSTextContainer.h"
45#import "AppKit/NSTextAttachment.h"
46#import "AppKit/NSTextView.h"
47
48
49@implementation NSTextAttachmentCell
50
51- (void)drawWithFrame: (NSRect)cellFrame
52	       inView: (NSView *)controlView
53       characterIndex: (NSUInteger)charIndex
54{
55  //  cellFrame.origin.y -= cellFrame.size.height;
56  [self drawWithFrame: cellFrame
57	inView: controlView];
58}
59
60- (void)drawWithFrame: (NSRect)cellFrame
61	       inView: (NSView *)controlView
62       characterIndex: (NSUInteger)charIndex
63	layoutManager: (NSLayoutManager *)layoutManager
64{
65  [self drawWithFrame: cellFrame
66	inView: controlView
67	characterIndex: charIndex];
68}
69
70- (NSPoint)cellBaselineOffset
71{
72  return NSZeroPoint;
73}
74
75- (NSRect)cellFrameForTextContainer: (NSTextContainer *)textContainer
76	       proposedLineFragment: (NSRect)lineFrag
77		      glyphPosition: (NSPoint)position
78		     characterIndex: (NSUInteger)charIndex
79{
80  NSRect aRect;
81
82  aRect.origin = [self cellBaselineOffset];
83  aRect.size = [self cellSize];
84  return aRect;
85}
86
87- (BOOL)wantsToTrackMouse
88{
89  return YES;
90}
91
92- (BOOL)wantsToTrackMouseForEvent: (NSEvent *)theEvent
93			   inRect: (NSRect)cellFrame
94			   ofView: (NSView *)controlView
95		 atCharacterIndex: (NSUInteger)charIndex
96{
97  return [self wantsToTrackMouse];
98}
99
100- (BOOL)trackMouse: (NSEvent *)theEvent
101	    inRect: (NSRect)cellFrame
102	    ofView: (NSView *)controlView
103      untilMouseUp: (BOOL)flag
104{
105  if ([controlView respondsToSelector: @selector(delegate)])
106    {
107      NSTextView *textView = (NSTextView*)controlView;
108      id delegate = [textView delegate];
109      NSEventType type = [theEvent type];
110
111      if (type == NSLeftMouseUp)
112        {
113	  if ([theEvent clickCount] == 2)
114	    {
115	      if (delegate != nil && [delegate respondsToSelector:
116		@selector(textView:doubleClickedOnCell:inRect:)])
117	        {
118		  [delegate textView: textView
119			    doubleClickedOnCell: self
120			    inRect: cellFrame];
121		  return YES;
122		}
123	    }
124	  else
125	    {
126	      if (delegate != nil && [delegate respondsToSelector:
127		@selector(textView:clickedOnCell:inRect:)])
128	        {
129		  [delegate textView: textView
130			    clickedOnCell: self
131			    inRect: cellFrame];
132		  return YES;
133		}
134	    }
135	}
136      else if (type == NSLeftMouseDragged)
137        {
138	  if (delegate != nil && [delegate respondsToSelector:
139	    @selector(textView:draggedCell:inRect:event:)])
140	    {
141	      [delegate textView: textView
142			draggedCell: self
143			inRect: cellFrame
144			event: theEvent];
145	      return YES;
146	    }
147	}
148    }
149
150  return [super trackMouse: theEvent
151		inRect: cellFrame
152		ofView: controlView
153		untilMouseUp: flag];
154}
155
156- (BOOL)trackMouse: (NSEvent *)theEvent
157	    inRect: (NSRect)cellFrame
158	    ofView: (NSView *)controlView
159  atCharacterIndex: (NSUInteger)charIndex
160      untilMouseUp: (BOOL)flag
161{
162  if ([controlView respondsToSelector: @selector(delegate)])
163    {
164      NSTextView *textView = (NSTextView*)controlView;
165      id delegate = [textView delegate];
166      NSEventType type = [theEvent type];
167
168      if (type == NSLeftMouseDown)
169        {
170	  if ([theEvent clickCount] == 2)
171	    {
172              if (delegate != nil)
173                {
174                  if ([delegate respondsToSelector:
175                    @selector(textView:doubleClickedOnCell:inRect:atIndex:)])
176                    {
177                      [delegate textView: textView
178                                doubleClickedOnCell: self
179                                inRect: cellFrame
180                                atIndex: charIndex];
181                      return YES;
182                    }
183                  else if ([delegate respondsToSelector:
184                    @selector(textView:doubleClickedOnCell:inRect:)])
185                    {
186                      [delegate textView: textView
187                                doubleClickedOnCell: self
188                                inRect: cellFrame];
189                      return YES;
190                    }
191                }
192	    }
193	  else
194	    {
195              if (delegate != nil)
196                {
197                  if ([delegate respondsToSelector:
198                    @selector(textView:clickedOnCell:inRect:atIndex:)])
199                    {
200                      [delegate textView: textView
201                                clickedOnCell: self
202                                inRect: cellFrame
203                                atIndex: charIndex];
204                      return YES;
205                    }
206                  else if ([delegate respondsToSelector:
207                    @selector(textView:clickedOnCell:inRect:)])
208                    {
209                      [delegate textView: textView
210                                clickedOnCell: self
211                                inRect: cellFrame];
212                      return YES;
213                    }
214                }
215	    }
216	}
217      else if (type == NSLeftMouseDragged)
218        {
219	  if (delegate != nil && [delegate respondsToSelector:
220	    @selector(textView:draggedCell:inRect:event:atIndex:)])
221	    {
222	      [delegate textView: textView
223			draggedCell: self
224			inRect: cellFrame
225			event: theEvent
226			atIndex: charIndex];
227	      return YES;
228	    }
229	}
230    }
231
232  return [self trackMouse: theEvent
233	       inRect: cellFrame
234	       ofView: controlView
235	       untilMouseUp: flag];
236}
237
238- (void)setAttachment: (NSTextAttachment *)anObject
239{
240  // Do not retain the attachment
241  _attachment = anObject;
242}
243
244- (NSTextAttachment *)attachment
245{
246  return _attachment;
247}
248
249//FIXME: I had to add those methods to keep the compiler quite.
250// This are already defined on the super class and should be taken from there.
251
252- (NSSize)cellSize
253{
254  return [super cellSize];
255}
256
257- (void)highlight: (BOOL)flag
258	withFrame: (NSRect)cellFrame
259	   inView: (NSView *)controlView
260{
261  [super highlight: flag
262	 withFrame: cellFrame
263	 inView: controlView];
264}
265
266- (void)drawWithFrame: (NSRect)cellFrame inView: (NSView *)controlView
267{
268  [super drawWithFrame: cellFrame inView: controlView];
269}
270
271@end
272
273
274@implementation NSTextAttachment
275
276- (id) init
277{
278  return [self initWithFileWrapper: nil];
279}
280
281- (void) dealloc
282{
283  DESTROY(_fileWrapper);
284  DESTROY(_cell);
285  [super dealloc];
286}
287
288- (id) initWithFileWrapper: (NSFileWrapper *)fileWrapper
289{
290  self = [super init];
291  if (self != nil)
292    {
293      _cell = [[NSTextAttachmentCell alloc ] init];
294      [self setFileWrapper: fileWrapper];
295    }
296  return self;
297}
298
299- (void)setFileWrapper: (NSFileWrapper *)fileWrapper
300{
301  ASSIGN(_fileWrapper, fileWrapper);
302  // Reset the cell, so it shows the new attachment
303  if ([_cell respondsToSelector: @selector(setAttachment:)] == YES)
304    {
305      [_cell setAttachment: self];
306    }
307  if (_taflags.cell_explicitly_set == 0)
308    {
309      if (fileWrapper != nil)
310        {
311          NSImage *icon = nil;
312          NSString *fileName = [fileWrapper filename];
313
314          if (fileName != nil)
315            {
316              // Try to set the image to the file wrapper content
317              icon = [[NSImage alloc] initByReferencingFile: fileName];
318            }
319          if (icon == nil)
320            icon = RETAIN([fileWrapper icon]);
321
322          [(NSTextAttachmentCell*)_cell setImage: icon];
323          RELEASE(icon);
324        }
325    }
326}
327
328- (NSFileWrapper *)fileWrapper
329{
330  return _fileWrapper;
331}
332
333- (id <NSTextAttachmentCell>)attachmentCell
334{
335  return _cell;
336}
337
338- (void)setAttachmentCell: (id <NSTextAttachmentCell>)cell
339{
340  ASSIGN(_cell, cell);
341  _taflags.cell_explicitly_set = 1;
342  if ([_cell respondsToSelector: @selector(setAttachment:)] == YES)
343    {
344      [_cell setAttachment: self];
345    }
346}
347
348/*
349 * NSCoding protocol
350 */
351- (void) encodeWithCoder: (NSCoder*)aCoder
352{
353  if ([aCoder allowsKeyedCoding])
354    {
355      [aCoder encodeObject: [self fileWrapper] forKey: @"NSFileWrapper"];
356      if (_cell != nil)
357        {
358          [aCoder encodeObject: _cell forKey: @"NSCell"];
359        }
360    }
361  else
362    {
363      [aCoder encodeObject: _fileWrapper];
364      [aCoder encodeObject: _cell];
365    }
366}
367
368- (id) initWithCoder: (NSCoder*)aDecoder
369{
370  if ([aDecoder allowsKeyedCoding])
371    {
372      [self setFileWrapper: [aDecoder decodeObjectForKey: @"NSFileWrapper"]];
373      [self setAttachmentCell: [aDecoder decodeObjectForKey: @"NSCell"]];
374    }
375  else
376    {
377      [aDecoder decodeValueOfObjCType: @encode(id) at: &_fileWrapper];
378      [aDecoder decodeValueOfObjCType: @encode(id) at: &_cell];
379
380      // Reconnect the cell, so the cell does not have to store the attachment
381      if ([_cell respondsToSelector: @selector(setAttachment:)] == YES)
382	{
383	  [_cell setAttachment: self];
384	}
385    }
386  return self;
387}
388
389@end
390