1/** <title>NSDataLink</title>
2
3   Copyright (C) 1996, 2005 Free Software Foundation, Inc.
4
5   Author: Gregory John Casamento <greg_casamento@yahoo.com>
6   Date: 2005
7   Author: Scott Christley <scottc@net-community.com>
8   Date: 1996
9
10   This file is part of the GNUstep GUI Library.
11
12   This library is free software; you can redistribute it and/or
13   modify it under the terms of the GNU Lesser General Public
14   License as published by the Free Software Foundation; either
15   version 2 of the License, or (at your option) any later version.
16
17   This library is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
20   Lesser General Public License for more details.
21
22   You should have received a copy of the GNU Lesser General Public
23   License along with this library; see the file COPYING.LIB.
24   If not, see <http://www.gnu.org/licenses/> or write to the
25   Free Software Foundation, 51 Franklin Street, Fifth Floor,
26   Boston, MA 02110-1301, USA.
27*/
28
29#include "config.h"
30#import <Foundation/NSFileManager.h>
31#import <Foundation/NSArchiver.h>
32#import <Foundation/NSData.h>
33#import "AppKit/NSDataLink.h"
34#import "AppKit/NSDataLinkManager.h"
35#import "AppKit/NSPasteboard.h"
36#import "AppKit/NSSavePanel.h"
37#import "AppKit/NSSelection.h"
38
39@implementation NSDataLink
40
41//
42// Class methods
43//
44+ (void)initialize
45{
46  if (self == [NSDataLink class])
47    {
48      // Initial version
49      [self setVersion: 0];
50    }
51}
52
53//
54//
55// Instance methods
56//
57// Initializing a Link
58//
59- (id)initLinkedToFile:(NSString *)filename
60{
61  if ((self = [self init]) != nil)
62    {
63      NSData *data = [NSData dataWithBytes: [filename cString] length: [filename cStringLength]];
64      NSSelection *selection = [NSSelection selectionWithDescriptionData: data];
65      ASSIGN(sourceSelection, selection);
66    }
67  return self;
68}
69
70- (id)initLinkedToSourceSelection:(NSSelection *)selection
71			managedBy:(NSDataLinkManager *)linkManager
72		  supportingTypes:(NSArray *)newTypes
73{
74  if ((self = [self init]) != nil)
75    {
76      ASSIGN(sourceSelection,selection);
77      ASSIGN(sourceManager,linkManager);
78      ASSIGN(types,newTypes);
79    }
80  return self;
81}
82
83- (id)initWithContentsOfFile:(NSString *)filename
84{
85  NSData *data = [[NSData alloc] initWithContentsOfFile: filename];
86  id object = [NSUnarchiver unarchiveObjectWithData: data];
87
88  RELEASE(data);
89  RELEASE(self);
90  return RETAIN(object);
91}
92
93- (id)initWithPasteboard:(NSPasteboard *)pasteboard
94{
95  NSData *data = [pasteboard dataForType: NSDataLinkPboardType];
96  id object = [NSUnarchiver unarchiveObjectWithData: data];
97
98  RELEASE(self);
99  return RETAIN(object);
100}
101
102//
103// Exporting a Link
104//
105- (BOOL)saveLinkIn:(NSString *)directoryName
106{
107  NSSavePanel		*sp;
108  int			result;
109
110  sp = [NSSavePanel savePanel];
111  [sp setRequiredFileType: NSDataLinkFilenameExtension];
112  result = [sp runModalForDirectory: directoryName file: @""];
113  if (result == NSOKButton)
114    {
115      NSFileManager	*mgr = [NSFileManager defaultManager];
116      NSString		*path = [sp filename];
117
118      if ([mgr fileExistsAtPath: path] == YES)
119	{
120	  /* NSSavePanel has already asked if it's ok to replace */
121	  NSString	*bPath = [path stringByAppendingString: @"~"];
122
123	  [mgr removeFileAtPath: bPath handler: nil];
124	  [mgr movePath: path toPath: bPath handler: nil];
125	}
126
127      // save it.
128      return [self writeToFile: path];
129    }
130  return NO;
131}
132
133- (BOOL)writeToFile:(NSString *)filename
134{
135  NSString *path = filename;
136
137  if ([[path pathExtension] isEqual: NSDataLinkFilenameExtension] == NO)
138    {
139      path = [filename stringByAppendingPathExtension: NSDataLinkFilenameExtension];
140    }
141
142  return [NSArchiver archiveRootObject: self toFile: path];
143}
144
145- (void)writeToPasteboard:(NSPasteboard *)pasteboard
146{
147  NSData *data = [NSArchiver archivedDataWithRootObject: self];
148  [pasteboard setData: data forType: NSDataLinkPboardType];
149}
150
151//
152// Information about the Link
153//
154- (NSDataLinkDisposition)disposition
155{
156  return disposition;
157}
158
159- (NSDataLinkNumber)linkNumber
160{
161  return linkNumber;
162}
163
164- (NSDataLinkManager *)manager
165{
166  return sourceManager;
167}
168
169//
170// Information about the Link's Source
171//
172- (NSDate *)lastUpdateTime
173{
174  return lastUpdateTime;
175}
176
177- (BOOL)openSource
178{
179  return NO;
180}
181
182- (NSString *)sourceApplicationName
183{
184  return sourceApplicationName;
185}
186
187- (NSString *)sourceFilename
188{
189  return sourceFilename;
190}
191
192- (NSSelection *)sourceSelection
193{
194  return sourceSelection;
195}
196
197- (NSArray *)types
198{
199  return types;
200}
201
202//
203// Information about the Link's Destination
204//
205- (NSString *)destinationApplicationName
206{
207  return destinationApplicationName;
208}
209
210- (NSString *)destinationFilename
211{
212  return destinationFilename;
213}
214
215- (NSSelection *)destinationSelection
216{
217  return destinationSelection;
218}
219
220//
221// Changing the Link
222//
223- (BOOL)break
224{
225  id srcDelegate = [sourceManager delegate];
226  id dstDelegate = [destinationManager delegate];
227
228  // The spec is quite vague here.  I don't know under what
229  // circumstances a link cannot be broken, so this method
230  // always returns YES.
231
232  if ([srcDelegate respondsToSelector: @selector(dataLinkManager:didBreakLink:)])
233    {
234      [srcDelegate dataLinkManager: sourceManager didBreakLink: self];
235    }
236
237  if ([dstDelegate respondsToSelector: @selector(dataLinkManager:didBreakLink:)])
238    {
239      [dstDelegate dataLinkManager: destinationManager didBreakLink: self];
240    }
241
242  return (_flags.broken = YES);
243}
244
245- (void)noteSourceEdited
246{
247  _flags.isDirty = YES;
248
249  if (updateMode != NSUpdateNever)
250    {
251      [sourceManager noteDocumentEdited];
252    }
253}
254
255- (void)setUpdateMode:(NSDataLinkUpdateMode)mode
256{
257  updateMode = mode;
258}
259
260- (BOOL)updateDestination
261{
262  return NO;
263}
264
265- (NSDataLinkUpdateMode)updateMode
266{
267  return updateMode;
268}
269
270//
271// NSCoding protocol
272//
273- (void) encodeWithCoder: (NSCoder*)aCoder
274{
275  BOOL flag = NO;
276
277  if ([aCoder allowsKeyedCoding])
278    {
279      [aCoder encodeInt: linkNumber forKey: @"GSLinkNumber"];
280      [aCoder encodeInt: disposition forKey: @"GSUpdateMode"];
281      [aCoder encodeInt: updateMode forKey: @"GSLastUpdateMode"];
282
283      [aCoder encodeObject: lastUpdateTime forKey: @"GSLastUpdateTime"];
284
285      [aCoder encodeObject: sourceApplicationName forKey: @"GSSourceApplicationName"];
286      [aCoder encodeObject: sourceFilename forKey: @"GSSourceFilename"];
287      [aCoder encodeObject: sourceSelection forKey: @"GSSourceSelection"];
288      [aCoder encodeObject: sourceManager forKey: @"GSSourceManager"];
289
290      [aCoder encodeObject: destinationApplicationName forKey: @"GSDestinationApplicationName"];
291      [aCoder encodeObject: destinationFilename forKey: @"GSDestinationFilename"];
292      [aCoder encodeObject: destinationSelection forKey: @"GSDestinationSelection"];
293      [aCoder encodeObject: destinationManager forKey: @"GSDestinationManager"];
294
295      [aCoder encodeObject: types forKey: @"GSTypes"];
296
297      // flags...
298      flag = _flags.appVerifies;
299      [aCoder encodeBool: flag forKey: @"GSAppVerifies"];
300      flag = _flags.canUpdateContinuously;
301      [aCoder encodeBool: flag forKey: @"GSCanUpdateContinuously"];
302      flag = _flags.isDirty;
303      [aCoder encodeBool: flag forKey: @"GSIsDirty"];
304      flag = _flags.willOpenSource;
305      [aCoder encodeBool: flag forKey: @"GSWillOpenSource"];
306      flag = _flags.willUpdate;
307      [aCoder encodeBool: flag forKey: @"GSWillUpdate"];
308    }
309  else
310    {
311      [aCoder encodeValueOfObjCType: @encode(int) at: &linkNumber];
312      [aCoder encodeValueOfObjCType: @encode(int) at: &disposition];
313      [aCoder encodeValueOfObjCType: @encode(int) at: &updateMode];
314      [aCoder encodeValueOfObjCType: @encode(id)  at: &lastUpdateTime];
315
316      [aCoder encodeValueOfObjCType: @encode(id)  at: &sourceApplicationName];
317      [aCoder encodeValueOfObjCType: @encode(id)  at: &sourceFilename];
318      [aCoder encodeValueOfObjCType: @encode(id)  at: &sourceSelection];
319      [aCoder encodeValueOfObjCType: @encode(id)  at: &sourceManager];
320
321      [aCoder encodeValueOfObjCType: @encode(id)  at: &destinationApplicationName];
322      [aCoder encodeValueOfObjCType: @encode(id)  at: &destinationFilename];
323      [aCoder encodeValueOfObjCType: @encode(id)  at: &destinationSelection];
324      [aCoder encodeValueOfObjCType: @encode(id)  at: &destinationManager];
325
326      [aCoder encodeValueOfObjCType: @encode(id)  at: &types];
327
328      // flags...
329      flag = _flags.appVerifies;
330      [aCoder encodeValueOfObjCType: @encode(BOOL)  at: &flag];
331      flag = _flags.canUpdateContinuously;
332      [aCoder encodeValueOfObjCType: @encode(BOOL)  at: &flag];
333      flag = _flags.isDirty;
334      [aCoder encodeValueOfObjCType: @encode(BOOL)  at: &flag];
335      flag = _flags.willOpenSource;
336      [aCoder encodeValueOfObjCType: @encode(BOOL)  at: &flag];
337      flag = _flags.willUpdate;
338      [aCoder encodeValueOfObjCType: @encode(BOOL)  at: &flag];
339    }
340}
341
342- (id) initWithCoder: (NSCoder*)aCoder
343{
344  if ([aCoder allowsKeyedCoding])
345    {
346      id obj;
347
348      linkNumber = [aCoder decodeIntForKey: @"GSLinkNumber"];
349      disposition = [aCoder decodeIntForKey: @"GSDisposition"];
350      updateMode = [aCoder decodeIntForKey: @"GSUpdateMode"];
351
352      obj = [aCoder decodeObjectForKey: @"GSSourceManager"];
353      ASSIGN(sourceManager,obj);
354      obj = [aCoder decodeObjectForKey: @"GSDestinationManager"];
355      ASSIGN(destinationManager,obj);
356      obj = [aCoder decodeObjectForKey: @"GSLastUpdateTime"];
357      ASSIGN(lastUpdateTime, obj);
358      obj = [aCoder decodeObjectForKey: @"GSSourceApplicationName"];
359      ASSIGN(sourceApplicationName,obj);
360      obj = [aCoder decodeObjectForKey: @"GSSourceFilename"];
361      ASSIGN(sourceFilename,obj);
362      obj = [aCoder decodeObjectForKey: @"GSSourceSelection"];
363      ASSIGN(sourceSelection,obj);
364      obj = [aCoder decodeObjectForKey: @"GSSourceManager"];
365      ASSIGN(sourceManager,obj);
366      obj = [aCoder decodeObjectForKey: @"GSDestinationApplicationName"];
367      ASSIGN(destinationApplicationName,obj);
368      obj = [aCoder decodeObjectForKey: @"GSDestinationFilename"];
369      ASSIGN(destinationFilename,obj);
370      obj = [aCoder decodeObjectForKey: @"GSDestinationSelection"];
371      ASSIGN(destinationSelection,obj);
372      obj = [aCoder decodeObjectForKey: @"GSDestinationManager"];
373      ASSIGN(destinationManager,obj);
374      obj = [aCoder decodeObjectForKey: @"GSTypes"];
375      ASSIGN(types,obj);
376
377      // flags...
378      _flags.appVerifies = [aCoder decodeBoolForKey: @"GSAppVerifies"];
379      _flags.canUpdateContinuously = [aCoder decodeBoolForKey: @"GSCanUpdateContinuously"];
380      _flags.isDirty = [aCoder decodeBoolForKey: @"GSIsDirty"];
381      _flags.willOpenSource = [aCoder decodeBoolForKey: @"GSWillOpenSource"];
382      _flags.willUpdate = [aCoder decodeBoolForKey: @"GSWillUpdate"];
383    }
384  else
385    {
386      int version = [aCoder versionForClassName: @"NSDataLink"];
387      if (version == 0)
388	{
389	  BOOL flag = NO;
390
391	  [aCoder decodeValueOfObjCType: @encode(int) at: &linkNumber];
392	  [aCoder decodeValueOfObjCType: @encode(int) at: &disposition];
393	  [aCoder decodeValueOfObjCType: @encode(int) at: &updateMode];
394	  [aCoder decodeValueOfObjCType: @encode(id)  at: &sourceManager];
395	  [aCoder decodeValueOfObjCType: @encode(id)  at: &destinationManager];
396	  [aCoder decodeValueOfObjCType: @encode(id)  at: &lastUpdateTime];
397
398	  [aCoder decodeValueOfObjCType: @encode(id)  at: &sourceApplicationName];
399	  [aCoder decodeValueOfObjCType: @encode(id)  at: &sourceFilename];
400	  [aCoder decodeValueOfObjCType: @encode(id)  at: &sourceSelection];
401	  [aCoder decodeValueOfObjCType: @encode(id)  at: &sourceManager];
402
403	  [aCoder decodeValueOfObjCType: @encode(id)  at: &destinationApplicationName];
404	  [aCoder decodeValueOfObjCType: @encode(id)  at: &destinationFilename];
405	  [aCoder decodeValueOfObjCType: @encode(id)  at: &destinationSelection];
406	  [aCoder decodeValueOfObjCType: @encode(id)  at: &destinationManager];
407
408	  [aCoder decodeValueOfObjCType: @encode(id)  at: &types];
409
410	  // flags...
411	  [aCoder decodeValueOfObjCType: @encode(BOOL)  at: &flag];
412	  _flags.appVerifies = flag;
413	  [aCoder decodeValueOfObjCType: @encode(BOOL)  at: &flag];
414	  _flags.canUpdateContinuously = flag;
415	  [aCoder decodeValueOfObjCType: @encode(BOOL)  at: &flag];
416	  _flags.isDirty = flag;
417	  [aCoder decodeValueOfObjCType: @encode(BOOL)  at: &flag];
418	  _flags.willOpenSource = flag;
419	  [aCoder decodeValueOfObjCType: @encode(BOOL)  at: &flag];
420	  _flags.willUpdate = flag;
421	}
422      else
423	return nil;
424    }
425
426  return self;
427}
428
429@end
430