|
|
1.1 root 1: {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;\f1\fmodern Courier;\f2\fnil Times-Roman;\f3\fmodern Ohlfs;}
2: \paperw11340
3: \paperh8400
4: \margl120
5: \margr120
6: {\colortbl;\red0\green0\blue0;}
7: \pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i0\ulnone\fs48\fc0\cf0 Object Links for Draw (file:
8: \f1\fc1\cf1 gvLinks.m
9: \f0\fc0\cf0 )
10: \fs24 \
11: \
12:
13: \f2\fs28 There are a number of things you have to do to implement Object Links in an application. Many of them are optional (depending on the level of functionality you want or are able to provide), but Draw does them ALL, so this should be a good reference point for you.\
14: \
15: Please refer to the documentation in the system about Object Links to get an overall background in place before reading this document.\
16: \
17:
18: \f0\fs36\fc1\cf1 DrawDocument\
19:
20: \f2\fs28\fc0\cf0 \
21: Note first that Object Links only works on a document basis, so the
22: \f1\b\fc1\cf1 GraphicView
23: \f2\b0\fc0\cf0 object cannot do links on its own. Only the
24: \f1\b\fc1\cf1 DrawDocument
25: \f2\b0\fc0\cf0 object knows the name of the file, for example, and this is crucial to making links work. So, even though most of the implementation of Object Links in Draw is in
26: \f1\b\fc1\cf1 GraphicView
27: \f2\b0\fc0\cf0 (actually, a category thereof found in
28: \f1\b\fc1\cf1 gvLinks.m
29: \f2\b0\fc0\cf0 ), you'll notice that it is the
30: \f1\b\fc1\cf1 DrawDocument
31: \f2\b0\fc0\cf0 which creates (and is the delegate of) the
32: \f1\b\fc1\cf1 NXDataLinkManager
33: \f2\b0\fc0\cf0 , etc. However, it usually forwards most of the messages it gets from the
34: \f1\b\fc1\cf1 NXDataLinkManager
35: \f2\b0\fc0\cf0 onto the
36: \f1\b\fc1\cf1 GraphicView
37: \f2\b0\fc0\cf0 .\
38: \
39: Note also that a significant part of making Object Links work in Draw is all the messages that
40: \f1\b\fc1\cf1 DrawDocument
41: \f2\b0\fc0\cf0 sends TO the
42: \f1\b\fc1\cf1 NXDataLinkManager
43: \f2\b0\fc0\cf0 (grep for ``
44: \f1\b\fc1\cf1 [linkManager
45: \f2\b0\fc0\cf0 '' in
46: \f1\b\fc1\cf1 DrawDocument.m
47: \f2\b0\fc0\cf0 to find all those calls).
48: \f1\b\fc1\cf1 DrawDocument
49: \f2\b0\fc0\cf0 is responsible for letting the system know when something about the document changes (e.g. the document is saved or closed or reverted to saved or whatever).\
50: \
51: The ``Publish'' aspect of Draw is done via the
52: \f1\b\fc1\cf1 saveLink:
53: \f2\b0\fc0\cf0 method in
54: \f1\b\fc1\cf1 DrawDocument
55: \f2\b0\fc0\cf0 . You should be able to understand the implementation of this method after reading all the description of how Object Links works below.\
56: \
57:
58: \fc1\cf1 It also calls
59: \f1\b updateLinksPanel
60: \f2\b0 from its
61: \f1\b windowDidUpdate:
62: \f2\b0 method to keep the Link Inspector panel up to date.\
63: \
64: Now let's dive into how Draw actually implements the Object Links mechanism ...\
65:
66: \fc0\cf0 \
67:
68: \f0\fs36\fc1\cf1 Selections\
69:
70: \f2\fs28\fc0\cf0 \
71: The most important part of participating in Object Links is also the part that requires the most thought. It is the process of representing a ``selection'' in your document. It is appropriate that this be the most ``difficult'' thing to do in Object Links because it is the part of the Object Links mechanism that is purely application-dependent. NeXTSTEP tries to do as much of the Object Links functionality for you, but it cannot do the things that are dependent upon what your application does for a living.\
72: \
73:
74: \f0\b\fc1\cf1 A ``linked-to
75: \f2\b0\fc0\cf0 ''
76: \f0\b\fc1\cf1 selection (``source
77: \f2\b0\fc0\cf0 ''
78: \f0\b\fc1\cf1 selections):\
79:
80: \f2\b0\fc0\cf0 \
81: If you want people to link to documents in your application, you must be able to describe a selection that the user makes and then copies and pastes (and links) into another document in another application. This selection description can be anything you want (it's a ``bag o' bits''), but it must survive and make sense no matter what happens to the source document (unless, of course, the items in the selection the user originally made eventually get deleted, but even that case you must detect).\
82: \
83: How you represent this selection is really something you must think about carefully. Draw actually has more than one way of representing the selection (this may well be true in the case of your application too). Draw's selection-representation choice is purely for example purposes and you should, by no means, draw the conclusion that Draw's way is the only way (or even the best way) to represent a selection in an application that manipulates graphical elements (and obviously, Draw's way is not appropriate for text manipulation, spreadsheets, and other kinds of applications).\
84: \
85: Okay, now that the disclaimer is out of the way, let's talk about how Draw represents selections that it exports to other applications. First, note that you can get the ``selection'' that the user has made in a
86: \f1\b\fc1\cf1 GraphicView
87: \f2\b0\fc0\cf0 at any time by calling the
88: \f1\b\fc1\cf1 currentSelection
89: \f2\b0\fc0\cf0 method defined in this file. It returns an
90: \f1\b\fc1\cf1 NXSelection
91: \f2\b0\fc0\cf0 object (the bag o' bits mentioned above) representing the current selection.\
92: \
93: So, how does Draw represent is current selection?\
94: \
95: 1.
96: \f1\b\fc1\cf1 [NXSelection allSelection]
97: \f2\b0\fc0\cf0 \
98: \
99: This is the selection that is created when the user does
100: \f0\fs24 Select All
101: \f2\fs28 (and only in that case). The
102: \f1\b\fc1\cf1 allSelection
103: \f2\b0\fc0\cf0 method of
104: \f1\b\fc1\cf1 NXSelection
105: \f2\b0\fc0\cf0 returns a ``special'' selection that Draw just chooses to know how to interpret. Most applications will want to handle this special-case of
106: \f1\b\fc1\cf1 allSelection
107: \f2\b0\fc0\cf0 .\
108: \
109: 2. Drag-Selection\
110: \
111: When the user drags out a box to make a selection in Draw, the
112: \f1\b\fc1\cf1 NXSelection
113: \f2\b0\fc0\cf0 that Draw uses to represent that state is the rectangle the user dragged out. Then, whenever Draw is asked about this
114: \f1\b\fc1\cf1 NXSelection
115: \f2\b0\fc0\cf0 , it just intersects that rectangle with the current state of the
116: \f1\b\fc1\cf1 Graphic
117: \f2\b0 '
118: \fc0\cf0 s in the view.\
119: \
120: This is a particularly questionable type of selection because the user often ends up with ``not quite what she expected.'' On the other hand, it is a bit more accurate than selection type #3 below because it remembers a bit more of the semantics of what the user selected. In any case, I have included it to show you what an alternative selection mechanism might be like and how to handle it.\
121: \
122: The
123: \f1\b\fc1\cf1 getRect:forSelection:
124: \f2\b0\fc0\cf0 method returns
125: \f1\b\fc1\cf1 YES
126: \f2\b0\fc0\cf0 if the
127: \f1\b\fc1\cf1 NXSelection
128: \f2\b0\fc0\cf0 passed to it is of the drag-select type (and, obviously, the ``rect'' that it ``gets'' is the rect the user dragged out to make her original selection).\
129: \
130: 3. Individual
131: \f1\b\fc1\cf1 Graphic
132: \f2\b0\fc0\cf0 Selection\
133: \
134: In this case, Draw just remembers the unique identifiers of each of the
135: \f1\b\fc1\cf1 Graphic
136: \f2\b0 '
137: \fc0\cf0 s in the selection. Then, when the system asks Draw about a selection of this kind, it looks in the current state of the Draw document for all of these items. Note that it also includes any
138: \f1\b\fc1\cf1 Group
139: \f2\b0\fc0\cf0 objects which include one of the
140: \f1\b\fc1\cf1 Graphic
141: \f2\b0 '
142: \fc0\cf0 s in the original selection. Users can use this to, for example, have a background which they include in the original copy/paste link and then group whatever image they want to be the ``currently exported thing'' with that background.\
143: \
144: The best selection mechanism would probably be some mixture of #2 and #3 (and perhaps some other types of selection mechanisms). I've chosen these two because they are easy to understand.\
145: \
146: The
147: \f1\b\fc1\cf1 findGraphicsInSelection:
148: \f2\b0\fc0\cf0 method returns a
149: \f1\b\fc1\cf1 List
150: \f2\b0
151: \fc0\cf0 object with all the
152: \f1\b\fc1\cf1 Graphic
153: \f2\b0 '
154: \fc0\cf0 s in the current document represented by the
155: \f1\b\fc1\cf1 NXSelection
156: \f2\b0\fc0\cf0 passed to it. This method can handle all three sorts of ``source'' selections (i.e. #1, #2, and #3 above). This method calls the above-mentioned
157: \f1\b\fc1\cf1 getRect:forSelection:
158: \f2\b0\fc0\cf0 method to handle case #2.\
159: \
160:
161: \f0\b\fc1\cf1 A ``linked-from'' selection (``destination'' selections):\
162:
163: \f2\b0\fc0\cf0 \
164: If you allow the user to copy something from another application and
165: \f0\fs24\fc1\cf1 Paste and Link
166: \f2\fs28\fc0\cf0 it into the documents your application edits, you must be able to describe where in your document the thing was
167: \f0\fs24\fc1\cf1 Paste and Link
168: \f2\fs28\fc0\cf0 'ed. This, too, is just a description of a selection in your document.\
169: \
170: Since Draw only allows PostScript and TIFF (i.e.
171: \f1\b\fc1\cf1 NXImage
172: \f2\b0\fc0\cf0 -handled data types) and RTF and ASCII (i.e.
173: \f1\fc1\cf1 Text
174: \f2\fc0\cf0 object-handled data types) to be
175: \f0\fs24\fc1\cf1 Paste and Link
176: \f2\fs28\fc0\cf0 'ed in (of course, these are the only types Draw allows to be normal-pasted in as well!), Draw represents this sort of ``destination'' selection by just remembering which
177: \f1\b\fc1\cf1 Image
178: \f2\b0\fc0\cf0 or
179: \f1\b\fc1\cf1 TextGraphic
180: \f2\b0\fc0\cf0 was created to import the data (since all objects in Draw have a unique identifier associated with them, this is an easy task).\
181: \
182: There is a method implemented in the
183: \f1\b\fc1\cf1 Graphic
184: \f2\b0\fc0\cf0 base class called ``selection'' which returns an
185: \f1\b\fc1\cf1 NXSelection
186: \f2\b0\fc0\cf0 which describes the
187: \f1\b\fc1\cf1 Graphic
188: \f2\b0\fc0\cf0 you sent the message to in terms of its unique identifier (i.e., it creates an
189: \f1\b\fc1\cf1 NXSelection
190: \f2\b0\fc0\cf0 and tosses the unique identifier of the receiving
191: \f1\b\fc1\cf1 Graphic
192: \f2\b0\fc0\cf0 into the bag o' bits and returns it to you). The
193: \f1\b\fc1\cf1 findGraphicInSelection:
194: \f2\b0
195: \fc0\cf0 method in this file searches through the document to find the
196: \f1\b\fc1\cf1 Graphic
197: \f2\b0\fc0\cf0 with the corresponding unique identifier extracted from the
198: \f1\b\fc1\cf1 NXSelection
199: \f2\b0\fc0\cf0 passed to it.\
200: \
201:
202: \f0\fs36\fc1\cf1 Importing/Exporting Link Data\
203:
204: \f2\fs28\fc0\cf0 \
205: Okay, so now you understand how Draw creates an
206: \f1\b\fc1\cf1 NXSelection
207: \f2\b0\fc0\cf0 object to represent either a selection made in a Draw document which is going to be exported to another application via
208: \f0\fs24\fc1\cf1 Copy/Paste and Link
209: \f2\fs28\fc0\cf0 and also how it represents a selection which describes which
210: \f1\b\fc1\cf1 Graphic
211: \f2\b0\fc0\cf0 is the receiving end of an Object Link. Let's quickly talk about how Draw exports a link and how it imports a link.\
212: \
213:
214: \f0\b\fc1\cf1 Exporting:\
215:
216: \f2\b0\fc0\cf0 \
217: It exports a link via the method
218: \f1\b\fc1\cf1 writeLinkToPasteboard:types:
219: \f2\b0\fc0\cf0 . This is a very simple method, but very important to the Object Links mechanism. It does two distinct things:\
220: \
221: 1. It creates and writes an
222: \f1\b\fc1\cf1 NXDataLink
223: \f2\b0\fc0\cf0 object to the
224: \f1\b\fc1\cf1 Pasteboard
225: \f2\b0\fc0\cf0 which includes all the stuff another application would need to know to create an Object Link to the current selection the user has made in Draw (primarily just the
226: \f1\b\fc1\cf1 currentSelection
227: \f2\b0\fc0\cf0 itself and the data types Draw will export (e.g. PostScript and TIFF)). This is the most important thing this method does.\
228: \
229: 2. It writes all of the links in the
230: \f1\b\fc1\cf1 GraphicView
231: \f2\b0\fc0\cf0 to the
232: \f1\b\fc1\cf1 Pasteboard
233: \f2\b0\fc0\cf0 .\
234: \
235: Why, you may ask, does it do this? Well, if you copy an
236: \f1\b\fc1\cf1 Image
237: \f2\b0\fc0\cf0 in Draw which is actually the destination of an Object Link (not the source of a link, but the DESTINATION), then if you pasted that
238: \f1\b\fc1\cf1 Image
239: \f2\b0\fc0\cf0 into another Draw document, you want it to keep its ``linkness'', i.e., you want the thing you pasted to also get updates when the source of that
240: \f1\b\fc1\cf1 Image
241: \f2\b0\fc0\cf0 gets updated. Simple, huh?\
242: \
243: Which brings us to the
244: \f1\b\fc1\cf1 readLinkForGraphic:fromPasteboard:useNewIdentifier:
245: \f2\b0\fc0\cf0 method. It's the thing that is called every time you paste a
246: \f1\b\fc1\cf1 Graphic
247: \f2\b0\fc0\cf0 into Draw to get that pasted
248: \f1\b\fc1\cf1 Graphic
249: \f2\b0\fc0\cf0 properly linked up with the
250: \f1\b\fc1\cf1 NXDataLinkManager
251: \f2\b0\fc0\cf0 in the Draw document you paste it into.\
252: \
253: It is implemented by calling the
254: \f1\b\fc1\cf1 addLinkPreviouslyAt:fromPasteboard:at:
255: \f2\b0\fc0\cf0 method in
256: \f1\b\fc1\cf1 NXDataLinkManager
257: \f2\b0\fc0\cf0 which simply reestablishes the link that
258: \f1\b\fc1\cf1 Image
259: \f2\b0\fc0\cf0 has to another document (that was at
260: \i oldSelection
261: \i0 in the old document) by setting the destination selection of the link to the selection which represents the
262: \f1\b\fc1\cf1 Image
263: \f2\b0\fc0\cf0 's location in the new document (
264: \f1\b\fc1\cf1 [graphic selection]
265: \f2\b0\fc0\cf0 ).\
266: \
267: The
268: \i useNewIdentifier
269: \i0 thing is so that if you copy and immediately paste back into the same document, no actual change occurs (this is important in case someone else is linked to something that is in turn linked to something else--just trust me, you want copy/paste from/to the same document to be a net ``no-change'' in the document as far as links are concerned).\
270: \
271:
272: \f0\b\fc1\cf1 Importing:\
273:
274: \f2\b0\fc0\cf0 \
275: Importing a linked thing happens
276: \b only
277: \b0 via the
278: \f1\b\fc1\cf1 addLink:toGraphic:at:update:
279: \f2\b0\fc0\cf0 method. No where else in Draw is a linked thing added to the document (except, of course in
280: \f1\b\fc1\cf1 readLinkForGraphic:fromPasteboard:useNewIdentifier:
281: \f2\b0\fc0\cf0 , but that's a special case).\
282: \
283: Let's quickly summarize how this method works:\
284: \
285: The arguments are simple. The
286: \i link
287: \i0 is an
288: \f1\b\fc1\cf1 NXDataLink
289: \f2\b0\fc0\cf0 gotten either from a file (
290: \f1\b\fc1\cf1 .objlink
291: \f2\b0\fc0\cf0 ) or from a
292: \f1\b\fc1\cf1 Pasteboard
293: \f2\b0\fc0\cf0 (during
294: \f0\fs24\fc1\cf1 Paste and Link
295: \f2\fs28\fc0\cf0 ) or was alloc/init'ed pointing to a file. See the callers of
296: \f1\b\fc1\cf1 addLink:
297: \f2 ...
298: \b0\fc0\cf0 to see about that. The
299: \i graphic
300: \i0 is just an
301: \f1\b\fc1\cf1 Image
302: \f2\b0\fc0\cf0 or
303: \f1\b\fc1\cf1 TextGraphic
304: \f2\b0\fc0\cf0 created from the same
305: \f1\b\fc1\cf1 Pasteboard
306: \f2\b0\fc0\cf0 we got the
307: \i link
308: \i0 out of or from the file that we alloc/init'ed the
309: \i link
310: \i0 to point to. If
311: \i graphic
312: \i0
313: \f1\b\fc1\cf1 nil
314: \f2\b0\fc0\cf0 , then we probably got the
315: \i link
316: \i0 from a
317: \f1\b\fc1\cf1 .objlink
318: \f2\b0\fc0\cf0 file, so we don't actually know what kind of data we're talking about yet. We take care of that first thing in this method (see the next paragraph). The
319: \i update
320: \i0 argument is used to describe whether this is a normal link, or a link which is never updated (link buttons and links to files represented by the file's icon are the classic examples of these) or a link which must be updated immediately because we don't yet have any data for it (again, see the next paragraph).\
321: \
322: The first if-statement handles the case of pasting or dragging in an
323: \f1\b\fc1\cf1 NXDataLink
324: \f2\b0\fc0\cf0 without any corresponding data (i.e. no PostScript or TIFF to go with it). This is always the case for a
325: \f1\b\fc1\cf1 .objlink
326: \f2\b0\fc0\cf0 file, and could conceivably be the case for a
327: \f0\fs24\fc1\cf1 Copy/Paste and Link
328: \f2\fs28\fc0\cf0 if the app that copied the stuff in only copied the
329: \f1\b\fc1\cf1 NXDataLink
330: \f2\b0\fc0\cf0 and forgot to (or chose not to for some reason) put the thing being linked to itself in the
331: \f1\b\fc1\cf1 Pasteboard
332: \f2\b0\fc0\cf0 . Anyway, what that first if-statement does is figure out what data types the
333: \f1\b\fc1\cf1 NXDataLink
334: \f2\b0\fc0\cf0 deals in (again, e.g., PostScript or RTF or some such) and creates an ``empty''
335: \f1\b\fc1\cf1 Graphic
336: \f2\b0\fc0\cf0 (an
337: \f1\b\fc1\cf1 Image
338: \f2\b0\fc0\cf0 or
339: \f1\b\fc1\cf1 TextGraphic
340: \f2\b0\fc0\cf0 ) which will be filled in immediately when, later in the method, we force an
341: \f1\b\fc1\cf1 updateDestination
342: \f2\b0\fc0\cf0 to occur (setting the update mode to
343: \f1\b\fc1\cf1 UPDATE_IMMEDIATELY
344: \f2\b0\fc0\cf0 is what does this).\
345: \
346: The second if-statement is what's doing all the work, of course. First, it asks the
347: \f1\b\fc1\cf1 Graphic
348: \f2\b0\fc0\cf0 which is going to be the destination of this Object Link (it'll be an
349: \f1\b\fc1\cf1 Image
350: \f2\b0\fc0\cf0 or
351: \f1\b\fc1\cf1 TextGraphic
352: \f2\b0\fc0\cf0 ) for an
353: \f1\b\fc1\cf1 NXSelection
354: \f2\b0\fc0\cf0 object which represents it. Then it ``adds'' the link to the
355: \f1\b\fc1\cf1 NXDataLinkManager
356: \f2\b0\fc0\cf0 . If the link is successfully added, then we let the
357: \f1\b\fc1\cf1 Image
358: \f2\b0\fc0\cf0 or
359: \f1\b\fc1\cf1 TextGraphic
360: \f2\b0\fc0\cf0 know about the link to it (only so that we can ask for it back later, the
361: \f1\b\fc1\cf1 Image
362: \f2\b0\fc0\cf0 and
363: \f1\b\fc1\cf1 TextGraphic
364: \f2\b0\fc0\cf0 's never actually do anything themselves with the link). Next, we put the
365: \f1\b\fc1\cf1 Graphic
366: \f2\b0\fc0\cf0 into the document using the standard
367: \f1\b\fc1\cf1 placeGraphic:at:
368: \f2\b0\fc0\cf0 method that we always use to add foreign data to the view (see
369: \f1\b\fc1\cf1 gvPasteboard.m
370: \f2\b0\fc0\cf0 ).\
371: \
372: Finally, if we need to update the link immediately because we have no data, we do so by calling
373: \f1\b\fc1\cf1 updateDestination
374: \f2\b0\fc0\cf0 , then ensuring that the update actually caused some data to flow over by seeing if the
375: \f1\b\fc1\cf1 Graphic
376: \f2\b0\fc0\cf0
377: \f1\b\fc1\cf1 isValid
378: \f2\b0\fc0\cf0 . This works well for
379: \f1\b\fc1\cf1 Image
380: \f2\b0\fc0\cf0 's, but not so well for
381: \f1\b\fc1\cf1 TextGraphic
382: \f2\b0\fc0\cf0 's, I'm afraid (they always say they are valid!). Anyway, it's better than nothing.\
383: \
384: That's it for exporting and importing links. Not so bad, is it?\
385: \
386:
387: \f0\fs36\fc1\cf1 Updating Links\
388:
389: \f2\fs28\fc0\cf0 \
390: Now, how do we actually update links (in either direction)? This, too, is simple. Whenever NeXTSTEP wants you to update someone else who is linked to you, it sends you the message
391: \f1\b\fc1\cf1 copyToPasteboard:at:cheapCopyAllowed:
392: \f2\b0\fc0\cf0 . Whenever NeXTSTEP asks someone else to update something that is linked into your document, it sends you the message
393: \f1\b\fc1\cf1 pasteFromPasteboard:at:
394: \f2\b0\fc0\cf0 (or
395: \f1\b\fc1\cf1 importFile:at:
396: \f2\b0\fc0\cf0 if it's a whole file). All you have to do is to responds to these messages sensibly (you should assume that they can be called at any time). Return
397: \f1\b\fc1\cf1 nil
398: \f2\b0\fc0\cf0 from these methods if the
399: \f1\b\fc1\cf1 NXSelection
400: \f2\b0\fc0\cf0 's in question no longer exist (in their entirety).\
401: \
402: Draw's implementation of these methods is very straightforward (these methods are almost always really easy to implement if you already implement
403: \f0\fs24\fc1\cf1 Copy/Paste
404: \f2\fs28\fc0\cf0 or
405: \f0\fs24\fc1\cf1 Services
406: \f2\fs28\fc0\cf0 ).\
407: \
408: In
409: \f1\b\fc1\cf1 pasteFromPasteboard:at:
410: \f2\b0\fc0\cf0 , it just finds the
411: \f1\b\fc1\cf1 Image
412: \f2\b0\fc0\cf0 or
413: \f1\b\fc1\cf1 TextGraphic
414: \f2\b0\fc0\cf0 represented by the
415: \f1\b\fc1\cf1 NXSelection
416: \f2\b0\fc0\cf0 passed to it (see
417: \f1\b\fc1\cf1 findGraphicInSelection:
418: \f2\b0\fc0\cf0 ), then sends a message to that
419: \f1\b\fc1\cf1 Graphic
420: \f2\b0\fc0\cf0 to reinitialize itself with the data in the
421: \f1\b\fc1\cf1 Pasteboard
422: \f2\b0\fc0\cf0 passed to it. It then updates the view and marks the view as edited.\
423: \
424: The method
425: \f1\b\fc1\cf1 importFile:at:
426: \f2\b0\fc0\cf0 is just like
427: \f1\b\fc1\cf1 pasteFromPasteboard:at:
428: \f2\b0\fc0\cf0 , except that the source of the data comes out of a file instead of from a
429: \f1\b\fc1\cf1 Pasteboard
430: \f2\b0\fc0\cf0 . This happens when you create an Object Link to a whole file without involving the application that knows how to edit that file (see
431: \f1\b\fc1\cf1 gvDrag.m
432: \f2\b0\fc0\cf0 and the stuff where we drag a file into Draw with the Control key down (which means create a link to this file)).\
433: \
434: In
435: \f1\b\fc1\cf1 copyToPasteboard:at:cheapCopyAllowed:
436: \f2\b0\fc0\cf0 , there are basically two paths that can be taken depending on whether
437: \i\fc1\cf1 cheapCopyAllowed
438: \i0 is true
439: \fc0\cf0 .
440: \i cheapCopyAllowed
441: \i0 just means that you can use the lazy pasteboard mechanism to the fullest because NeXTSTEP guarantees that no changes to your document can occur between the time this method is called and the time the lazy
442: \f1\b\fc1\cf1 provideData:
443: \f2\b0\fc0\cf0 is called. In other words, when
444: \i cheapCopyAllowed
445: \i0 is true, we don't actually have to write the Draw objects in the selection to the pasteboard by value, we can simply write a reference to them.\
446: \
447: So, in Draw, when
448: \i cheapCopyAllowed
449: \i0 is true, we just declare that we can provide PostScript and TIFF, but write neither to the
450: \f1\b\fc1\cf1 Pasteboard
451: \f2\b0\fc0\cf0 (we'll provide it lazily). Of course, when the lazy
452: \f1\b\fc1\cf1 provideData:
453: \f2\b0\fc0\cf0 is called, we have to know what part of our document to put into the
454: \f1\b\fc1\cf1 Pasteboard
455: \f2\b0\fc0\cf0 , so we simply drop in the
456: \f1\b\fc1\cf1 NXSelection
457: \f2\b0\fc0\cf0 that we were asked to
458: \f1\b\fc1\cf1 copyToPasteboard:
459: \f2\b0\fc0\cf0 .\
460: \
461: Thus, in the
462: \i cheapCopyAllowed
463: \i0 case, the actual work of putting the data in is done in the INSTANCE method
464: \f1\b\fc1\cf1 pasteboard:provideData:
465: \f2\b0\fc0\cf0 ! It is okay to use the instance as the owner of the
466: \f1\b\fc1\cf1 Pasteboard
467: \f2\b0\fc0\cf0 because the system has guaranteed us that our document would not be changed (especially not FREED!). The implementation of
468: \f1\b\fc1\cf1 provideData:
469: \f2\b0\fc0\cf0 is really simple since we already had methods lying around that could write the PostScript or TIFF for a list of
470: \f1\b\fc1\cf1 Graphic
471: \f2\b0 '
472: \fc0\cf0 s into a stream (
473: \f1\b\fc1\cf1 write
474: \f2 \{
475: \f1 PS
476: \f2 ,
477: \f1 TIFF
478: \f2 \}
479: \f1 ToStream:usingList:
480: \f2\b0\fc0\cf0 ). We get the list of
481: \f1\b\fc1\cf1 Graphic
482: \f2\b0 '
483: \fc0\cf0 s to write from the
484: \f1\b\fc1\cf1 NXSelection
485: \f2\b0\fc0\cf0 we put in there (see how this all just dovetails together? Idn it great?).\
486: \
487: When
488: \i cheapCopyAllowed
489: \i0 is not true, then we just do what we normally do when the user hits
490: \f0\fs24\fc1\cf1 Copy
491: \f2\fs28\fc0\cf0 , we just do it with the
492: \f1\b\fc1\cf1 Graphic
493: \f2\b0 '
494: \fc0\cf0 s that are in the passed
495: \f1\b\fc1\cf1 NXSelection
496: \f2\b0\fc0\cf0 instead of the ones in the current selection. We plop the list of
497: \f1\b\fc1\cf1 Graphic
498: \f2\b0 '
499: \fc0\cf0 s into the
500: \f1\b\fc1\cf1 Pasteboard
501: \f2\b0\fc0\cf0 and let the normal lazy
502: \f1\b\fc1\cf1 Pasteboard
503: \f2\b0\fc0\cf0 stuff take care of the rest (the CLASS method
504: \f1\b\fc1\cf1 pasteboard:provideData:
505: \f2\b0\fc0\cf0 in this case, see
506: \f1\b\fc1\cf1 gvPasteboard.m
507: \f2\b0\fc0\cf0 ).\
508: \
509:
510: \f0\fs36\fc1\cf1 Miscellaneous methods.\
511:
512: \f2\fs28\fc0\cf0 \
513: There's a few other little methods you may want to implement.\
514: \
515: You'll probably want something akin to
516: \f1\b\fc1\cf1 updateLinksPanel
517: \f2\b0\fc0\cf0 which just keeps the Link Inspector panel up to date (it is called from
518: \f1\b\fc1\cf1 windowDidUpdate:
519: \f2\b0\fc0\cf0 in
520: \f1\b\fc1\cf1 DrawDocument
521: \f2\b0\fc0\cf0 ).\
522: \
523: The
524: \f1\b\fc1\cf1 showSelection:
525: \f2\b0\fc0\cf0 method in
526: \f1\b\fc1\cf1 gvLinks.m
527: \f2\b0\fc0\cf0 (the actual names of some of these methods is different, see
528: \f1\b\fc1\cf1 DrawDocument.m
529: \f2\b0\fc0\cf0 which forwards them onto
530: \f1\b\fc1\cf1 GraphicView
531: \f2\b0\fc0\cf0 ) is sent by NeXTSTEP when the user asks to show the source of an Object Link that comes from your document. It is very nice to respond properly to this message (the user will certainly be expecting this to work in your application). It is very easy for Draw to get the bounding box of the
532: \f1\b\fc1\cf1 Graphic
533: \f2\b0 '
534: \fc0\cf0 s in the passed selection (it even draws the little drag-selection rectangle if that's the kind of
535: \f1\b\fc1\cf1 NXSelection
536: \f2\b0\fc0\cf0 it is) since we already have methods lying around that, given a list of
537: \f1\b\fc1\cf1 Graphic
538: \f2\b0\fc0\cf0 's can find their bounding box.\
539: \
540: There is one notable thing that Draw does when showing source selections. It uses the fact that all the drawing done in a Draw document is actually done in an off-screen cache and composited to the screen. When Draw shows a source selection, it draws them directly to the on-screen window, then remembers the areas in which it draw (this is the
541: \f1\b\fc1\cf1 invalidRect
542: \f2\b0\fc0\cf0 ). Then, it leaves the source selection showing until the user touches the view (see
543: \f1\b\fc1\cf1 drawSelf::
544: \f2\b0\fc0\cf0 ) at which point, it just blows the
545: \f1\b\fc1\cf1 invalidRect
546: \f2\b0\fc0\cf0 away by copying that rectangle from the off-screen cache. If you do double-buffering like this in your application, this trick is easy and effective.\
547: \
548: The
549: \f1\b\fc1\cf1 breakLinkAndRedrawOutlines:
550: \f2\b0\fc0\cf0 method in Draw is what keeps the link outlines up-to-date. When the user chooses
551: \f0\fs24\fc1\cf1 Show Links
552: \f2\fs28\fc0\cf0 from the menu, all things that are linked into your document should show a border around them (there is a NeXTSTEP function to draw this border). These borders are kind of the opposite of what the
553: \f1\b\fc1\cf1 showSelection:
554: \f2\b0\fc0\cf0 method draws (i.e.
555: \f1\b\fc1\cf1 showSelection:
556: \f2\b0\fc0\cf0 shows what Object Links originate in your document, and
557: \f0\fs24\fc1\cf1 Show Links
558: \f2\fs28\fc0\cf0 shows the Object Links that are linked into your document from somewhere else). The argument to
559: \f1\b\fc1\cf1 breakLinkAndRedrawOutlines:
560: \f2\b0\fc0\cf0 is a
561: \i link
562: \i0 that was recently broken by NeXTSTEP (this method is called from
563: \b DrawDocument
564: \b0 's
565: \f1\b\fc1\cf1 dataLinkManager:didBreakLink:
566: \f2\b0\fc0\cf0 and
567: \f1\b\fc1\cf1 dataLinkManagerRedrawLinkOutlines:
568: \f2\b0\fc0\cf0 methods which are sent by NeXTSTEP).\
569: \
570: If the
571: \i link
572: \i0 argument is
573: \f1\b\fc1\cf1 nil
574: \f2\b0\fc0\cf0 , it means that no link was broken, so Draw just redraws
575: \b all
576: \b0 the link outlines. If the argument is not
577: \f1\b\fc1\cf1 nil
578: \f2\b0\fc0\cf0 , then the method searches for the
579: \f1\b\fc1\cf1 Graphic
580: \f2\b0\fc0\cf0 which held that
581: \i link
582: \i0 and redraws it so that it's outline goes away. Furthermore, if it was a link that didn't show the source data (i.e. it was a link button or file icon or something), that
583: \f1\b\fc1\cf1 Graphic
584: \f2\b0\fc0\cf0 is removed from the document (since it is now disconnected and useless--don't we all feel that way sometimes?).\
585: \
586:
587: \f0\fs36\fc1\cf1 Tracking Links\
588:
589: \f2\fs28\fc0\cf0 \
590: Finally, there is the task of tracking the sources of links. This is optional behaviour but is really a must if you want to implement
591: \f0\fs24\fc1\cf1 Continually
592: \f2\fs28\fc0\cf0 updating links. The idea here is that you tell NeXTSTEP when a selection which is the source of a link which you export has changed. Otherwise, NeXTSTEP has to assume that every time your document is edited that all the links that you export have changed. In other words, this is a performance optimization, but a valuable one.\
593: \
594: Note that you don't have to track
595: \b all
596: \b0 your links, only the ones that are showing up in other documents that are on the screen at the same time. NeXTSTEP (through the
597: \f1\b\fc1\cf1 NXDataLinkManager
598: \f2\b0\fc0\cf0 ) will tell you when to start and stop tracking links (NeXTSTEP is such a polite entity, is it not?).\
599: \
600: Draw tracks links very easily by making the assumption that if any region of the Draw document which is redrawn overlaps the source of a link, that link must have changed and needs to be updated. Since Draw has a nice knothole through which all updates to the document go (
601: \f1\b\fc1\cf1 cache:
602: \f2\b0\fc0\cf0 ), this is a mere matter of keeping track of the boundaries of the sources of links which Draw exports.\
603: \
604: Draw does this by keeping a
605: \f1\b\fc1\cf1 Storage
606: \f2\b0\fc0\cf0 object which a struct in it that has three pieces of information.\
607: \
608: 1. The rectangle which encloses the source of the link.\
609: 2. The link in question.\
610: 3. What type of selection is involved (all, drag or normal).\
611: \
612: Almost every time
613: \f1\b\fc1\cf1 cache:
614: \f2\b0\fc0\cf0 is called (sometimes
615: \f1\b\fc1\cf1 cache:
616: andUpdateLinks:
617: \f2\b0 is called with
618: \f1 NO
619: \f2 as its argument
620: \fc0\cf0 , but not very often, grep the code and you find out the times when that is necessary) the method
621: \f1\fc1\cf1 updateTrackedLinks:
622: \f2\fc0\cf0 is called. This method has a two-fold purpose:\
623: \
624: 1. Notify the
625: \f1\b\fc1\cf1 NXDataLinkManager
626: \f2\b0\fc0\cf0 if any of the currently-being-tracked links intersects the area which was just
627: \f1\b\fc1\cf1 cache:
628: \f2\b0\fc0\cf0 'ed.\
629: 2. Reevaluate the bounds of any of the source selections that intersects the area which was just
630: \f1\b\fc1\cf1 cache:
631: \f2\b0\fc0\cf0 'ed.\
632: \
633: We must do step #2, because the thing that might have caused
634: \f1\b\fc1\cf1 cache:
635: \f2\b0\fc0\cf0 to get called could have been that the user resized one of the objects which are linked to. Thus, step #2 is not necessary for the drag-selection (since that originally dragged-out box can never ``change size'') and
636: \f1\b\fc1\cf1 allSelection
637: \f2\b0\fc0\cf0 cases. Step #2 is implemented simply by getting the
638: \f1\b\fc1\cf1 NXSelection
639: \f2\b0\fc0\cf0 from the link, calling
640: \f1\b\fc1\cf1 findGraphicsInSelection:
641: \f2\b0\fc0\cf0 , then calling the already-existing
642: \f1\b\fc1\cf1 getBBox:of:
643: \f2\b0\fc0\cf0 method.\
644: \
645: All we do in
646: \f1\b\fc1\cf1 startTrackingLinks:
647: \f2\b0\fc0\cf0 and
648: \f1\b\fc1\cf1 stopTrackingLinks:
649: \f2\b0\fc0\cf0 is add/remove structs from the
650: \f1\b\fc1\cf1 Storage
651: \f2\b0\fc0\cf0 object.\
652: \
653:
654: \f0\fs36\fc1\cf1 Summary\
655:
656: \f2\fs28\fc0\cf0 \
657: Well, that's all there is about links and Draw. I hope this document is illuminating. The take-home messages should be that Object Links should be simple to implement if you already implement
658: \f0\fs24\fc1\cf1 Copy/Paste
659: \f2\fs28\fc0\cf0 and/or
660: \f0\fs24\fc1\cf1 Services
661: \f2\fs28\fc0\cf0 . The only ``hard part'' might be figuring out how to represent a selection in your document. Good luck with that part. :-)\
662:
663: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.