home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Information / CSMP Digest / volume 2 / csmp-v2-002.txt < prev    next >
Encoding:
Text File  |  1994-12-08  |  42.0 KB  |  1,067 lines  |  [TEXT/R*ch]

  1. C.S.M.P. Digest             Fri, 08 Jan 93       Volume 2 : Issue 2
  2.  
  3. Today's Topics:
  4.  
  5.     How can I draw arrowheads on lines?
  6.     How long can an AE handler take?
  7.     Calling C from Pascal vs calling Pascal from C
  8.     Perfect Window Zooming (longish)
  9.     Is this a BaseAddress problem with the Quadra .?.?.?.
  10.  
  11.  
  12.  
  13. The Comp.Sys.Mac.Programmer Digest is moderated by Michael A. Kelly.
  14.  
  15. The digest is a collection of article threads from the internet newsgroup
  16. comp.sys.mac.programmer.  It is designed for people who read c.s.m.p. semi-
  17. regularly and want an archive of the discussions.  If you don't know what a
  18. newsgroup is, you probably don't have access to it.  Ask your systems
  19. administrator(s) for details.  If you don't have access to news, there is
  20. no way that I know of for you to post articles to the group.
  21.  
  22. Each issue of the digest contains one or more sets of articles (called
  23. threads), with each set corresponding to a 'discussion' of a particular
  24. subject.  The articles are not edited; all articles included in this digest
  25. are in their original posted form (as received by our news server at
  26. cs.uoregon.edu).  Article threads are not added to the digest until the last
  27. article added to the thread is at least one month old (this is to ensure that
  28. the thread is dead before adding it to the digest).  Article threads that
  29. consist of only one message are generally not included in the digest.
  30.  
  31. The entire digest is available for anonymous ftp from ftp.cs.uoregon.edu
  32. [128.223.8.8] in the directory /pub/mac/csmp-digest.  Be sure to read the
  33. file /pub/mac/csmp-digest/README before downloading any files.  The most
  34. recent issues are available from sumex-aim.stanford.edu [36.44.0.6] in the
  35. directory /info-mac/digest/csmp.  If you don't have ftp capability, the sumex
  36. archive has a mail server; send a message with the text '$MACarch help' (no
  37. quotes) to LISTSERV@ricevm1.rice.edu for more information.
  38.  
  39. The digest is also available via email.  Just send a note saying that you
  40. want to be on the digest mailing list to mkelly@cs.uoregon.edu, and you will
  41. automatically receive each new issue as it is created.  Sorry, back issues
  42. are not available through the mailing list.
  43.  
  44. Send administrative mail to mkelly@cs.uoregon.edu.
  45.  
  46.  
  47. -------------------------------------------------------
  48.  
  49. From: tcwan@umiami.ir.miami.edu
  50. Subject: How can I draw arrowheads on lines?
  51. Date: 3 Dec 92 14:02:41 GMT
  52. Organization: Univ of Miami IR
  53.  
  54. I'm not sure where to look for this info, so if someone could either
  55. point me to the right source (I checked the UMPG, without success) or
  56. could supply some code snippets, I'd appreciate it :) :)
  57.  
  58. I'm trying to draw arrowed lines like those in MacDraw or Canvas. Is
  59. there a simple way to accomplish this? The lines can have any gradient
  60. and the arrowhead sizes are supposed to be quite small (within a 4x4 to 8x8
  61. enclosing square).
  62.  
  63. PS: I'm programming using TC 5.0.2 & TCL 1.1.2, if that helps
  64.  
  65. Thanks for your help,
  66. t.c.
  67. Grad Student,
  68. Univ of Miami, FL
  69. #I reserve the right to be wrong :)
  70.  
  71. +++++++++++++++++++++++++++
  72.  
  73. Date: 4 Dec 92 21:58:25 GMT
  74. Organization: Royal Institute of Technology, Stockholm, Sweden
  75.  
  76. In <1992Dec3.090241.14165@umiami.ir.miami.edu> tcwan@umiami.ir.miami.edu writes:
  77.  
  78. >I'm not sure where to look for this info, so if someone could either
  79. >point me to the right source (I checked the UMPG, without success) or
  80. >could supply some code snippets, I'd appreciate it :) :)
  81.  
  82. Use a polygon. Beware the off-by-one drawing of polygons.
  83.  
  84. Cheers,
  85.  
  86.                         / h+
  87.  
  88. - -- 
  89.  -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
  90.    The word "politics" is derived from the word "poly", meaning
  91.    "many", and the word "ticks", meaning "blood sucking parasites".
  92.                      -- Larry Hardiman
  93.  
  94. +++++++++++++++++++++++++++
  95.  
  96. From: tcwan@umiami.ir.miami.edu
  97. Date: 4 Dec 92 21:42:47 EST
  98. Organization: Univ of Miami IR
  99.  
  100. tte) writes:
  101. > In <1992Dec3.090241.14165@umiami.ir.miami.edu> tcwan@umiami.ir.miami.edu writes:
  102. >>I'm not sure where to look for this info, so if someone could either
  103. >>point me to the right source (I checked the UMPG, without success) or
  104. >>could supply some code snippets, I'd appreciate it :) :)
  105. > Use a polygon. Beware the off-by-one drawing of polygons.
  106.  
  107. Well, Eggert Thorlacius (ebth@rhi.hi.is) suggested using an arc to approximate
  108. a triangle, and it works pretty well. (ie: use PtToAngle to find the angle
  109. of the line, and drawing a filled arc centered about the point where the 
  110. arrow should be. The reason I was hesitant on using polygons is that I'd have 
  111. to calculate the gradient and normal, then find out which points to set as the
  112. vertices.
  113.  
  114. If QD supplies some of this functions for free (I believe it's done in fixed
  115. point, so it should be faster too), I'd rather do it that way.
  116. Thanks for all your help though :)
  117.  
  118. t.c.
  119.  
  120. > Cheers, 
  121. >                         / h+
  122. > > -- 
  123. >  -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
  124. >    The word "politics" is derived from the word "poly", meaning
  125. >    "many", and the word "ticks", meaning "blood sucking parasites".
  126. >                      -- Larry Hardiman
  127.  
  128. +++++++++++++++++++++++++++
  129.  
  130. From: lhp@daimi.aau.dk (Lasse Hiller|e Petersen)
  131. Date: 5 Dec 92 07:14:01 GMT
  132. Organization: DAIMI: Computer Science Department, Aarhus University, Denmark
  133.  
  134. tcwan@umiami.ir.miami.edu writes:
  135. [How to use arcs as arrowheads deleted]
  136. >If QD supplies some of this functions for free (I believe it's done in fixed
  137. >point, so it should be faster too), I'd rather do it that way.
  138.  
  139. Filled arcs. That's how Word 5 does it. Ugliest arrowheads I've ever seen!
  140. - --
  141. Lasse Hiller|e Petersen                   !  /*] Once upon a time at 2:30 in
  142. lhp@daimi.aau.dk - lassehp@imv.aau.dk     ! /*<  the afternoon, there lived
  143. Department of Information & Media Science !/* *\ a wise&benevolent&wonderful
  144. Aarhus University, Denmark                ! L L  wizard who wore a big hat...
  145.  
  146. ---------------------------
  147.  
  148. From: goodill_eric@tandem.com (Eric Goodill)
  149. Subject: How long can an AE handler take?
  150. Date: 2 Dec 92 16:29:54 GMT
  151. Organization: Tandem Computers, Inc.
  152.  
  153. Hi,
  154.  
  155. I'm writing a drag-and-drop application: you drag a file onto it, it does
  156. it's thing to the file, and quits.  The thing it does takes about 20
  157. seconds or so (at least on a IIci).  Should I go ahead and call my code
  158. that takes this long directly from my odoc handler, or should I just set a
  159. flag in odoc (saving the info about the dropped file in a global) and then
  160. return to my event loop which detects the flag and takes the time.  How
  161. long does Apple's Apple event manager that called my odoc handler expect me
  162. to take?  Are there any limits to how long my handlers can take before
  163. returning?
  164.  
  165. Cheers, Eric
  166. Eric Goodill, goodill_eric@tandem.com, Tandem Computers, Cupertino, Calif.
  167.  
  168. +++++++++++++++++++++++++++
  169.  
  170. Organization: Royal Institute of Technology, Stockholm, Sweden
  171. Date: Thu, 3 Dec 1992 12:32:42 GMT
  172.  
  173. In <goodill_eric-021292082533@ericg.cpd.tandem.com> goodill_eric@tandem.com (Eric Goodill) writes:
  174.  
  175. >I'm writing a drag-and-drop application: you drag a file onto it, it does
  176. >it's thing to the file, and quits.  The thing it does takes about 20
  177. >seconds or so (at least on a IIci).  Should I go ahead and call my code
  178. >that takes this long directly from my odoc handler, or should I just set a
  179. >flag in odoc (saving the info about the dropped file in a global) and then
  180. >return to my event loop which detects the flag and takes the time.  How
  181.  
  182. t call WaitNextEvent reliably in an AppleEvent handler,
  183. unless you call AEInteractWithUser first.
  184.  
  185. re calling WaitNextEvent, you should be fine, since
  186. background tasks get their time. However, it might simply be the
  187. easiest to retrieve all files affected as a handle of FSSpecs, and
  188. use the main loop to take the time.
  189.  
  190. Cheers,
  191.  
  192.                             / h+
  193.  
  194. - -- 
  195.  -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
  196.  
  197.   "From now on I will re-label the EQ on the deck as Fizz and Wobble
  198.    instead of HF and LF."
  199.  
  200. +++++++++++++++++++++++++++
  201.  
  202. From: jpugh@apple.com (Jon Pugh)
  203. Date: 7 Dec 92 03:25:15 GMT
  204. Organization: Apple Computer, Inc.
  205.  
  206. In article <goodill_eric-021292082533@ericg.cpd.tandem.com>,
  207. goodill_eric@tandem.com (Eric Goodill) wrote:
  208. > Hi,
  209. > I'm writing a drag-and-drop application: you drag a file onto it, it does
  210. > it's thing to the file, and quits.  The thing it does takes about 20
  211. > seconds or so (at least on a IIci).  Should I go ahead and call my code
  212. > that takes this long directly from my odoc handler, or should I just set a
  213. > flag in odoc (saving the info about the dropped file in a global) and then
  214. > return to my event loop which detects the flag and takes the time.  How
  215. > long does Apple's Apple event manager that called my odoc handler expect me
  216. > to take?  Are there any limits to how long my handlers can take before
  217. > returning?
  218.  
  219. There are no limits built into the AE manager.  All events are sent with a
  220. timeout specified.  This is how the AE manager decides how long to wait for
  221. a reply.  The Finder sends a timeout of about 300 ticks, but it doesn't
  222. wait for a reply anyhow, so it doesn't matter.  The Finder just goes about
  223. its business.  
  224.  
  225. You can take as long as you like.  Don't bother messing with the reply from
  226. the odoc event (or the oapp or the pdoc events).  Other events should be
  227. handled on an event by event basis.
  228.  
  229. Jon
  230.  
  231. ---------------------------
  232.  
  233. From: felciano@summit.stanford.edu (Ramon M. Felciano)
  234. Subject: Calling C from Pascal vs calling Pascal from C
  235. Organization: Stanford University Medical Media and Information 
  236. Date: Sun, 22 Nov 92 01:01:38 GMT
  237.  
  238. I would like to clean up my tools and utilities libraries in order to
  239. make them as generically useful as possible. I would like the routines
  240. to be callable both from THINK Pascal and from THINK C. Which should I
  241. use to write the routines and build the library with?
  242.  
  243. Thanks!
  244.  
  245. Ramon
  246.  
  247. +----------------------------------------------------------------+
  248. | Ramon M. Felciano                felciano@summit.stanford.edu  |
  249. | Associate Director, SUMMIT       (415) 723-9688                |
  250. | Stanford University Medical Media and Information Technologies |
  251. +----------------------------------------------------------------+
  252.  
  253. +++++++++++++++++++++++++++
  254.  
  255. From: mrubin@hebb.uoregon.edu (Matthew Rubin)
  256. Date: 28 Nov 92 00:52:46 GMT
  257. Organization: Institute of Cognitive & Decision Sciences
  258.  
  259. Calling C from Pascal vs calling Pascal from C
  260.  
  261. As Think C exists right now I don't think Symantec provided any support
  262. for including
  263. compiled Think Pascal libraries into a Think C project.  However, the
  264. reverse, including compiled Think C libraries into a Think Pascal project
  265. is supported.  Make sure you
  266. declare all C functions intended to be called from the Think Pascal
  267. environment with
  268. the *pascal* keyword.  In order to call a compiled C function in a
  269. library after it
  270. has been added to the Think Pascal project, you first need to create an
  271. interface file
  272. which gives all the prototypes (ie. function names and formal parameter
  273. lists) for
  274. the library functions you wish to be visible to the Think Pascal
  275. evironment.  See 
  276. Think Pascal 4.0's user manual (p. 183 -Using Think C with Think Pascal)
  277. for more information.
  278.  
  279. +++++++++++++++++++++++++++
  280.  
  281. From: peter@ncrpda.curtin.edu.au (Peter N Lewis)
  282. Date: 30 Nov 92 01:26:09 GMT
  283. Organization: NCRPDA, Curtin University
  284.  
  285. In article <1992Nov28.005246.4382@sol.ctr.columbia.edu>, Matthew Rubin
  286. <mrubin@hebb.uoregon.edu> wrote:
  287.  
  288. > As Think C exists right now I don't think Symantec provided any support
  289. > for including compiled Think Pascal libraries into a Think C project.  
  290. > However, the reverse, including compiled Think C libraries into a Think 
  291. > Pascal project is supported.  Make sure you declare all C functions 
  292. > intended to be called from the Think Pascal environment with the 
  293. > *pascal* keyword.  In order to call a compiled C function in a
  294. > library after it has been added to the Think Pascal project, you 
  295. > first need to create an interface file which gives all the prototypes 
  296. > (ie. function names and formal parameter lists) for the library 
  297. > functions you wish to be visible to the Think Pascal evironment.  See 
  298. > Think Pascal 4.0's user manual (p. 183 -Using Think C with Think Pascal)
  299. > for more information.
  300.  
  301. The problem with all this is that if you write any reasonable library in
  302. either C or Pascal, you need to include trap calls, like HOpen.  But if you
  303. call HOpen in the Pascal library, then in includes glue code defining
  304. HOpen, when you try to link this with a C library which also calls HOpen,
  305. you get a link error because the glue for each HOpen (the Pascal and C
  306. interface) are both included.
  307.  
  308. I havent found a way around this which effectively makes it impossible to
  309. include reasonable C or Pascal libraries in a Pascal or C program.  If
  310. anyone knows how to get around this I'd be interested in hearing about it.
  311.    Peter. 
  312.  
  313. _______________________________________________________________________
  314. Peter N Lewis <peter@ncrpda.curtin.edu.au>           Ph: +61 9 368 2055
  315.  
  316. +++++++++++++++++++++++++++
  317.  
  318. From: rhorn@csws19.ic.sunysb.edu (Robert Horn)
  319. Date: 30 Nov 92 20:39:10 GMT
  320. Organization: State University of New York at Stony Brook
  321.  
  322. In article <peter-301192092234@rocky.curtin.edu.au> peter@ncrpda.curtin.edu.au (Peter N Lewis) writes:
  323. >In article <1992Nov28.005246.4382@sol.ctr.columbia.edu>, Matthew Rubin
  324. ><mrubin@hebb.uoregon.edu> wrote:
  325. >
  326. >The problem with all this is that if you write any reasonable library in
  327. >either C or Pascal, you need to include trap calls, like HOpen.  But if you
  328. >call HOpen in the Pascal library, then in includes glue code defining
  329. >HOpen, when you try to link this with a C library which also calls HOpen,
  330. >you get a link error because the glue for each HOpen (the Pascal and C
  331. >interface) are both included.
  332. >
  333. >I havent found a way around this which effectively makes it impossible to
  334. >include reasonable C or Pascal libraries in a Pascal or C program.  If
  335. >anyone knows how to get around this I'd be interested in hearing about it.
  336.  
  337. Perhaps I'm missing something (nonetheless I post my ignorance across the 
  338. entire civilized world), but can't you just not include the glue for the
  339. traps in your library? I believe that the glue is the very same glue for
  340. both of Symantec's compilers, and even if not exactly the same, it should
  341. be mostly compatable, since the Think C glue uses pascal calling conventions.
  342.  
  343.  
  344.  
  345.  
  346.  
  347. - -- 
  348. rhorn@ic.sunysb.edu         Never choose a college because it has a duckpond.
  349. $@`                                             Send me hate mail, I love it.
  350.  
  351. +++++++++++++++++++++++++++
  352.  
  353. From: russells@ccu1.aukuni.ac.nz (Russell Street)
  354. Date: 30 Nov 92 23:59:31 GMT
  355. Organization: University of Auckland, New Zealand.
  356.  
  357. peter@ncrpda.curtin.edu.au (Peter N Lewis) writes:
  358.  
  359. >In article <1992Nov28.005246.4382@sol.ctr.columbia.edu>, Matthew Rubin
  360. ><mrubin@hebb.uoregon.edu> wrote:
  361.  
  362. >> As Think C exists right now I don't think Symantec provided any support
  363. >> for including compiled Think Pascal libraries into a Think C project.  
  364.  
  365. Well not directly at least.  Think Pascal libraries are compatible
  366. with MPW object files so you can use MPW to convert them to an MPW object
  367. file "proper" and then use Think C's oConv utility to turn it to a
  368. Think C project -- whew!
  369.  
  370. >The problem with all this is that if you write any reasonable library in
  371. >either C or Pascal, you need to include trap calls, like HOpen.  But if you
  372. >call HOpen in the Pascal library, then in includes glue code defining
  373. >HOpen, when you try to link this with a C library which also calls HOpen,
  374. >you get a link error because the glue for each HOpen (the Pascal and C
  375. >interface) are both included.
  376.  
  377. That is because you _are_ including the glue twice.  The solution
  378. is to remove MacTraps from the Think C project before you build
  379. it as a library.
  380.  
  381. - -----------------------------------------------------------------
  382. Russell Street (russells@ccu1.aukuni.ac.nz)
  383.     Listeners are requested to make the necessary adjustments
  384.  
  385. +++++++++++++++++++++++++++
  386.  
  387. From: peter@ncrpda.curtin.edu.au (Peter N Lewis)
  388. Organization: NCRPDA, Curtin University
  389. Date: Thu, 3 Dec 1992 05:09:15 GMT
  390.  
  391. In article <1992Nov30.203910.14412@sbcs.sunysb.edu>,
  392. rhorn@csws19.ic.sunysb.edu (Robert Horn) wrote:
  393. > In article <peter-301192092234@rocky.curtin.edu.au> peter@ncrpda.curtin.edu.au (Peter N Lewis) writes:
  394.  
  395. > >I havent found a way around this which effectively makes it impossible to
  396. > >include reasonable C or Pascal libraries in a Pascal or C program.  If
  397. > >anyone knows how to get around this I'd be interested in hearing about it.
  398. > Perhaps I'm missing something (nonetheless I post my ignorance across the 
  399. > entire civilized world), but can't you just not include the glue for the
  400. > traps in your library? I believe that the glue is the very same glue for
  401. > both of Symantec's compilers, and even if not exactly the same, it should
  402. > be mostly compatable, since the Think C glue uses pascal calling conventions.
  403.  
  404. Well, someone correct me if I'm wrong, but as far as I could tell at the
  405. time I tried this, the TC glue defines HOpen (and presuambly lots of
  406. others) as C functions, not pascal functions.  The TP glue defines them as
  407. Pascal functions.  So if you don't include the C glue, the C routines will
  408. explode when they call the pascal glue using C calling conventions. 
  409. Anyway, that was my interpretation of the problem when I tried this
  410. briefly, I could be wrong, it happens quite often :-)
  411.    Peter.
  412.  
  413. _______________________________________________________________________
  414. Peter N Lewis <peter@ncrpda.curtin.edu.au>           Ph: +61 9 368 2055
  415.  
  416. +++++++++++++++++++++++++++
  417.  
  418. From: mrubin@hebb.uoregon.edu (Matthew Rubin)
  419. Date: 7 Dec 92 00:11:51 GMT
  420. Organization: Institute of Cognitive & Decision Sciences
  421.  
  422. In article <peter-031292130750@rocky.curtin.edu.au> Peter N Lewis,
  423. peter@ncrpda.curtin.edu.au writes:
  424. >Well, someone correct me if I'm wrong, but as far as I could tell at the
  425. >time I tried this, the TC glue defines HOpen (and presuambly lots of
  426. >others) as C functions, not pascal functions.  The TP glue defines them
  427. as
  428. >Pascal functions.  So if you don't include the C glue, the C routines
  429. will
  430. >explode when they call the pascal glue using C calling conventions. 
  431. >Anyway, that was my interpretation of the problem when I tried this
  432. >briefly, I could be wrong, it happens quite often :-)
  433.  
  434. When I originally posted I did not anticipate calling Toolbox routines
  435. from 
  436. within the C library.  If you are going to call Toolbox routines from the
  437. TC library,
  438. declare the routine as *extern pascal* and don't include MacTraps in your
  439. TC library.
  440. That way  the toolbox is only included once in TP and any toolbox
  441. routines in the TC
  442. library follow pascal calling conventions because of their *extern
  443. pascal* keyword
  444. definitions.
  445.  
  446. ---------------------------
  447.  
  448. From: nerm@apple.com (Dean Yu)
  449. Subject: Perfect Window Zooming (longish)
  450. Date: 7 Dec 92 02:10:34 GMT
  451. Organization: Apple Computer, Inc.
  452.  
  453.  
  454.   About once a year, I go on a tirade about how few programs zoom their
  455. windows properly. Most programs zoom to the full size of the main screen;
  456. some others only make the window as big as they need to, but still move it
  457. to the main screen, even if I have the window on another monitor.  The 7.0
  458. Finder comes close to making me happy, but every once in a while, it has
  459. the annoying habit of zipping a window to the main screen for some unknown
  460. reason.
  461.   I was writing some application code recently, so I took the opportunity
  462. to make it zoom windows the way I wished everything else would zoom
  463. windows.  I'm posting the code that came out the other side in the hopes
  464. that more programs will zoom windows correctly in the future.
  465.  
  466.   What follows are the rules describing how zooming windows should behave. 
  467. Most of it is a re-hash of the HI Tech Note on zooming windows, but some of
  468. it is new stuff that Apple hasn't really told people "this is how you
  469. should do it" yet.
  470.  
  471.   Rules on Size:
  472.   1)  The standard state of a window should be the most useful size for
  473. your program:  word processors, for example, would probably want their
  474. standard state to be the size of a printed page.  The Finder's most useful
  475. size is the size that will just show all the icons in that window.
  476.   2)  If the most useful size would be larger than the monitor you're
  477. zooming to, the dimension that is larger than the monitor should be pinned
  478. to that dimension of the monitor.  If the standard state of a word
  479. processor document window is 8.5 by 11 inches, but the screen is less than
  480. 11 inches high, the window should only be as high as the height of the
  481. monitor.
  482.   3)  When zooming a window to the screen with the menu bar, leave a strip
  483. of space on the right side of the screen to let volume icons in the Finder
  484. show through.
  485.  
  486.   Rules on Position:
  487.   The basic guideline for positioning a window during zooming is that the
  488. window should move as little as possible in order to avoid distracting the
  489. user.
  490.   4)  A window in it's standard state should be positioned so that it is
  491. entirely on one monitor.  When a window is zoomed from the user state to
  492. the standard state, and the window is straddling more than one screen, the
  493. window should be zoomed to the monitor that contains the largest portion of
  494. the window's content region.
  495.   5)  If possible, the standard state of a window should be anchored at the
  496. current top left corner of the window.  When a window zooms from the user
  497. state to the standard state, and the standard state size will fit on the
  498. screen without moving the window, just resize the window.  If the standard
  499. state of the window will not fit on the screen, offset the window so that
  500. the standard state will fit.  (Note that this is different than the zooming
  501. window HI Tech Note, which suggests moving the window to a default position
  502. if zooming to the standard state will cause a portion of the window to wind
  503. up offscreen.)
  504.  
  505.   This is best described with an illustration.  Say you have a screen setup
  506. like the following, where a window is partially offscreen:
  507.                       _________________________
  508.                       |_______________________|
  509.     __________________|.......................|
  510.     |.................|.......................|
  511.     |.................|.......................|
  512.     |.................|.......................|
  513.     |.......__________|____...................|
  514.     |.......|         |   |...................|
  515.     |.......|         |   |...................|
  516.     |-----------------|-----------------------|
  517.             |_____________|
  518.  
  519.   After zooming to it's standard state, the window should be positioned on
  520. the monitor that has most if it's content region, like this:
  521.                       _________________________
  522.                       |_______________________|
  523.     __________________|.......................|
  524.     |.................|.......................|
  525.     |.................|.......................|
  526.     |.................|.......................|
  527.     |...______________|.......................|
  528.     |...|            ||.......................|
  529.     |...|            ||.......................|
  530.     |...|            ||.......................|
  531.     |...|____________||.......................|
  532.     |-----------------|-----------------------|
  533.  
  534.  
  535.   A Few Notes About the Code
  536.   People keep asking me why I insist on using MPW instead of Think.  The
  537. best answer I can give is that I'm used to MPW.  This code was written in
  538. MPW C, so Think C users might have to do some tweaking.
  539.   The CalculatePerfectDocumentSize() routine is the one routine that will
  540. vary from application to application.  In this routine, you should
  541. calculate the optimum size for you document window as if you had one
  542. infinitely large monitor.  I've included what I needed to do in this
  543. routine for my application; yours will probably be more complicated.
  544.   The coolest bit of inspiration that hit me when I was writing this code
  545. was to have DeviceLoop() walk through the screens for me and call a routine
  546. to figure out window area for each screen the window intersects.  This
  547. simplifies the code a lot, but of course, it limits it to System 7 users. 
  548. What a bummer.
  549.   Any illegible characters that come out are curly quotes which I use in my
  550. source, but don't translate well when posting a Usenet article.
  551.   Finally, this code assumes standard rectangular document windows.  If
  552. you're not using 'WDEF' 0, you might have to adjust the kMinTitleBarHeight
  553. constant.  If you don't have rectangular windows, you'll have to rework the
  554. CalcWindowAreaOnScreen() routine to figure out the area of the window based
  555. on it's geometric shape.  (Anyone remember how to figure out the area of a
  556. circle?  I don't...)
  557.  
  558.   The Code (with apologies about the formatting)
  559.  
  560. struct ZoomData {
  561.     GDHandle    screenWithLargestPartOfWindow;
  562.     long        largestArea;
  563.     Rect        windowBounds;
  564. };
  565. typedef struct ZoomData ZoomData, *ZoomDataPtr;
  566.  
  567. #define    kNudgeSlop            4
  568. #define    kIconSpace            64
  569. #define    kMinTitleBarHeight    21
  570.  
  571. void    ZoomTheWindow(WindowPeek theWindow, short zoomState);
  572.  
  573. pascal    void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle
  574.                                     targetDevice, long userData);
  575. void   CalculatePerfectDocumentSize(WindowPtr theWindow, Rect
  576. *contentSize);
  577. short  CalculateOffsetAmount(short startPoint1, short endPoint1, short
  578.                               startPoint2, short endPoint2, short
  579. screenEdge1,
  580.                               short screenEdge2);
  581.  
  582.  
  583. void    ZoomTheWindow(WindowPeek theWindow, short zoomState)
  584. {
  585.     ZoomData    zoomData;
  586.     FontInfo    systemFontInfo;
  587.     Rect        newStandardRect;
  588.     Rect        scratchRect;
  589.     Rect        screenRect;
  590.     GrafPtr        currentPort;
  591.     GrafPtr        windowManagerPort;
  592.     RgnHandle    localContentRegion;
  593.     short        horizontalAmountOffScreen;
  594.     short        verticalAmountOffScreen;
  595.     short        titleBarHeight;
  596.     
  597.     GetPort(¤tPort);
  598.     
  599.     // Calculate the height of the title bar
  600.     
  601.     GetWMgrPort(&windowManagerPort);
  602.     SetPort(windowManagerPort);
  603.     GetFontInfo(&systemFontInfo);
  604.     titleBarHeight = (short) (systemFontInfo.ascent + systemFontInfo.descent +
  605. 4);
  606.     if ((titleBarHeight % 2) == 1)
  607.         titleBarHeight--;
  608.     if (titleBarHeight < kMinTitleBarHeight)
  609.         titleBarHeight = kMinTitleBarHeight;
  610.         
  611.     SetPort((WindowPtr) theWindow);
  612.     
  613.  // If the window is being zoomed into the standard state, calculate the
  614. best
  615.  // size to display the windowUs information.
  616.     
  617.     if (zoomState == inZoomOut) {
  618.         zoomData.screenWithLargestPartOfWindow = nil;
  619.         zoomData.largestArea = -1;
  620.         zoomData.windowBounds = (**(theWindow->contRgn)).rgnBBox;
  621.     
  622.     // Use DeviceLoop to walk through all the active screens to find the one
  623. with 
  624.     // the largest portion of the zoomed window
  625.     
  626.         localContentRegion = NewRgn();
  627.         RectRgn(localContentRegion, &(((WindowPtr) theWindow)->portRect));
  628.         DeviceLoop(localContentRegion, &CalcWindowAreaOnScreen, (long) &zoomData,
  629.              (DeviceLoopFlags) singleDevices);
  630.         DisposeRgn(localContentRegion);
  631.         screenRect = (**(zoomData.screenWithLargestPartOfWindow)).gdRect;
  632.         
  633.     // Go figure out the perfect size for the window as if we had an
  634. infinitely 
  635.  // large screen
  636.     
  637.         CalculatePerfectDocumentSize((WindowPtr) theWindow, &newStandardRect);
  638.         
  639.     // Anchor the new rectangle at the windowUs current top left corner
  640.     
  641.         OffsetRect(&newStandardRect, (**theWindow->contRgn).rgnBBox.left,
  642.              (**theWindow->contRgn).rgnBBox.top);
  643.         
  644.     // If the new rectangle falls off the edge of the screen, nudge it so that
  645. itUs
  646.  // just on the screen.
  647.     
  648.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  649.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  650.             horizontalAmountOffScreen = CalculateOffsetAmount(newStandardRect.left,
  651.                                                      newStandardRect.right,
  652.                                                      scratchRect.left, 
  653.                                                      scratchRect.right,
  654.                                                      screenRect.left, 
  655.                                                      screenRect.right);
  656.             verticalAmountOffScreen = CalculateOffsetAmount(newStandardRect.top,
  657.                                                    newStandardRect.bottom, 
  658.                                                    scratchRect.top,
  659.                                                    scratchRect.bottom, 
  660.                                                    screenRect.top, 
  661.                                                    screenRect.bottom);
  662.             OffsetRect(&newStandardRect, horizontalAmountOffScreen,
  663.                verticalAmountOffScreen);
  664.         }
  665.     
  666.     // If weUre still falling off the edge of the screen, that means that the 
  667.  // perfect size is larger than the screen, so we need to shrink down the 
  668.  // standard size
  669.     
  670.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  671.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  672.             if (newStandardRect.left < screenRect.left)
  673.                 newStandardRect.left = (short) (screenRect.left + kNudgeSlop);
  674.     
  675.     // Move in the right edge.  If weUre zooming to the main screen, leave
  676. some 
  677.  // space for the first row of Finder icons on the right.
  678.     
  679.             if (newStandardRect.right > screenRect.right)
  680.                 newStandardRect.right = (short) (screenRect.right - kNudgeSlop);
  681.             if ((zoomData.screenWithLargestPartOfWindow == GetMainDevice()) &&
  682.                    (newStandardRect.right > (screenRect.right - kIconSpace)))
  683.                 newStandardRect.right = (short) (screenRect.right - kIconSpace);
  684.     
  685.     // Move in the top.  If weUre zooming to the main screen, make sure the
  686. window
  687.  // doesnUt wind up underneath the menu bar.
  688.     
  689.             if (newStandardRect.top < screenRect.top)
  690.                 newStandardRect.top = (short) (screenRect.top + kNudgeSlop +
  691.                                    titleBarHeight);
  692.             if ((zoomData.screenWithLargestPartOfWindow == GetMainDevice()) &&
  693.                    (newStandardRect.top < (screenRect.top + GetMBarHeight() +
  694.         titleBarHeight)))
  695.                 newStandardRect.top = (short) (screenRect.top + GetMBarHeight() + 
  696.                                    titleBarHeight);
  697.  
  698.             if (newStandardRect.bottom > screenRect.bottom)
  699.                 newStandardRect.bottom = (short) (screenRect.bottom - kNudgeSlop);
  700.         }
  701.     
  702.     // WeUve got the best possible window position.  Slam it into the
  703. WStateData
  704.  // record, and let ZoomWindow take care of the rest.
  705.         
  706.         (**((WStateDataHandle) (theWindow->dataHandle))).stdState =
  707. newStandardRect;
  708.     }
  709.     
  710.     ClipRect(&(((WindowPtr) theWindow)->portRect));
  711.     EraseRect(&(((WindowPtr) theWindow)->portRect));
  712.     ZoomWindow((WindowPtr) theWindow, zoomState, false);
  713.     
  714.     SetPort(currentPort);
  715. }
  716.  
  717. pascal void    CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle
  718.  
  719.                                     targetDevice, long userData)
  720. {
  721. #pragma unused (depth, deviceFlags)
  722.     ZoomDataPtr    zoomData = (ZoomDataPtr) userData;
  723.     long        windowAreaOnScreen;
  724.     Rect        windowPortionOnScreen;
  725.     
  726.     SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect),
  727.           &windowPortionOnScreen);
  728.     OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left,
  729.             -windowPortionOnScreen.top);
  730.     windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) 
  731.                              windowPortionOnScreen.bottom;
  732.     
  733.     if (windowAreaOnScreen > zoomData->largestArea) {
  734.         zoomData->largestArea = windowAreaOnScreen;
  735.         zoomData->screenWithLargestPartOfWindow = targetDevice;
  736.     }
  737. }
  738.  
  739. void CalculatePerfectDocumentSize(WindowPtr theWindow, Rect *contentSize)
  740. {
  741.     ArticleInfoPtr    currentArticle;
  742.     short            runningHeight;
  743.     short            lineHeight;
  744.     
  745.     lineHeight = GetLineHeight();
  746.     runningHeight = (short) (lineHeight + 4);
  747.     if (lineHeight < kMinLineHeight)
  748.         lineHeight = kMinLineHeight;
  749.         
  750.     currentArticle = (**((IssueInfoHandle) GetWRefCon(theWindow))).articles;
  751.     while (currentArticle != nil) {
  752.         runningHeight += lineHeight;
  753.         currentArticle = currentArticle->nextArticle;
  754.     }
  755.     
  756.     runningHeight++;
  757.     SetRect(contentSize, 0, 0, (theWindow->portRect).right - 
  758.          (theWindow->portRect).left, runningHeight);
  759. }
  760.  
  761. // Figure out how much we need to move the window to get it entirely on the
  762.  
  763. // monitor.  If the window wouldnUt fit completely on the monitor anyway,
  764. donUt
  765. // move it at all; weUll make it fit later on.
  766.  
  767. short    CalculateOffsetAmount(short startPoint1, short endPoint1, short 
  768.                             startPoint2, short endPoint2, short
  769. screenEdge1,
  770.                             short screenEdge2)
  771. {
  772.     short    offsetAmount;
  773.  
  774.     if ((startPoint1 < screenEdge1) && (endPoint1 > screenEdge2))
  775.         offsetAmount = 0;
  776.     else {
  777.         offsetAmount = (short) ((endPoint1 - startPoint1) - (endPoint2 - 
  778.                           startPoint2));
  779.         if (offsetAmount > 0)
  780.             offsetAmount += kNudgeSlop;
  781.         if (endPoint1 > screenEdge2)
  782.             offsetAmount = (short) (-offsetAmount);
  783.     }
  784.     
  785.     return offsetAmount;
  786. }
  787.  
  788. - -- Dean Yu
  789.    Blue Meanie, Negative Ethnic Role Model, etc.
  790.    Apple Computer, Inc.
  791.  
  792. +++++++++++++++++++++++++++
  793.  
  794. From: keith@taligent.com (Keith Rollin)
  795. Organization: Taligent
  796. Date: Mon, 7 Dec 1992 20:47:47 GMT
  797.  
  798. In article <nerm-061292170139@90.10.20.86>, nerm@apple.com (Dean Yu) wrote:
  799. >   About once a year, I go on a tirade about how few programs zoom their
  800. > windows properly.
  801.  
  802. Dean, thanks for posting this code. I hope that you can get it incorporated
  803. into Technote #79 sometime. Also, it might be interesting to compare your
  804. method with MacApp's and the Finder's (what does yours do that they
  805. don't?).
  806.  
  807. >   Finally, this code assumes standard rectangular document windows.  If
  808. > you're not using 'WDEF' 0, you might have to adjust the kMinTitleBarHeight
  809. > constant.
  810. >   ...
  811. > #define    kMinTitleBarHeight    21
  812. >   ...
  813. >     // Calculate the height of the title bar
  814. >     
  815. >     GetWMgrPort(&windowManagerPort);
  816. >     SetPort(windowManagerPort);
  817. >     GetFontInfo(&systemFontInfo);
  818. >     titleBarHeight = (short) (systemFontInfo.ascent + systemFontInfo.descent +
  819. > 4);
  820. >     if ((titleBarHeight % 2) == 1)
  821. >         titleBarHeight--;
  822. >     if (titleBarHeight < kMinTitleBarHeight)
  823. >         titleBarHeight = kMinTitleBarHeight;
  824.  
  825. I'm wondering why you do it this way. If you wanted the size of the
  826. titlebar, why not subtract the top of the content region from the top of
  827. the structure region? 
  828.  
  829. In fact, this approach should probably be taken on all four sides of the
  830. window's bounds. The approach I take is to calculate the bounds for the
  831. window's _frame_, not its content area. Once I have that, I inset it by
  832. delta's between the window's contRgn and strucRgn. This gives me a
  833. rectangle that I can use in calls to ZoomWindow, MoveWindow, and
  834. SizeWindow.
  835.  
  836. >        localContentRegion = NewRgn();
  837. >        RectRgn(localContentRegion, &(((WindowPtr) theWindow)->portRect));
  838. >        DeviceLoop(localContentRegion, &CalcWindowAreaOnScreen, (long) &zoomData,
  839. >             (DeviceLoopFlags) singleDevices);
  840. >        DisposeRgn(localContentRegion);
  841. >        screenRect = (**(zoomData.screenWithLargestPartOfWindow)).gdRect;
  842.  
  843. It might be more generic here to clone the contRgn and localize it, rather
  844. than assume that the content area is rectangular.
  845.  
  846. >    ClipRect(&(((WindowPtr) theWindow)->portRect));
  847. >    EraseRect(&(((WindowPtr) theWindow)->portRect));
  848. >    ZoomWindow((WindowPtr) theWindow, zoomState, false);
  849.  
  850. One thing that some programs do, including the Finder, that's really nice
  851. is examine the case where the window size changes, but not its location. In
  852. that circumstance, they just call SizeWindow. One of the advantages of this
  853. is that they don't have to call EraseRect, and don't end up redrawing the
  854. entire window contents.
  855.  
  856. - -----
  857. Keith Rollin
  858. Phantom Programmer
  859. Taligent, Inc.
  860.  
  861. +++++++++++++++++++++++++++
  862.  
  863. From: nerm@apple.com (Dean Yu)
  864. Date: Mon, 7 Dec 1992 22:22:48 GMT
  865. Organization: Apple Computer, Inc.
  866.  
  867. In article <keith-071292122558@kip-15.taligent.com>, keith@taligent.com
  868. (Keith Rollin) wrote:
  869. > Dean, thanks for posting this code. I hope that you can get it incorporated
  870. > into Technote #79 sometime. Also, it might be interesting to compare your
  871. > method with MacApp's and the Finder's (what does yours do that they
  872. > don't?).
  873.  
  874.   The biggest difference is that if the Finder determines that the standard
  875. state won't fit on the screen by keeping the window anchored to it's
  876. current position, it moves the window to a default position.  My version
  877. nudges the window so that it will fit on the screen.  This is one of my
  878. biggest peeves about how the Finder zooms it's windows.  If you have a
  879. window near the bottom of the screen and you zoom it, the Finder snaps it
  880. to the top left of the screen.  I'd much rather have the window just bumped
  881. up a bit so that all of it shows up in it's new state instead of it moving
  882. halfway across the screen where I have to refocus on the window.
  883.  
  884. > I'm wondering why you do it this way. If you wanted the size of the
  885. > titlebar, why not subtract the top of the content region from the top of
  886. > the structure region? 
  887.  
  888.   Because I knew how 'WDEF' 0 calculates the title bar height.  :)
  889.   I didn't think of your way, and I agree it's better.
  890.  
  891. - -- Dean Yu
  892.    Blue Meanie, Negative Ethnic Role Model, etc.
  893.    Apple Computer, Inc.
  894.  
  895. ---------------------------
  896.  
  897. From: BLOOMDA@ctrvax.vanderbilt.edu (_creative_output_)
  898. Subject: Is this a BaseAddress problem with the Quadra .?.?.?.
  899. Date: 18 Nov 92 23:26:53 GMT
  900. Organization: Camp Vanderbilt Vision Research Center
  901.  
  902. Hi, Gals & Guys.
  903.  
  904. I do pseudo-instantaneous presentation of visual stimuli using
  905. color table tricks.  I.E., I read a byte from video RAM, write
  906. into only the high or low 4 bits, as desired, and then write
  907. my byte back out.  Appropriately managing color tables, I can
  908. paint an image of up to 16 colors invisibly while another image
  909. of up to 16 colors is onscreen, then swap them in one VRT.
  910.  
  911. This technique broke when I tried it on a 24-bit card set to
  912. 8-bit mode.  That's when I learned about incorporating the slot
  913. number of the video card into the base address:
  914.      ( ( baseAddr >> 24 ) | something ) << 20 | baseAddr, or somesuch
  915.  
  916. One of my bosses has left town for another University.  He took his
  917. third of my funding, and now I'm part-time here, so I'm incentivized
  918. to keep supporting him through email.
  919.  
  920. He has a Quadra.
  921.  
  922. My code generates Bus Errors on his machine, during the loop which
  923. accesses video RAM.  This is _similar_ to the errors generated by
  924. my earlier failure to account for the slot number.
  925.  
  926. I have 'testing access' to a Quadra 700 here, but not 'development
  927. access'.  That means I can go and bother someone in his office for
  928. a moment to see if I got it right, and crash his machine many times.
  929.  
  930. I've gotten a hold of IM v6, the monster, and shall be perusing it
  931. tonight.  It may hold a solution.  However, I'm moved to ask you
  932. folks out there if anyone has a quick hint.  Anybody run into this
  933. difference?
  934.  
  935. (s)he who helps shall receive a brief burst of good thoughts from
  936. Music City, U.S.A. and a personal thank-you.  I'll check news and
  937. mail at 2 a.m., so listen up hard for that burst of good karma...
  938.  
  939. - - Dave    =(^;|=                Dave Bloom, JSPS
  940.                     bn: bloomda@vuctrvax
  941.                     in: bloomda@ctrvax.vanderbilt.edu
  942. "Mekka Lekka Hai, Lekka Heinie Ho"
  943.  
  944. +++++++++++++++++++++++++++
  945.  
  946. From: absurd@apple.apple.com (Tim Dierks, software saboteur)
  947. Date: Fri, 20 Nov 1992 00:38:14 GMT
  948. Organization: MacDTS Marauders
  949.  
  950. In article <1992Nov18.232653.13027@news.vanderbilt.edu>,
  951. BLOOMDA@ctrvax.vanderbilt.edu (_creative_output_) wrote:
  952. > 8-bit mode.  That's when I learned about incorporating the slot
  953. > number of the video card into the base address:
  954. >      ( ( baseAddr >> 24 ) | something ) << 20 | baseAddr, or somesuch
  955. > - Dave    =(^;|=                Dave Bloom, JSPS
  956. >                     bn: bloomda@vuctrvax
  957. >                     in: bloomda@ctrvax.vanderbilt.edu
  958. > "Mekka Lekka Hai, Lekka Heinie Ho"
  959.  
  960. Here's the right way to get the slot number of a monitor, given its
  961. GDevice:  (as a bonus, this also gets the name of the card in
  962. question; to just get the slot, chop off all the lines after the
  963. *slot = ... line.
  964.  
  965. OSErr
  966. GetSlotAndName(GDHandle gDev,short *slot,char *name)
  967. {   OSErr       err;
  968.     short       refNum;
  969.     SpBlock     spb;
  970.     
  971.     refNum = (**gDev).gdRefNum;    // video driver refNum for this GDevice
  972.     *slot = (**(AuxDCEHandle)GetDCtlEntry(refNum)).dCtlSlot;
  973.                                    // slot in which this video card sits
  974.     
  975.     spb.spSlot = *slot;         // In the slot we're interested in
  976.     spb.spID = 0;
  977.     spb.spExtDev = 0;
  978.     spb.spCategory = 1;         // Get the board sResource
  979.     spb.spCType = 0;
  980.     spb.spDrvrSW = 0;
  981.     spb.spDrvrHW = 0;
  982.     spb.spTBMask = 0;
  983.     
  984.     err = SNextTypeSRsrc(&spb);
  985.     if (err)
  986.         return err;
  987.     
  988.     
  989.     spb.spID = 2;               // Getting the description string
  990.                                 // spSPointer was set up by
  991. SNextTypesResource
  992.     err = SGetCString(&spb);
  993.     if (err)
  994.         return err;
  995.     
  996.     strcpy(name,(char *)spb.spResult);  // Get the returned string
  997.     DisposPtr((Ptr)spb.spResult);    // Undocumented; we have to dispose of
  998. it
  999.     
  1000.     c2pstr(name);
  1001.     
  1002.     return noErr;
  1003. }
  1004.  
  1005. You should also be very careful to use SwapMMUMode() to switch into
  1006. 32-bit mode any time you write to the framebuffer, regardless of what
  1007. bitdepth it is at.
  1008.  
  1009. Tim Dierks
  1010. MacDTS, but what?
  1011.  
  1012. +++++++++++++++++++++++++++
  1013.  
  1014. From: paul@taniwha.UUCP (Paul Campbell)
  1015. Date: 2 Dec 92 19:45:51 GMT
  1016. Organization: Taniwha Systems Design
  1017.  
  1018. In article <1992Nov18.232653.13027@news.vanderbilt.edu>,
  1019. BLOOMDA@ctrvax.vanderbilt.edu (_creative_output_) wrote:
  1020. > 8-bit mode.  That's when I learned about incorporating the slot
  1021. > number of the video card into the base address:
  1022. >      ( ( baseAddr >> 24 ) | something ) << 20 | baseAddr, or somesuch
  1023.  
  1024. This ONLY works for a card that has it's frame buffer in slot space at the same
  1025. time that the CPU is running in 24-bit mode. Such a card would have it's
  1026. frame buffer at NuBus address Fs000000 ('s' is the slot number), and would be
  1027. accessed at the 24-bit address XXs00000 (X is a don't care), the MMU translates
  1028. from XXsABCDE to Fs0ABCDE. This ment one could use the magic address FssABCDE
  1029. to work in both spaces (provided the card degeneratively decoded bits 20-23
  1030. of incoming addresses, which reduces it's max address space to 1Mb).
  1031.  
  1032. These days cards can't afford to waste the address space (by ignoring bits
  1033. 20-23) - a 24-bit card needs at least 4Mb of address space for it's frame
  1034. store (plus ROM and registers) these cards need to be accessed with addresses
  1035. like FsABCDEF in 32-bit mode since in 24-bit mode only 1Mb of address space is
  1036. available. Modern cards with large frame stores and lots of off-screen GWorld
  1037. memory need >16Mb and have NuBus 'SuperSlot' addresses and need to be accessed
  1038. in 32-bit mode with addresses like sABCDEFG.
  1039.  
  1040. Framestore base addresses DON'T always start with their LSBs being all 0s.
  1041. Don't count on the base addresses of the framestore remaining the same if the
  1042. user changes the bit depth. (and of course don't write directly to the screen
  1043. unless you absolutely have to anyway).
  1044.  
  1045. In short use the slot manager to get base addresses etc from slot numbers.
  1046.  
  1047.         Paul
  1048.  
  1049. - -- 
  1050. Paul Campbell    UUCP: ..!mtxinu!taniwha!paul     AppleLink: CAMPBELL.P
  1051. Use up your Quayle jokes now while they're still good "Quayle for Pres. in '94"
  1052.      Q: Why is Marilyn Quayle like Marion Barry?
  1053.      A: They both suck a little dope.
  1054.  
  1055. ---------------------------
  1056.  
  1057. End of C.S.M.P. Digest
  1058. **********************
  1059.