7a6e2b190653284272552f438d45fb3bbc915dea
[oweals/cde.git] / cde / lib / DtHelp / Selection.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: Selection.c /main/22 1996/11/12 11:44:48 cde-hp $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  **
27  **   File:     Selection.c
28  **
29  **   Project:  Cde DtHelp
30  **
31  **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
32  **
33  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
34  **  (c) Copyright 1993, 1994 International Business Machines Corp.
35  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
36  **  (c) Copyright 1993, 1994 Novell, Inc.
37  **
38  **
39  ****************************************************************************
40  ************************************<+>*************************************/
41
42 /*
43  * system includes
44  */
45 #include <stdlib.h>
46 #include <string.h>
47
48 /*
49  * Canvas Engine includes
50  */
51 #include "CanvasP.h"
52 #include "CanvasSegP.h"
53
54 /*
55  * private includes
56  */
57 #include "CanvasI.h"
58 #include "CvStringI.h"
59 #include "LayoutUtilI.h"
60 #include "SelectionI.h"
61
62 #ifdef NLS16
63 #endif
64
65 /********    Private Function Declarations    ********/
66 static  void    AdjustSelection (
67                         _DtCanvasStruct *canvas,
68                         _DtCvSelectData  next);
69 static  int     GetSelectedText(
70                         _DtCanvasStruct *canvas,
71                         _DtCvSelectData  start,
72                         _DtCvSelectData  end,
73                         unsigned int         mask,
74                         _DtCvPointer    *ret_data);
75 /********    End Private Function Declarations    ********/
76
77 /********    Private Defines                 ********/
78 #define GROW_SIZE       5
79 /********    End Private Defines             ********/
80
81 /********    Macros                          ********/
82 #define Equal(a,b)              (a.y == b.y && a.x == b.x)
83 #define LessThan(a,b)           ((a.y < b.y) || (a.y == b.y && a.x < b.x))
84 #define GreaterThan(a,b)        ((a.y > b.y) || (a.y == b.y && a.x > b.x))
85 #define LessThanEq(a,b)         (Equal(a,b) || LessThan(a,b))
86 #define GreaterThanEq(a,b)      (Equal(a,b) || GreaterThan(a,b))
87
88 #define InRegion(top,bot,min,max)       ((min) <= (bot) && (top) <= (max))
89
90 /********    End Macros                      ********/
91
92 /********    Private Variable Declarations    ********/
93 static  const _DtCvSelectData   defaultSelect = { -1, -1, -1, -1};
94
95 /********    End Private Variable Declarations    ********/
96
97 /******************************************************************************
98  *                             Private Functions
99  ******************************************************************************/
100 /******************************************************************************
101  * Function: StartXOfLine
102  *
103  * Purpose: Determine the start of a line, takes into consideration
104  *          the traversal and link before values. The 'x' returned is
105  *          exactly where the text/graphic is to be placed on the canvas.
106  *****************************************************************************/
107 static _DtCvUnit
108 StartXOfLine(
109     _DtCanvasStruct     *canvas,
110     _DtCvDspLine         line)
111 {
112     _DtCvValue   lastLinkVisible = FALSE;
113     int          lnkInd = -1;
114     _DtCvUnit    xPos;
115     _DtCvSegmentI   *pSeg;
116
117     xPos = _DtCvGetStartXOfLine(&line, &pSeg);
118     return (_DtCvAdvanceXOfLine( canvas, pSeg, xPos,
119                         &lnkInd, &lastLinkVisible));
120
121 } /* End StartXOfLine */
122
123 /*****************************************************************************
124  * Function: SearchForClosestLine
125  *
126  * Purpose:  Initializes the 'new' structure with information indicating
127  *           what line is closest to the target_y.
128  *              next->y         Set to target_y if no line straddles it.
129  *                              Otherwise, it will be set to the minimum
130  *                              y of all lines straddling the target_y.
131  *              next->x         Set to target_x if no line straddles
132  *                              target_y or if target_x is before the
133  *                              first line straddling target_y.
134  *              next->line_idx  Set to -1 if no line straddles target_y.
135  *                              Otherwise, set to the first line that
136  *                              straddles target_x or is the minimum x
137  *                              that is greater than target_x of all the
138  *                              lines straddling target_x.
139  *              next->char_idx  Set to -1 if no straddles target_y.
140  *                              Otherwise, set to the character that
141  *                              resides at target_x if target_x is in
142  *                              the middle of the line.  Set to zero if
143  *                              target_x is before the line, and set to
144  *                              the line count if target_x is after the
145  *                              line.
146  *
147  *****************************************************************************/
148 static  void
149 SearchForClosestLine (
150     _DtCanvasStruct     *canvas,
151     _DtCvUnit            target_x,
152     _DtCvUnit            target_y,
153     _DtCvSelectData *next)
154 {
155     int          i;
156     int          maxI;
157     _DtCvUnit    lineY;
158     _DtCvUnit    endX;
159     _DtCvUnit    begX;
160     _DtCvUnit    maxX = -1;
161     _DtCvDspLine *lines = canvas->txt_lst;
162
163     *next = defaultSelect;
164     for (i = 0; i < canvas->txt_cnt; i++)
165       {
166         /*
167          * get the maximum y of the line
168          * if it straddles the target y, process it.
169          */
170         lineY = lines[i].baseline + lines[i].descent;
171         if (_DtCvStraddlesPt(target_y,lines[i].baseline-lines[i].ascent,lineY))
172           {
173             /*
174              * Is this the minimum y of all the maximum y values of the
175              * line straddling the target y?
176              */
177             if (next->y  == -1 || next->y > lineY)
178                 next->y = lineY;
179
180             /*
181              * Get the maximum X position of the line.
182              * If this is the maximum X of all the lines straddling
183              * the target y, remember it.
184              */
185             endX = canvas->txt_lst[i].max_x;
186             if (maxX < endX && endX < target_x)
187               {
188                 maxX = endX;
189                 maxI = i;
190               }
191
192             /*
193              * Does this line straddle the x?
194              */
195             begX = StartXOfLine(canvas, lines[i]);
196             if (_DtCvStraddlesPt(target_x, begX, endX))
197               {
198                 next->line_idx = i;
199                 next->char_idx = _DtCvGetCharIdx(canvas,lines[i],target_x);
200               }
201           }
202       }
203
204     /*
205      * remember what the target x was for this line.  If the target x is
206      * less than the start of the line, then the selection process will
207      * highlight the space before the line.  If its in the middle, it
208      * will just highlight starting at the character.  If it's after the
209      * end, the rest will be cut off at the end of the line.
210      */
211     next->x = target_x;
212
213     /*
214      * If we found a line straddling the target y, but it does not
215      * straddle the target_x, check max x for the correct info.
216      */
217     if (next->line_idx == -1 && maxX > -1)
218       {
219         next->line_idx = maxI;
220         next->char_idx = lines[maxI].length;
221       }
222
223     /*
224      * didn't find a line straddling the target_y, set y.
225      */
226     if (next->y == -1)
227         next->y = target_y;
228 }
229
230 /*****************************************************************************
231  * Function: MarkLinesOutsideBoundary
232  *
233  *****************************************************************************/
234 static  void
235 MarkLinesOutsideBoundary (
236     _DtCanvasStruct     *canvas,
237     _DtCvUnit            top_y,
238     _DtCvUnit            top_x,
239     _DtCvUnit            bot_y,
240     _DtCvUnit            bot_x)
241 {
242     int       i;
243     _DtCvUnit      maxY;
244     _DtCvUnit      minY;
245     _DtCvDspLine  *lines = canvas->txt_lst;
246
247     for (i = 0; i < canvas->txt_cnt; i++)
248       {
249         maxY = lines[i].baseline + lines[i].descent;
250         minY = lines[i].baseline - lines[i].ascent;
251
252         /*
253          * is this line outside the boundary?
254          * If so, mark it so it's not processed.
255          */
256         if (maxY < top_y || minY > bot_y )
257             _DtCvSetProcessed(lines[i]);
258
259         else
260           {
261             /*
262              * does it straddle the top?
263              */
264             if (_DtCvStraddlesPt(top_y, minY, maxY))
265               {
266                 /*
267                  * Does it begin before the selection?
268                  * If so, mark it so it's not processed.
269                  */
270                 if (canvas->txt_lst[i].max_x <= top_x)
271                     _DtCvSetProcessed(lines[i]);
272               }
273         
274             /*
275              * does it straddle the bottom?
276              */
277             if (_DtCvStraddlesPt(bot_y, minY, maxY))
278               {
279                 /*
280                  * Does it start after the selection?
281                  * If so, mark it so it's not processed.
282                  */
283                 if (StartXOfLine(canvas, lines[i]) >= bot_x)
284                     _DtCvSetProcessed(lines[i]);
285               }
286           }
287       }
288 }
289
290 /*****************************************************************************
291  * Function: AdjustSelection
292  *
293  *****************************************************************************/
294 static  void
295 AdjustSelection (
296     _DtCanvasStruct     *canvas,
297     _DtCvSelectData next)
298 {
299     _DtCvSelectData  start = canvas->select_start;
300     _DtCvSelectData  end   = canvas->select_end;
301
302     if (!(Equal(next, end)))
303       {
304         if (next.line_idx != -1 && next.line_idx == canvas->select_end.line_idx
305                 &&
306             next.char_idx != -1 && next.char_idx == canvas->select_end.char_idx)
307             return;
308
309         if (GreaterThan(next, end))
310           {
311             if (LessThanEq(start, end))
312                 _DtCvDrawAreaWithFlags (canvas, end, next,
313                                                 0, _DtCvSELECTED_FLAG,
314                                                 _DtCvBAD_TYPE, NULL);
315
316             else if (GreaterThanEq(start, next))
317                 _DtCvDrawAreaWithFlags (canvas, end, next,
318                                                 _DtCvSELECTED_FLAG, 0,
319                                                 _DtCvBAD_TYPE, NULL);
320
321             else /* end < start < next */
322               {
323                 _DtCvDrawAreaWithFlags (canvas, end  , start,
324                                                 _DtCvSELECTED_FLAG, 0,
325                                                 _DtCvBAD_TYPE, NULL);
326                 _DtCvDrawAreaWithFlags (canvas, start, next ,
327                                                 0, _DtCvSELECTED_FLAG,
328                                                 _DtCvBAD_TYPE, NULL);
329               }
330           }
331         else /* if (next < end) */
332           {
333             if (LessThanEq(start, next))
334                 _DtCvDrawAreaWithFlags (canvas, next, end,
335                                                 _DtCvSELECTED_FLAG, 0,
336                                                 _DtCvBAD_TYPE, NULL);
337
338             else if (GreaterThanEq(start, end))
339                 _DtCvDrawAreaWithFlags (canvas, next, end,
340                                                 0, _DtCvSELECTED_FLAG,
341                                                 _DtCvBAD_TYPE, NULL);
342
343             else /* next < start < end */
344               {
345                 _DtCvDrawAreaWithFlags (canvas, start, end  ,
346                                                 _DtCvSELECTED_FLAG, 0,
347                                                 _DtCvBAD_TYPE, NULL);
348                 _DtCvDrawAreaWithFlags (canvas, next , start,
349                                                 0, _DtCvSELECTED_FLAG,
350                                                 _DtCvBAD_TYPE, NULL);
351               }
352           }
353       }
354
355     canvas->select_end = next;
356 }
357
358 /*****************************************************************************
359  * Function: SkipOtherLines
360  *
361  *****************************************************************************/
362 static void
363 SkipOtherLines(
364     _DtCvDspLine *lines,
365     int          max_cnt,
366     int          idx,
367     _DtCvUnit    target_y,
368     int         *ret_idx)
369 {
370     while (idx < max_cnt && _DtCvIsNotProcessed(lines[idx]) &&
371                         lines[idx].baseline - lines[idx].ascent > target_y)
372         idx++;
373
374     *ret_idx = idx;
375 }
376
377 /*****************************************************************************
378  * Function: CheckAndSwitchPoints
379  *
380  *****************************************************************************/
381 static int
382 CheckAndSwitchPoints(
383     _DtCvSelectData *pt1,
384     _DtCvSelectData *pt2)
385 {
386     _DtCvSelectData temp;
387
388     if (pt1->y > pt2->y || (pt1->y == pt2->y && pt1->x >  pt2->x))
389       {
390         temp = *pt2;
391         *pt2 = *pt1;
392         *pt1 = temp;
393       }
394 }
395
396 /*****************************************************************************
397  * Function: AddSegmentToData
398  *
399  *****************************************************************************/
400 static _DtCvUnit
401 AddSegmentToData(
402     _DtCanvasStruct     *canvas,
403     unsigned int         mask,
404     _DtCvUnit            start_x,
405     int                  line_idx,
406     int                  char_idx,
407     int                  copy_cnt,
408     _DtCvFlags           end_flag,
409     _DtCvUnit           *ret_y,
410     _DtCvPointer        *ret_data)
411 {
412     _DtCvDspLine line   = canvas->txt_lst[line_idx];
413     int          result = _DtCvSTATUS_OK;
414     int          count  = line.length;
415     int          start  = line.byte_index;
416     int          lnkInd = -1;
417     int          cnt;
418     int          len;
419     _DtCvUnit    segWidth;
420     _DtCvUnit    xPos  = line.text_x;
421     void        *pChar;
422     _DtCvSegmentI       *pSeg  = line.seg_ptr;
423     _DtCvFlags   flag  = 0;
424     _DtCvValue   done  = False;
425     _DtCvValue   lastLinkVisible = FALSE;
426     _DtCvStringInfo strInfo;
427
428     xPos = _DtCvGetStartXOfLine(&line, &pSeg);
429
430     while (done == False && char_idx)
431       {
432         /*
433          * advance past the link and traversal info
434          */
435         xPos = _DtCvAdvanceXOfLine(canvas, pSeg, xPos,
436                                 &lnkInd, &lastLinkVisible);
437
438         /*
439          * advance the pointer by the width
440          */
441         _DtCvGetWidthOfSegment(canvas, pSeg, start, count,
442                                                         &cnt, &segWidth, NULL);
443         if (cnt < char_idx)
444           {
445             xPos     += segWidth;
446             pSeg      = pSeg->next_disp;
447             count    -= cnt;
448             char_idx -= cnt;
449             start     = 0;
450           }
451         else 
452           {
453             _DtCvGetWidthOfSegment(canvas, pSeg, start, char_idx,
454                                                         &cnt, &segWidth, NULL);
455             xPos  += segWidth;
456             start += cnt;
457             count -= cnt;
458             done = True;
459           }
460       }
461
462     if (start_x > xPos)
463         start_x = xPos;
464
465     while (_DtCvSTATUS_OK == result && pSeg != NULL && copy_cnt > 0)
466       {
467         /*
468          * advance past the link and traversal info
469          */
470         xPos = _DtCvAdvanceXOfLine(canvas, pSeg, xPos,
471                                 &lnkInd, &lastLinkVisible);
472
473         switch (_DtCvPrimaryTypeOfSeg(pSeg))
474           {
475             case _DtCvSTRING:
476
477                 pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
478                                         _DtCvIsSegWideChar(pSeg), start);
479                 len   = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));
480
481                 if (len > copy_cnt)
482                     len = copy_cnt;
483
484                 segWidth = _DtCvGetStringWidth(canvas, pSeg, pChar, len);
485                 if (copy_cnt == len)
486                     flag = end_flag;
487
488                 strInfo.string   = pChar;
489                 strInfo.byte_len = len;
490                 strInfo.wc       = _DtCvIsSegWideChar(pSeg);
491                 strInfo.font_ptr = _DtCvFontOfStringSeg(pSeg);
492
493                 if (canvas->virt_functions.build_selection != NULL)
494                     result = (*(canvas->virt_functions.build_selection))(
495                                 canvas->client_data,
496                                 _DtCvSTRING_TYPE,
497                                 mask,
498                                 ret_data,
499                                 xPos - start_x,
500                                 segWidth,
501                                 flag,
502                                 (_DtCvPointer) &strInfo);
503
504                 if (_DtCvSTATUS_OK == result)
505                   {
506                     if (line.baseline + line.descent > *ret_y)
507                     *ret_y  = line.baseline + line.descent;
508                     start_x = xPos + segWidth;
509                   }
510                 else if (_DtCvSTATUS_NONE == result)
511                     result = _DtCvSTATUS_OK;
512
513                 xPos     += segWidth;
514                 copy_cnt -= len;
515                 start     = 0;
516                 break;
517
518             case _DtCvREGION:
519                 if (copy_cnt == 1)
520                     flag = end_flag;
521
522                 if (canvas->virt_functions.build_selection != NULL)
523                     result = (*(canvas->virt_functions.build_selection))(
524                                 canvas->client_data,
525                                 _DtCvREGION_TYPE,
526                                 mask,
527                                 ret_data,
528                                 xPos - start_x,
529                                 _DtCvWidthOfRegionSeg(pSeg),
530                                 flag,
531                                 _DtCvInfoOfRegionSeg(pSeg));
532
533                 if (_DtCvSTATUS_OK == result)
534                   {
535                     if (line.baseline + line.descent > *ret_y)
536                     *ret_y = line.baseline + line.descent;
537                     start_x = xPos + segWidth;
538                   }
539                 else if (_DtCvSTATUS_NONE == result)
540                     result = _DtCvSTATUS_OK;
541
542                 copy_cnt--;
543                 xPos    += _DtCvWidthOfRegionSeg(pSeg);
544                 break;
545           }
546         pSeg = pSeg->next_disp;
547       }
548
549     if (result != _DtCvSTATUS_OK)
550         return -1;
551
552     return start_x;
553 }
554
555 /*****************************************************************************
556  * Function: BuildLine
557  *
558  *****************************************************************************/
559 static int
560 BuildLine(
561     _DtCanvasStruct     *canvas,
562     unsigned int         mask,
563     _DtCvUnit            prev_y,
564     _DtCvUnit            target_x,
565     int                  line_idx,
566     int                  char_idx,
567     int                  copy_cnt,
568     _DtCvFlags           end_flag,
569     _DtCvUnit           *ret_x,
570     _DtCvUnit           *ret_y,
571     _DtCvPointer        *ret_data)
572 {
573     _DtCvUnit            topY;
574     _DtCvDspLine        *lines = canvas->txt_lst;
575     _DtCvStringInfo      strInfo = { NULL, 0, 1, NULL };
576
577     topY = lines[line_idx].baseline - lines[line_idx].ascent;
578     if (topY > prev_y && canvas->virt_functions.build_selection != NULL)
579       {
580         int   newLines;
581         _DtCvUnit  lineSize = canvas->metrics.line_height / 2;
582         if (lineSize < 1)
583             lineSize = 1;
584
585         newLines = (topY - prev_y) / lineSize;
586         while (newLines > 0)
587           {
588             newLines--;
589             if ((*(canvas->virt_functions.build_selection))(
590                                 canvas->client_data, _DtCvSTRING_TYPE,
591                                 mask, ret_data, 0, 0, _DtCvEND_OF_LINE,
592                                 (_DtCvPointer) &strInfo) != _DtCvSTATUS_OK)
593                 return -1;
594           }
595       }
596
597     *ret_y = 0;
598     *ret_x = AddSegmentToData (canvas, mask, target_x, line_idx, char_idx,
599                                         copy_cnt, end_flag, ret_y, ret_data);
600     _DtCvSetProcessed(lines[line_idx]);
601
602     if (*ret_x == -1)
603         return -1;
604     return 0;
605 }
606
607 /*****************************************************************************
608  * Function: FindMinX
609  *
610  *****************************************************************************/
611 static  int
612 FindMinX (
613     _DtCvDspLine *lines,
614     int          txt_cnt,
615     _DtCvUnit    target_y,
616     int         *ret_line)
617 {
618     int   i;
619     int   cnt    = 0;
620     _DtCvUnit  curMin = -1;
621     _DtCvUnit  curX;
622
623     for (i = 0; i < txt_cnt; i++)
624       {
625         if (_DtCvIsNotProcessed(lines[i]))
626           {
627             if (lines[i].baseline - lines[i].ascent < target_y &&
628                         target_y <= lines[i].baseline + lines[i].descent)
629               {
630                 cnt++;
631                 curX = lines[i].text_x;
632
633                 if (curMin == -1 || curMin > curX)
634                   {
635                     curMin    = curX;
636                     *ret_line = i;
637                   }
638               }
639           }
640       }
641     return cnt;
642 }
643
644 /*****************************************************************************
645  * Function: FindNextMinY
646  *
647  *****************************************************************************/
648 static _DtCvValue
649 FindNextMinY(
650     _DtCvDspLine *lines,
651     int          max_cnt,
652     _DtCvUnit    target_y,
653     _DtCvUnit   *ret_y)
654 {
655     int         i     = 0;
656     _DtCvUnit   maxY;
657     _DtCvValue  found = False;
658
659     while (i < max_cnt)
660      {
661         if (_DtCvIsNotProcessed(lines[i]))
662           {
663             maxY = lines[i].baseline + lines[i].descent;
664             if (target_y == -1 || maxY < target_y)
665               {
666                 found    = True;
667                 target_y = maxY;
668               }
669             SkipOtherLines (lines, max_cnt, i+1, target_y, &i);
670           }
671         else
672             i++;
673      }
674
675     *ret_y = target_y;
676     return found;
677 }
678
679 /*****************************************************************************
680  * Function: GetSelectedText
681  *
682  *****************************************************************************/
683 static int
684 GetSelectedText(
685     _DtCanvasStruct     *canvas,
686     _DtCvSelectData next,
687     _DtCvSelectData end,
688     unsigned int         mask,
689     _DtCvPointer        *ret_data)
690 {
691     _DtCvUnit   maxY;
692     _DtCvUnit   botY;
693     int    i;
694     int    lineCnt  = 0;
695     int    junk;
696     int    result   = 0;
697     int    cpyCnt   = 0;
698     int    txtCnt   = canvas->txt_cnt;
699     _DtCvFlags endFlag;
700     _DtCvValue  processing = True;
701     _DtCvDspLine   *lines = canvas->txt_lst;
702
703     for (i = 0; i < txtCnt; i++)
704         _DtCvClearProcessed(lines[i]);
705
706     MarkLinesOutsideBoundary(canvas, next.y, next.x, end.y, end.x);
707
708     maxY = next.y;
709     if (next.line_idx == -1)
710       {
711         /*
712          * find the first selected line
713          */
714         if (FindNextMinY(lines, txtCnt, -1, &next.y) == False)
715             return 0;
716
717         next.x  = 0;
718         lineCnt = FindMinX(lines, txtCnt, next.y, &next.line_idx);
719         next.char_idx = 0;
720       }
721     else
722         lineCnt = FindMinX(lines, txtCnt, next.y, &junk);
723
724     while (processing == True && result == 0)
725       {
726         /*
727          * process the next line of text.
728          */
729         do
730           {
731             endFlag = 0;
732             cpyCnt  = lines[next.line_idx].length - next.char_idx;
733             if (next.line_idx == end.line_idx)
734                 cpyCnt = cpyCnt - lines[next.line_idx].length + end.char_idx;
735             else if (lineCnt == 1)
736                 endFlag = _DtCvEND_OF_LINE;
737
738             result  = BuildLine(canvas, mask, maxY, next.x,
739                                         next.line_idx, next.char_idx,
740                                         cpyCnt, endFlag,
741                                         &next.x, &botY, ret_data);
742
743             if (botY > maxY)
744                 maxY = botY;
745
746             next.char_idx = 0;
747             lineCnt       = FindMinX(lines, txtCnt, next.y, &next.line_idx);
748
749           } while (result == 0 && lineCnt > 0);
750
751         if (result == 0)
752           {
753             next.x = 0;
754             processing = FindNextMinY(lines, txtCnt, -1, &next.y);
755             if (processing == True)
756                 lineCnt = FindMinX(lines, txtCnt, next.y, &next.line_idx);
757           }
758       }
759
760     return result;
761
762 } /* End GetSelectedText */
763
764 /*****************************************************************************
765  * Function:    GetSegsInArea()
766  *
767  * Purpose:     Retrieve the segments making up the selection.
768  *
769  *****************************************************************************/
770 static _DtCvStatus
771 GetSegsInArea (
772     _DtCanvasStruct     *canvas,
773     _DtCvSelectData       *beg,
774     _DtCvSelectData       *end,
775     _DtCvSegPts         ***ret_segs,
776     _DtCvUnit             *ret_y1,
777     _DtCvUnit             *ret_y2)
778 {
779     int                  cnt;
780     int                  count;
781     int                  start;
782     int                  length;
783     int                  lineCnt;
784     int                  result = 0;
785     _DtCvValue           processing = True;
786     _DtCvUnit            minY;
787     _DtCvUnit            maxY;
788     _DtCvUnit            botY;
789     _DtCvSelectData      next;
790     _DtCvSegPts         *newPt;
791     _DtCvSegmentI       *pSeg;
792     _DtCvDspLine        *lines = canvas->txt_lst;
793
794     *ret_segs = NULL;
795
796     if (beg->x == -1)
797         return _DtCvSTATUS_NONE;
798
799     /*
800      * make sure the selection points are in the correct order.
801      */
802     CheckAndSwitchPoints(beg, end);
803
804     /*
805      * clear the processed bit
806      */
807     for (cnt = 0; cnt < canvas->txt_cnt; cnt++)
808         _DtCvClearProcessed(lines[cnt]);
809
810     /*
811      * initialize the working structure
812      * mark all the lines outside the selection regiion as invalid
813      */
814     next = *beg;
815     MarkLinesOutsideBoundary(canvas, next.y, next.x, end->y, end->x);
816
817     /*
818      * start the minimum and maximum Y at this location.
819      */
820     minY = next.y;
821     maxY = end->y;
822
823     /*
824      * is there a line at this location?
825      */
826     if (next.line_idx == -1)
827       {
828         /*
829          * find the first selected line within the region.
830          */
831         if (FindNextMinY(lines, canvas->txt_cnt, -1, &next.y) == False)
832             processing = False;         /* empty of any text */
833         else
834           {
835             /*
836              * now find the first line that is on this 'line' and
837              * the number of lines.
838              */
839             next.x  = 0;
840             lineCnt = FindMinX(lines, canvas->txt_cnt, next.y, &next.line_idx);
841             next.char_idx = 0;
842           }
843       }
844     else /* find the number of lines on this 'line' */
845         lineCnt = FindMinX(lines, canvas->txt_cnt, next.y, &cnt);
846
847     /*
848      * loop will there are segments to process
849      */
850     while (processing == True && result == 0)
851       {
852         /*
853          * process the next line of text.
854          */
855         while (result == 0 && lineCnt > 0)
856           {
857             /*
858              * for each segment in this line (that is selected)
859              * create a segment point for it.
860              */
861             length = lines[next.line_idx].length;
862             start  = lines[next.line_idx].byte_index;
863
864             /*
865              * if this is the last line, shorten the length
866              * by the ending index.
867              */
868             if (next.line_idx == end->line_idx)
869                 length = end->char_idx;
870
871             /*
872              * move through the line's segments until we
873              * hit the segment starting the selection
874              */
875             pSeg = lines[next.line_idx].seg_ptr;
876             count = next.char_idx;
877             while (NULL != pSeg && 0 < count)
878               {
879                 /*
880                  * get the byte count of this segment
881                  */
882                 _DtCvGetWidthOfSegment(canvas, pSeg, start, length,
883                                         &cnt, NULL, NULL);
884
885                 /*
886                  * is the byte count of this segment larger than
887                  * the starting index of the selection? If not,
888                  * the selection is after this segment.
889                  */
890                 if (count >= cnt)
891                   {
892                     start          = 0;
893                     length        -= cnt;
894                     pSeg           = pSeg->next_disp;
895                   }
896                 else
897                   {
898                     length        -= count;
899                     start          = start + count;
900                   }
901                 count -= cnt;
902               }
903
904             while (0 == result && NULL != pSeg && 0 < length)
905               {
906                 /*
907                  * start with error condition. If the malloc works
908                  * the error result gets reset to valid.
909                  */
910                 result = -1;
911                 newPt = (_DtCvSegPts *) malloc (sizeof(_DtCvSegPts));
912                 if (NULL != newPt)
913                   {
914                     /*
915                      * indicate everything is okay.
916                      */
917                     result = 0;
918
919                     /*
920                      * get the width of this segment
921                      */
922                     _DtCvGetWidthOfSegment(canvas, pSeg, start, length,
923                                                         &cnt, NULL, NULL);
924
925                     /*
926                      * now set the segment point information and add it to the
927                      * array of segment points.
928                      */
929                     newPt->offset  = start;
930                     newPt->len     = cnt;
931                     newPt->segment = pSeg;
932
933                     *ret_segs = (_DtCvSegPts **) _DtCvAddPtrToArray(
934                                                         (void **) *ret_segs,
935                                                         (void  *)  newPt);
936                     if (NULL == *ret_segs)
937                         result = -1;
938
939                     pSeg    = pSeg->next_disp;
940                     length -= cnt;
941                     start   = 0;
942                   }
943               }
944
945             /*
946              * does this line extend below the selection y?
947              * if so, report it as the maximum y.
948              */
949             botY = lines[next.line_idx].baseline + lines[next.line_idx].descent;
950             if (botY > maxY)
951                 maxY = botY;
952
953             /*
954              * indicate this line has been processed.
955              */
956             _DtCvSetProcessed(lines[next.line_idx]);
957
958             /*
959              * get the next line
960              */
961             next.char_idx = 0;
962             lineCnt       = FindMinX(lines, canvas->txt_cnt, next.y,
963                                                         &next.line_idx);
964           }
965
966         if (result == 0)
967           {
968             next.x = 0;
969             processing = FindNextMinY(lines,canvas->txt_cnt, -1, &next.y);
970             if (processing == True)
971                 lineCnt = FindMinX(lines,canvas->txt_cnt,next.y,&next.line_idx);
972           }
973       }
974
975     /*
976      * if no errors, add a null to the array
977      */
978     if (0 != result)
979       {
980         *ret_segs = (_DtCvSegPts **) _DtCvAddPtrToArray((void **) *ret_segs,
981                                                         (void  *) NULL);
982         if (NULL == *ret_segs)
983             result = -1; 
984       }
985
986     /*
987      * if errors, free the segment points and return a bad status.
988      */
989     if (0 != result)
990       {
991         if (NULL != *ret_segs)
992           {
993             for (lineCnt = 0; NULL != (*ret_segs)[lineCnt]; lineCnt++)
994                 free((*ret_segs)[lineCnt]);
995             free(*ret_segs);
996           }
997         return _DtCvSTATUS_BAD;
998       }
999
1000     if (NULL != ret_y1)
1001         *ret_y1 = minY;
1002     
1003     if (NULL != ret_y2)
1004         *ret_y2 = minY;
1005
1006     return _DtCvSTATUS_OK;
1007 }
1008
1009 /******************************************************************************
1010  *                             Semi-Public Functions
1011  ******************************************************************************/
1012 /*****************************************************************************
1013  * Function: _DtCvDrawAreaWithFlags
1014  *
1015  *****************************************************************************/
1016 void
1017 _DtCvDrawAreaWithFlags (
1018     _DtCanvasStruct     *canvas,
1019     _DtCvSelectData      start,
1020     _DtCvSelectData      end,
1021     _DtCvFlags           old_flags,
1022     _DtCvFlags           new_flags,
1023     _DtCvElemType        trav_type,
1024     _DtCvPointer         trav_data)
1025 {
1026     int    i;
1027     int    len;
1028     int    count;
1029     int    startChar;
1030     int    lnkInd;
1031     _DtCvUnit   dstX;
1032     _DtCvUnit   topY;
1033     _DtCvUnit   botY;
1034     _DtCvUnit   superWidth;
1035     _DtCvUnit   subWidth;
1036     _DtCvUnit   superY;
1037     _DtCvUnit   subY;
1038     _DtCvUnit   scriptX;
1039     _DtCvUnit   segWidth;
1040     _DtCvSegmentI  *pSeg;
1041     _DtCvValue   lstLinkVis;
1042     _DtCvValue   lstWasSuper;
1043     _DtCvValue   lstWasSub;
1044     _DtCvValue   trimmed;
1045     _DtCvFlags   flagMask = old_flags | new_flags;
1046     _DtCvFlags   endFlag  = flagMask & _DtCvTRAVERSAL_END;
1047
1048     _DtCvDspLine   *lines = canvas->txt_lst;
1049
1050     /*
1051      * now use the flagMask to determine what else to look for.
1052      * I.e. if flagMask has _DtCvMARK_FLAG set, then it becomes
1053      * set to _DtCvSELECTED_FLAG and visa versa.
1054      */
1055     flagMask ^= (_DtCvSELECTED_FLAG | _DtCvMARK_FLAG);
1056
1057     /*
1058      * strip the end flag from the other flags
1059      */
1060     new_flags &= ~(_DtCvTRAVERSAL_END);
1061     old_flags &= ~(_DtCvTRAVERSAL_END);
1062
1063     if (Equal(start, end))
1064         return;
1065
1066     for (i = 0; i < canvas->txt_cnt; i++)
1067       {
1068         topY  = lines[i].baseline - lines[i].ascent;
1069         botY  = lines[i].baseline + lines[i].descent;
1070
1071         if (InRegion(topY, botY, start.y, end.y))
1072           {
1073             /*
1074              * get the start of the text.
1075              */
1076             lstLinkVis  = False;
1077             lstWasSuper = False;
1078             lstWasSub   = False;
1079             lnkInd      = -1;
1080             dstX        = _DtCvGetStartXOfLine(&(lines[i]), &pSeg);
1081             startChar   = lines[i].byte_index;
1082             count       = lines[i].length;
1083
1084             while (pSeg != NULL && _DtCvIsSegNoop(pSeg))
1085               {
1086                 startChar  = 0;
1087                 pSeg       = pSeg->next_disp;
1088               }
1089
1090             /*
1091              * advance the starting point
1092              */
1093             dstX = _DtCvAdvanceXOfLine(canvas, pSeg, dstX,
1094                                         &lnkInd, &lstLinkVis);
1095             /*
1096              * take into account super/sub scripting
1097              */
1098             dstX = _DtCvAdjustForSuperSub(canvas, pSeg, dstX, &scriptX,
1099                                         &superWidth, &superY, &subWidth, &subY,
1100                                         &lstWasSuper, &lstWasSub);
1101
1102             /*
1103              * set this flag so that the first pass of 'while (cnt > 0)'
1104              * doesn't do it again.
1105              */
1106             trimmed = True;
1107
1108             if (_DtCvStraddlesPt(start.y, topY, botY))
1109               {
1110                 /*
1111                  * skip this item?
1112                  * I.E. is this line before the start or after the end?
1113                  */
1114                 if (canvas->txt_lst[i].max_x < start.x ||
1115                                         end.y == start.y && end.x <= dstX )
1116                     continue;
1117
1118                 /*
1119                  * does this line start the mark/selection?
1120                  */
1121                 if (i == start.line_idx && start.x >= dstX)
1122                   {
1123                     int cnt = start.char_idx;
1124
1125                     while (cnt > 0)
1126                       {
1127                         if (trimmed == False)
1128                           {
1129                             /*
1130                              * advance the starting point
1131                              */
1132                             dstX = _DtCvAdvanceXOfLine(canvas, pSeg, dstX,
1133                                         &lnkInd, &lstLinkVis);
1134                             /*
1135                              * take into account super/sub scripting
1136                              */
1137                             dstX = _DtCvAdjustForSuperSub(canvas,
1138                                         pSeg, dstX, &scriptX,
1139                                         &superWidth, &superY, &subWidth, &subY,
1140                                         &lstWasSuper, &lstWasSub);
1141                           }
1142
1143                         /*
1144                          * take into account the length of the segment
1145                          */
1146                         _DtCvGetWidthOfSegment(canvas, pSeg,
1147                                         startChar , cnt,
1148                                         &len, &segWidth, &trimmed);
1149
1150                         dstX       += segWidth;
1151                         startChar  += len;
1152                         if (trimmed == False)
1153                           {
1154                             startChar  = 0;
1155                             pSeg       = pSeg->next_disp;
1156                           }
1157
1158                         trimmed = False;
1159
1160                         cnt -= len;
1161
1162                       }
1163
1164                     count -= start.char_idx;
1165                   }
1166
1167                 /*
1168                  * otherwise this line is after the line that starts
1169                  * the mark/selection. Stick with its start x.
1170                  */
1171               }
1172
1173             /*
1174              * does this straddle the end point?
1175              */
1176             if (_DtCvStraddlesPt(end.y, topY, botY))
1177               {
1178                 /*
1179                  * does this start after the end of the mark/selection?
1180                  * if so, skip.
1181                  */
1182                 if (end.x <= dstX)
1183                     continue;
1184
1185                 /*
1186                  * Does this segment end after the end of the mark/selection?
1187                  * If so, trim how much gets highlighted.
1188                  */
1189                 if (canvas->txt_lst[i].max_x > end.x)
1190                     count -= (lines[i].length - end.char_idx);
1191               }
1192
1193             /*
1194              * while there is something to draw (un)mark/selected.
1195              */
1196             old_flags = old_flags | _DtCvMARK_BEGIN;
1197             new_flags = new_flags | _DtCvMARK_BEGIN;
1198             while (count > 0)
1199               {
1200                 /*
1201                  * the original count to render
1202                  */
1203                 len = count;
1204
1205                 /*
1206                  * check for other marks and selection.
1207                  */
1208                 _DtCvCheckLineMarks(canvas, i, startChar - lines[i].byte_index,
1209                                                 count, dstX, flagMask,
1210                                                 &len, &old_flags, &new_flags);
1211
1212                 /*
1213                  * if this is the last segment(s) of the (un)mark/selection
1214                  * set the end flags.
1215                  */
1216                 if (len == count)
1217                   {
1218                     new_flags |= (endFlag | _DtCvLINK_END | _DtCvMARK_END);
1219                     old_flags |= (endFlag | _DtCvLINK_END | _DtCvMARK_END);
1220                   }
1221
1222                 /*
1223                  * draw the segments that are marked/unmarked.
1224                  */
1225                 dstX = _DtCvDrawSegments(canvas, lines[i],
1226                                         pSeg, startChar , len, &lnkInd,
1227                                         dstX, dstX, &scriptX,
1228                                         &superWidth, &superY, &subWidth, &subY,
1229                                         &lstWasSub, &lstWasSuper,
1230                                         &lstLinkVis, old_flags, new_flags,
1231                                         trav_type, trav_data);
1232                 /*
1233                  * modify the count by the length processed
1234                  */
1235                 count  -= len;
1236
1237                 /*
1238                  * did this do the entire length? If not, set the
1239                  * indexes ahead and do again.
1240                  */
1241                 if (count > 0)
1242                     _DtCvSkipLineChars(canvas, pSeg, startChar , count + len,
1243                                                 len, &startChar , &pSeg);
1244
1245                 /*
1246                  * strip the any begin flags.
1247                  */
1248                 _DtCvRemoveBeginFlags(old_flags);
1249                 _DtCvRemoveBeginFlags(new_flags);
1250               }
1251           }
1252       }
1253 }
1254
1255 /*****************************************************************************
1256  * Function:    _DtCanvasGetSelectionPoints()
1257  *
1258  * Purpose:     Retrieve the segments making up the selection.
1259  *
1260  *****************************************************************************/
1261 _DtCvStatus
1262 _DtCvGetMarkSegs (
1263     _DtCanvasStruct     *canvas,
1264     _DtCvPointInfo      ***ret_info)
1265 {
1266     int                   i;
1267
1268     *ret_info = NULL;
1269
1270     for (i = 0; i < canvas->mark_cnt; i++)
1271       {
1272         _DtCvPointInfo  *nxtInfo;
1273
1274         /*
1275          * allocate mark information structure
1276          */
1277         nxtInfo = (_DtCvPointInfo *) malloc (sizeof(_DtCvPointInfo));
1278         if (NULL == nxtInfo)
1279             return _DtCvSTATUS_BAD;
1280
1281         nxtInfo->client_data = canvas->marks[i].client_data;
1282
1283         if (_DtCvSTATUS_BAD == GetSegsInArea(canvas, &(canvas->marks[i].beg),
1284                                                 &(canvas->marks[i].end),
1285                                                 &(nxtInfo->segs),
1286                                                 NULL, NULL))
1287             return _DtCvSTATUS_BAD;
1288
1289         *ret_info = (_DtCvPointInfo **) _DtCvAddPtrToArray((void **) *ret_info,
1290                                                         (void *) nxtInfo);
1291         if (NULL == *ret_info)
1292             return _DtCvSTATUS_BAD;
1293           }
1294
1295     return _DtCvSTATUS_OK;
1296 }
1297
1298 /******************************************************************************
1299  *                             Public Functions
1300  ******************************************************************************/
1301 /*****************************************************************************
1302  * Function:    _DtCanvasGetSelection()
1303  *
1304  * Purpose:     Indicate the end point for a selection.
1305  *
1306  *****************************************************************************/
1307 _DtCvStatus
1308 _DtCanvasGetSelection (
1309     _DtCvHandle          canvas_handle,
1310     unsigned int         mask,
1311     _DtCvPointer        *ret_select)
1312 {
1313     _DtCanvasStruct     *canvas = (_DtCanvasStruct *) canvas_handle;
1314
1315     *ret_select = NULL;
1316     return(GetSelectedText(canvas, canvas->select_start, canvas->select_end,
1317                                                         mask, ret_select));
1318 }
1319
1320 /*****************************************************************************
1321  * Function:    _DtCanvasProcessSelection()
1322  *
1323  * Purpose:     Indicate an new point for a selection.
1324  *
1325  *****************************************************************************/
1326 void
1327 _DtCanvasProcessSelection (
1328     _DtCvHandle         canvas_handle,
1329     _DtCvUnit           x,
1330     _DtCvUnit           y,
1331     _DtCvSelectMode     mode)
1332 {
1333     _DtCanvasStruct     *canvas = (_DtCanvasStruct *) canvas_handle;
1334     _DtCvSelectData      temp;
1335
1336     switch (mode)
1337           {
1338         case _DtCvSELECTION_CLEAR:
1339                 CheckAndSwitchPoints(&(canvas->select_start),
1340                                                         &(canvas->select_end));
1341
1342         case _DtCvSELECTION_START:
1343                 _DtCvDrawAreaWithFlags(canvas, canvas->select_start,
1344                                 canvas->select_end,
1345                                 _DtCvSELECTED_FLAG, 0,
1346                                 _DtCvBAD_TYPE, NULL);
1347
1348                 canvas->select_start = defaultSelect;
1349                 if (mode == _DtCvSELECTION_START)
1350                     SearchForClosestLine(canvas, x, y, &(canvas->select_start));
1351
1352                 canvas->select_end   = canvas->select_start;
1353                 break;
1354
1355         case _DtCvSELECTION_END:
1356         case _DtCvSELECTION_UPDATE:
1357                 SearchForClosestLine(canvas, x, y, &temp);
1358
1359                 AdjustSelection (canvas, temp);
1360                 if (mode == _DtCvSELECTION_END)
1361                     CheckAndSwitchPoints(&(canvas->select_start),
1362                                                         &(canvas->select_end));
1363                 break;
1364       }
1365 }
1366
1367 /*****************************************************************************
1368  * Function:    _DtCanvasGetSelectionPoints()
1369  *
1370  * Purpose:     Retrieve the segments making up the selection.
1371  *
1372  *****************************************************************************/
1373 _DtCvStatus
1374 _DtCanvasGetSelectionPoints (
1375     _DtCvHandle            canvas_handle,
1376     _DtCvSegPts         ***ret_segs,
1377     _DtCvUnit             *ret_y1,
1378     _DtCvUnit             *ret_y2)
1379 {
1380     _DtCanvasStruct     *canvas = (_DtCanvasStruct *) canvas_handle;
1381
1382     return (GetSegsInArea(canvas, &(canvas->select_start),
1383                         &(canvas->select_end), ret_segs, ret_y1, ret_y2));
1384 }
1385
1386 /*****************************************************************************
1387  * Function:    _DtCanvasActivatePts()
1388  *
1389  * Purpose:     Activate the points given.
1390  *
1391  *****************************************************************************/
1392 _DtCvStatus
1393 _DtCanvasActivatePts (
1394     _DtCvHandle          canvas_handle,
1395     unsigned int         mask,
1396     _DtCvPointInfo      *info,
1397     _DtCvUnit           *ret_y1,
1398     _DtCvUnit           *ret_y2)
1399 {
1400     int                  markIdx;
1401     _DtCanvasStruct     *canvas = (_DtCanvasStruct *) canvas_handle;
1402     _DtCvSelectData      startSel = defaultSelect;
1403     _DtCvSelectData      endSel   = defaultSelect;
1404     _DtCvFlags           flag;
1405     _DtCvSegmentI       *firstSeg;
1406
1407 #define REQUIRE_SEGS \
1408                 (_DtCvACTIVATE_MARK | _DtCvACTIVATE_SELECTION)
1409     /*
1410      * check to see if there is anything to do
1411      */
1412     if (0 == mask)
1413         return _DtCvSTATUS_NONE;
1414
1415     if ((mask & _DtCvACTIVATE_MARK) && (mask & _DtCvDEACTIVATE))
1416         return _DtCvSTATUS_BAD;
1417
1418     /*
1419      * Convert the segments into starting and ending positions.
1420      */
1421     if (((mask & _DtCvDEACTIVATE) && NULL == info->client_data)
1422                 || (mask & REQUIRE_SEGS))
1423       {
1424         if (NULL == info || NULL == info->segs ||
1425                 _DtCvSTATUS_BAD == _DtCvCvtSegsToPts(canvas, info->segs,
1426                                                 &startSel, &endSel,
1427                                                 ret_y1, ret_y2, &firstSeg))
1428             return _DtCvSTATUS_BAD;
1429       }
1430
1431     /*
1432      * Activate as a selection
1433      */
1434     if (mask & _DtCvACTIVATE_SELECTION)
1435       {
1436         _DtCanvasProcessSelection (canvas_handle, 0, 0, _DtCvSELECTION_CLEAR);
1437
1438         canvas->select_start = startSel;
1439         canvas->select_end   = endSel;
1440
1441         _DtCvDrawAreaWithFlags (canvas, startSel, endSel,
1442                                                 0, _DtCvSELECTED_FLAG,
1443                                                 _DtCvBAD_TYPE, NULL);
1444       }
1445
1446     /*
1447      * Activate as a mark
1448      */
1449     if (mask & _DtCvACTIVATE_MARK)
1450       {
1451         int             travIdx;
1452         _DtCvUnit       x;
1453         _DtCvUnit       y;
1454         _DtCvUnit       width;
1455         _DtCvUnit       height;
1456
1457         markIdx = _DtCvAddToMarkList(canvas, info->client_data,
1458                                 _DtCvIsMarkMaskOn(mask), &startSel, &endSel);
1459         if (-1 == markIdx)
1460             return _DtCvSTATUS_BAD;
1461
1462         /*
1463          * now put the mark in the traversal list and merge it into the
1464          * proper place.
1465          */
1466         travIdx = _DtCvGetNextTravEntry(canvas);
1467         if (-1 == travIdx
1468                 || 0 != _DtCvSetTravEntryInfo(canvas, travIdx,
1469                                                 _DtCvTraversalMark, firstSeg,
1470                                                 markIdx, _DtCvTRUE)
1471                 || 0 != _DtCvCalcMarkPos(canvas, markIdx,
1472                                                 &x, &y, &width, &height)
1473                 || 0 != _DtCvSetTravEntryPos(canvas, travIdx,
1474                                                 x, y, width, height))
1475             return _DtCvSTATUS_BAD;
1476
1477         _DtCvSortTraversalList(canvas, _DtCvTRUE);
1478
1479         /*
1480          * draw these segments marked.
1481          */
1482         flag = _DtCvMARK_FLAG;
1483         if (_DtCvTRUE == canvas->marks[markIdx].on)
1484             flag |= _DtCvMARK_ON;
1485
1486         _DtCvDrawAreaWithFlags (canvas, startSel, endSel,
1487                                                         0, flag,
1488                                                         _DtCvBAD_TYPE, NULL);
1489       }
1490
1491     /*
1492      * Clear the mark flag.
1493      */
1494     else if (mask & _DtCvDEACTIVATE)
1495       {
1496         int     travIdx;
1497
1498         /*
1499          * is there anything to deacivate?
1500          */
1501         if (NULL == canvas->marks || 0 == canvas->mark_cnt)
1502             return _DtCvSTATUS_BAD;
1503
1504         /*
1505          * was client data specified? If so, then look for it and ignore
1506          * the segment data.
1507          */
1508         markIdx = 0;
1509         if (NULL != info->client_data)
1510           {
1511             while (markIdx < canvas->mark_cnt &&
1512                    canvas->marks[markIdx].client_data != info->client_data)
1513                 markIdx++;
1514
1515             /*
1516              * initialize the selection points
1517              */
1518             if (markIdx < canvas->mark_cnt)
1519               {
1520                 startSel = canvas->marks[markIdx].beg;
1521                 endSel   = canvas->marks[markIdx].end;
1522               }
1523           }
1524         /*
1525          * look for the marked set using the segments.
1526          */
1527         else
1528           {
1529             while (markIdx < canvas->mark_cnt
1530                    && startSel.line_idx != canvas->marks[markIdx].beg.line_idx
1531                    && startSel.char_idx != canvas->marks[markIdx].beg.char_idx
1532                    && endSel.line_idx != canvas->marks[markIdx].end.line_idx
1533                    && endSel.line_idx != canvas->marks[markIdx].end.char_idx)
1534                 markIdx++;
1535           }
1536
1537         if (markIdx >= canvas->mark_cnt)
1538             return _DtCvSTATUS_BAD;
1539
1540         /*
1541          * draw these segments unmarked.
1542          */
1543         flag = _DtCvMARK_FLAG;
1544         if (_DtCvTRUE == canvas->marks[markIdx].on)
1545             flag |= _DtCvMARK_ON;
1546
1547         canvas->marks[markIdx].on = _DtCvFALSE;
1548         _DtCvDrawAreaWithFlags (canvas, startSel, endSel, flag, 0,
1549                                                 _DtCvBAD_TYPE, NULL);
1550
1551         /*
1552          * remove the mark from the traversal list
1553          *
1554          * first find the traversal entry of the mark and adjust any
1555          * traversal mark index values to reflect that the mark
1556          * list is about to shrink by 1.
1557          */
1558         for (travIdx = 0; _DtCvTraversalMark != canvas->trav_lst[travIdx].type
1559                         || markIdx != canvas->trav_lst[travIdx].idx; travIdx++)
1560           {
1561             /*
1562              * is this mark after the one being removed?
1563              * if so, decrease its index because it's about to move.
1564              */
1565             if (_DtCvTraversalMark == canvas->trav_lst[travIdx].type &&
1566                                         markIdx < canvas->trav_lst[travIdx].idx)
1567                 canvas->trav_lst[travIdx].idx--;
1568           }
1569
1570         /*
1571          * move the list of traversal entries to eliminate the mark entry.
1572          */
1573         while (travIdx + 1 < canvas->trav_cnt)
1574           {
1575             canvas->trav_lst[travIdx] = canvas->trav_lst[travIdx + 1];
1576
1577             /*
1578              * is this a mark after the one being removed?
1579              * if so, decrease it's index because it's about to move.
1580              */
1581             if (_DtCvTraversalMark == canvas->trav_lst[travIdx].type &&
1582                                         markIdx < canvas->trav_lst[travIdx].idx)
1583                 canvas->trav_lst[travIdx].idx--;
1584
1585             travIdx++;
1586           }
1587
1588         /*
1589          * update the traversal count and back up to the previous traversal
1590          * if the mark was at the end of the traversal list.
1591          */
1592         canvas->trav_cnt--;
1593         if (canvas->cur_trav >= canvas->trav_cnt)
1594             canvas->cur_trav--;
1595
1596         /*
1597          * move the list of marks up
1598          */
1599         while (markIdx + 1 < canvas->mark_cnt)
1600           {
1601             canvas->marks[markIdx] = canvas->marks[markIdx + 1];
1602             markIdx++;
1603           }
1604
1605         canvas->mark_cnt--;
1606       }
1607     else if ((_DtCvACTIVATE_MARK_ON | _DtCvACTIVATE_MARK_OFF) & mask)
1608       {
1609         markIdx = 0;
1610         if (NULL != info && NULL != info->client_data)
1611           {
1612             while (markIdx < canvas->mark_cnt &&
1613                    canvas->marks[markIdx].client_data != info->client_data)
1614                 markIdx++;
1615
1616             /*
1617              * was a mark with this client data found?
1618              */
1619             if (markIdx >= canvas->mark_cnt)
1620                 return _DtCvSTATUS_BAD;
1621           }
1622         else
1623           {
1624             /*
1625              * are there any traversals? Is the current one sitting on a mark?
1626              */
1627             if (0 == canvas->trav_cnt || -1 == canvas->cur_trav ||
1628                 _DtCvTraversalMark != canvas->trav_lst[canvas->cur_trav].type)
1629                 return _DtCvSTATUS_BAD;
1630
1631             /*
1632              * get the mark index
1633              */
1634             markIdx = canvas->trav_lst[canvas->cur_trav].idx;
1635           }
1636
1637         /*
1638          * is this different than what it is set at now? If not, do nothing.
1639          */
1640         if (_DtCvIsMarkMaskOn(mask) == canvas->marks[markIdx].on)
1641             return _DtCvSTATUS_NONE;
1642
1643         /*
1644          * set to mask value.
1645          */
1646         canvas->marks[markIdx].on = _DtCvIsMarkMaskOn(mask);
1647
1648         /*
1649          * set the flags correctly.
1650          */
1651         flag = _DtCvMARK_FLAG;
1652         if (_DtCvTRUE == canvas->marks[markIdx].on)
1653             flag |= _DtCvMARK_ON;
1654         if (_DtCvTRUE == canvas->trav_on &&
1655                         markIdx == canvas->trav_lst[canvas->cur_trav].idx)
1656             flag |= _DtCvTRAVERSAL_FLAG;
1657
1658         /*
1659          * draw the mark opposite what it was
1660          */
1661         _DtCvDrawAreaWithFlags (canvas, canvas->marks[markIdx].beg,
1662                                 canvas->marks[markIdx].end,
1663                                 (flag ^ _DtCvMARK_ON), flag,
1664                                 _DtCvBAD_TYPE, NULL);
1665       }
1666
1667     return _DtCvSTATUS_OK;
1668 }
1669
1670 /*****************************************************************************
1671  * Function:    _DtCanvasGetMarkPositions()
1672  *
1673  * Purpose:     Return the position in the canvas of the marks.
1674  *
1675  *****************************************************************************/
1676 _DtCvStatus
1677 _DtCanvasGetMarkPositions (
1678     _DtCvHandle             canvas_handle,
1679     _DtCvMarkPos         ***ret_pos)
1680 {
1681     int  i;
1682     _DtCanvasStruct     *canvas = (_DtCanvasStruct *) canvas_handle;
1683     _DtCvMarkPos        *nextPos;
1684     _DtCvMarkData       *markLst = canvas->marks;
1685
1686     *ret_pos = NULL;
1687
1688     if (0 == canvas->mark_cnt)
1689         return _DtCvSTATUS_NONE;
1690
1691     for (i = 0; i < canvas->mark_cnt; i++, markLst++)
1692       {
1693         /*
1694          * malloc memory for the information
1695          */
1696         nextPos = (_DtCvMarkPos *) malloc (sizeof(_DtCvMarkPos));
1697         if (NULL == nextPos)
1698             return _DtCvSTATUS_BAD;
1699
1700         /*
1701          * client data and baselines
1702          */
1703         nextPos->client_data = markLst->client_data;
1704         nextPos->baseline1   = canvas->txt_lst[markLst->beg.line_idx].baseline;
1705         nextPos->baseline2   = canvas->txt_lst[markLst->end.line_idx].baseline;
1706
1707         /*
1708          * top left corner
1709          */
1710         nextPos->x1 = markLst->beg.x;
1711         nextPos->y1 = nextPos->baseline1 -
1712                                 canvas->txt_lst[markLst->beg.line_idx].ascent;
1713
1714         /*
1715          * bottom right corner
1716          */
1717         nextPos->x2 = markLst->end.x;
1718         nextPos->y2 = nextPos->baseline2 +
1719                                 canvas->txt_lst[markLst->end.line_idx].descent;
1720
1721         *ret_pos = (_DtCvMarkPos **) _DtCvAddPtrToArray((void **) *ret_pos,
1722                                                         (void *) nextPos);
1723         if (NULL == *ret_pos)
1724             return _DtCvSTATUS_BAD;
1725       }
1726
1727     return _DtCvSTATUS_OK;
1728 }