1 /*
2  * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
3  *
4  * Copyright (C) 2001-2009, William Chia-Wei Cheng.
5  *
6  * This file may be distributed under the terms of the Q Public License
7  * as defined by Trolltech AS of Norway and appearing in the file
8  * LICENSE.QPL included in the packaging of this file.
9  *
10  * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
11  * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
12  * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
13  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
16  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/attr.c,v 1.15 2011/05/16 16:21:56 william Exp $
19  */
20 
21 #define _INCLUDE_FROM_ATTR_C_
22 
23 #include "tgifdefs.h"
24 
25 #include "attr.e"
26 #include "auxtext.e"
27 #include "button.e"
28 #include "choice.e"
29 #include "choose.e"
30 #include "cmd.e"
31 #include "color.e"
32 #include "cursor.e"
33 #include "dialog.e"
34 #include "drawing.e"
35 #include "dup.e"
36 #include "file.e"
37 #include "font.e"
38 #include "grid.e"
39 #include "mainloop.e"
40 #include "mainmenu.e"
41 #include "mark.e"
42 #include "menu.e"
43 #include "miniline.e"
44 #include "move.e"
45 #include "msg.e"
46 #include "names.e"
47 #include "obj.e"
48 #include "pattern.e"
49 #include "raster.e"
50 #include "rect.e"
51 #include "ruler.e"
52 #include "select.e"
53 #include "setup.e"
54 #include "stk.e"
55 #include "strtbl.e"
56 #include "text.e"
57 #include "util.e"
58 #include "wb.e"
59 
60 int dropObsIconAttrWhenUpdate=FALSE;
61 int maxAttrGroups=0;
62 
63 AttrGroupInfo	**gAttrGroupInfo=NULL;
64 EditAttrInfo *gpEditAttrInEditorAttrInfo=NULL;
65 
66 static struct AttrRec *topAttr=NULL, *botAttr=NULL;
67 
LinkInAttr(PrevPtr,NextPtr,AttrPtr)68 void LinkInAttr(PrevPtr, NextPtr, AttrPtr)
69    struct AttrRec *PrevPtr, *NextPtr, *AttrPtr;
70    /* add AttrPtr between PrevPtr and NextPtr */
71 {
72    AttrPtr->prev = PrevPtr;
73    AttrPtr->next = NextPtr;
74 
75    if (PrevPtr == NULL) {
76       topAttr = AttrPtr;
77    } else {
78       PrevPtr->next = AttrPtr;
79    }
80    if (NextPtr == NULL) {
81       botAttr = AttrPtr;
82    } else {
83       NextPtr->prev = AttrPtr;
84    }
85 }
86 
FreeAttr(AttrPtr)87 void FreeAttr(AttrPtr)
88    struct AttrRec *AttrPtr;
89    /* This routine only frees the attribute record, not   */
90    /*    the text record, which must be freed explicitly. */
91 {
92    if (AttrPtr->attr_name.s != NULL) free(AttrPtr->attr_name.s);
93    if (AttrPtr->attr_value.s != NULL) free(AttrPtr->attr_value.s);
94    free(AttrPtr);
95 }
96 
UnlinkAttr(AttrPtr)97 void UnlinkAttr(AttrPtr)
98    struct AttrRec *AttrPtr;
99 {
100    struct ObjRec *own_ptr;
101    struct AttrRec **top_attr_ad;
102    struct AttrRec **bot_attr_ad;
103 
104    own_ptr = AttrPtr->owner;
105 
106    top_attr_ad = &(own_ptr->fattr);
107    bot_attr_ad = &(own_ptr->lattr);
108 
109    if (*top_attr_ad == AttrPtr) {
110       *top_attr_ad = AttrPtr->next;
111    } else {
112       AttrPtr->prev->next = AttrPtr->next;
113    }
114    if (*bot_attr_ad == AttrPtr) {
115       *bot_attr_ad = AttrPtr->prev;
116    } else {
117       AttrPtr->next->prev = AttrPtr->prev;
118    }
119 }
120 
UpdAttrOwner(first_attr,owner_obj)121 void UpdAttrOwner(first_attr, owner_obj)
122    struct AttrRec *first_attr;
123    struct ObjRec *owner_obj;
124 {
125    while (first_attr != NULL) {
126       first_attr->owner = owner_obj;
127       first_attr = first_attr->next;
128    }
129 }
130 
131 static
FindEqual(s)132 char *FindEqual(s)
133    char *s;
134 {
135    while (*s != '=' && *s != '\0') s++;
136    return ((*s == '=') ? (s) : (char*)NULL);
137 }
138 
ParseAttrStr(buf,name,name_sz,value,value_sz)139 void ParseAttrStr(buf, name, name_sz, value, value_sz)
140    char *buf, *name, *value;
141    int name_sz, value_sz;
142 {
143    char *eq_ptr=NULL, *str_ptr=NULL, *psz=NULL;
144 
145    if ((eq_ptr=FindEqual(buf)) != NULL) {
146       eq_ptr++;
147       if (name != NULL) {
148          int index=0;
149 
150          psz = name;
151          str_ptr = buf;
152          for (;;) {
153             *psz = *str_ptr;
154             psz++;
155             str_ptr++;
156             index++;
157             if (str_ptr == eq_ptr) {
158                break;
159             } else if (index >= name_sz-2) {
160                *psz++ = '=';
161                break;
162             }
163          }
164          *psz = '\0';
165       }
166       if (value != NULL) {
167          int index=0;
168 
169          str_ptr = eq_ptr;
170          psz = value;
171          do {
172             *psz = *str_ptr;
173             psz++;
174             str_ptr++;
175             index++;
176          } while (*str_ptr != '\0' && index < value_sz-1);
177 
178          *psz = '\0';
179       }
180    } else {
181       if (name != NULL) *name = '\0';
182       if (value != NULL) UtilStrCpyN(value, value_sz, buf);
183    }
184 }
185 
UpdateAttr(TextPtr,AttrPtr)186 void UpdateAttr(TextPtr, AttrPtr)
187    struct TextRec *TextPtr;
188    struct AttrRec *AttrPtr;
189    /* This routine updates the name and value in the AttrRec */
190    /*   and its ObjRec after an attribute was edited.        */
191 {
192    if (AttrPtr->nameshown) {
193       char *s=GetTextPtrFirstStrSeg(TextPtr)->dyn_str.s;
194       int buf_sz=(strlen(s)<<1)+5;
195       char *name=(char*)malloc(buf_sz), *value=(char*)malloc(buf_sz);
196 
197       if (name == NULL || value == NULL) FailAllocMessage();
198       *name = *value = '\0';
199       ParseAttrStr(GetTextPtrFirstStrSeg(TextPtr)->dyn_str.s,
200             name, buf_sz, value, buf_sz);
201       DynStrSet(&AttrPtr->attr_value, value);
202       DynStrSet(&AttrPtr->attr_name, name);
203       strcat(name, value);
204       DynStrSet(&GetTextPtrFirstStrSeg(TextPtr)->dyn_str, name);
205       free(name);
206       free(value);
207    } else {
208       char *value=GetTextPtrFirstStrSeg(TextPtr)->dyn_str.s;
209 
210       DynStrSet(&AttrPtr->attr_value, value);
211       DynStrSet(&GetTextPtrFirstStrSeg(TextPtr)->dyn_str, value);
212    }
213    UpdTextBBox(AttrPtr->obj);
214    AdjObjCache(AttrPtr->obj);
215 }
216 
DrawAttrs(Win,XOff,YOff,AttrPtr)217 void DrawAttrs(Win, XOff, YOff, AttrPtr)
218    Window Win;
219    int XOff, YOff;
220    struct AttrRec *AttrPtr;
221 {
222    struct AttrRec *ptr;
223 
224    for (ptr=AttrPtr; ptr != NULL; ptr=ptr->next) {
225       if (ptr->shown) {
226          if (!editingText) {
227             /* UpdAttr(ptr); */ /* ALERT - not sure if this is needed. */
228          }
229          DrawTextObj(Win, XOff, YOff, ptr->obj);
230       }
231    }
232 }
233 
MoveAttrs(AttrPtr,Dx,Dy)234 void MoveAttrs(AttrPtr, Dx, Dy)
235    int Dx, Dy;
236    struct AttrRec *AttrPtr;
237 {
238    struct AttrRec *attr_ptr;
239 
240    for (attr_ptr=AttrPtr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
241       MoveObj(attr_ptr->obj, Dx, Dy);
242    }
243 }
244 
DelAllAttrs(AttrPtr)245 void DelAllAttrs(AttrPtr)
246    struct AttrRec *AttrPtr;
247 {
248    struct AttrRec *ptr, *next_attr;
249 
250    for (ptr=AttrPtr; ptr != NULL; ptr=next_attr) {
251       next_attr = ptr->next;
252       FreeObj(ptr->obj);
253       FreeAttr(ptr);
254    }
255 }
256 
AddAttrByNameAndValue(ObjPtr,AttrName,AttrValue)257 struct AttrRec *AddAttrByNameAndValue(ObjPtr, AttrName, AttrValue)
258    struct ObjRec *ObjPtr;
259    char *AttrName, *AttrValue;
260 {
261    struct ObjRec *text_obj_ptr=NULL;
262    struct TextRec *text_ptr=NULL;
263    struct AttrRec *attr_ptr=NULL;
264    StrSegInfo *pStrSeg=NULL;
265    MiniLinesInfo *minilines=NULL;
266    char *buf=(char*)malloc(strlen(AttrName)+strlen(AttrValue)+1);
267 
268    if (buf == NULL) FailAllocMessage();
269    sprintf(buf, "%s%s", AttrName, AttrValue);
270 
271    text_obj_ptr = (struct ObjRec *)malloc(sizeof(struct ObjRec));
272    text_ptr = (struct TextRec *)malloc(sizeof(struct TextRec));
273    attr_ptr = (struct AttrRec *)malloc(sizeof(struct AttrRec));
274    if (text_obj_ptr == NULL || text_ptr == NULL || attr_ptr == NULL) {
275       FailAllocMessage();
276    }
277    memset(text_obj_ptr, 0, sizeof(struct ObjRec));
278    memset(text_ptr, 0, sizeof(struct TextRec));
279    memset(attr_ptr, 0, sizeof(struct AttrRec));
280 
281    minilines = (&text_ptr->minilines);
282    CreateMiniLineFromString("", &minilines->first, &minilines->last);
283    minilines->first->owner_minilines = minilines;
284 
285    CopyCurInfoIntoTextPtr(text_obj_ptr, text_ptr);
286    text_ptr->lines = 1;
287    text_ptr->cached_zoom = 0;
288    text_ptr->cached_zoomed = FALSE;
289    text_ptr->cached_bitmap = None;
290 
291    pStrSeg = GetTextPtrFirstStrSeg(text_ptr);
292 
293    DynStrSet(&pStrSeg->dyn_str, buf);
294 
295    pStrSeg->font = curFont;
296    pStrSeg->style = curStyle;
297    pStrSeg->sz_unit = GetCurSzUnit();
298    pStrSeg->underline_on = curUnderlineOn;
299    pStrSeg->overline_on = curOverlineOn;
300 
301    pStrSeg->asc = canvasFontAsc;
302    pStrSeg->des = canvasFontDes;
303    pStrSeg->direction = canvasFontDirection;
304    pStrSeg->dontreencode = canvasFontDontReencode;
305    pStrSeg->double_byte = canvasFontDoubleByte;
306    pStrSeg->double_byte_mod_bytes = canvasFontDoubleByteModBytes;
307    pStrSeg->double_byte_vertical = canvasFontDoubleByteVertical;
308 
309    text_obj_ptr->x = ObjPtr->obbox.ltx;
310    text_obj_ptr->y = ObjPtr->obbox.rby;
311    text_obj_ptr->type = OBJ_TEXT;
312    text_obj_ptr->color = ObjPtr->color;
313    memcpy(text_obj_ptr->color_str, ObjPtr->color_str,
314          sizeof(text_obj_ptr->color_str));
315    text_obj_ptr->dirty = FALSE;
316    text_obj_ptr->id = objId++;
317    text_obj_ptr->rotation = 0;
318    text_obj_ptr->locked = FALSE;
319    text_obj_ptr->ctm = NULL;
320    text_obj_ptr->detail.t = text_ptr;
321 
322    UpdTextBBox(text_obj_ptr);
323 
324    attr_ptr->shown = FALSE;
325    attr_ptr->nameshown = TRUE;
326    attr_ptr->inherited = FALSE;
327    attr_ptr->obj = text_obj_ptr;
328    attr_ptr->owner = ObjPtr;
329    DynStrSet(&attr_ptr->attr_name, AttrName);
330    DynStrSet(&attr_ptr->attr_value, AttrValue);
331 
332    text_ptr->attr = attr_ptr;
333 
334    attr_ptr->prev = NULL;
335    attr_ptr->next = ObjPtr->fattr;
336    if (ObjPtr->fattr == NULL) {
337       ObjPtr->lattr = attr_ptr;
338    } else {
339       ObjPtr->fattr->prev = attr_ptr;
340    }
341    ObjPtr->fattr = attr_ptr;
342    AdjObjBBox(ObjPtr);
343    return attr_ptr;
344 }
345 
346 static
NewAttr(OwnerObjPtr,ObjPtr,Inherited)347 struct AttrRec *NewAttr(OwnerObjPtr, ObjPtr, Inherited)
348    struct ObjRec *ObjPtr;
349    struct ObjRec *OwnerObjPtr;
350    short Inherited;
351 {
352    struct AttrRec *attr_ptr;
353 
354    attr_ptr = (struct AttrRec *)malloc(sizeof(struct AttrRec));
355    if (attr_ptr == NULL) FailAllocMessage();
356    memset(attr_ptr, 0, sizeof(struct AttrRec));
357    attr_ptr->shown = TRUE;
358    attr_ptr->nameshown = TRUE;
359    attr_ptr->inherited = Inherited;
360    attr_ptr->obj = ObjPtr;
361    attr_ptr->next= attr_ptr->prev = NULL;
362    attr_ptr->owner = OwnerObjPtr;
363    DynStrSet(&attr_ptr->attr_name, "");
364    DynStrSet(&attr_ptr->attr_value, "");
365    ObjPtr->detail.t->attr = attr_ptr;
366 
367    return attr_ptr;
368 }
369 
370 static
DupAnAttr(FromAttrPtr,ToAttrPtr)371 void DupAnAttr(FromAttrPtr, ToAttrPtr)
372    struct AttrRec *FromAttrPtr, *ToAttrPtr;
373 {
374    struct ObjRec *text_obj_ptr;
375 
376    text_obj_ptr = (struct ObjRec *)malloc(sizeof(struct ObjRec));
377    if (text_obj_ptr == NULL) FailAllocMessage();
378    memset(text_obj_ptr, 0, sizeof(struct ObjRec));
379    DupObjBasics(FromAttrPtr->obj, text_obj_ptr);
380    DupTextObj(FromAttrPtr->obj->detail.t, FromAttrPtr->obj, text_obj_ptr);
381 
382    DynStrCpy(&ToAttrPtr->attr_name, &FromAttrPtr->attr_name);
383    DynStrCpy(&ToAttrPtr->attr_value, &FromAttrPtr->attr_value);
384    ToAttrPtr->shown = FromAttrPtr->shown;
385    ToAttrPtr->nameshown = FromAttrPtr->nameshown;
386    ToAttrPtr->inherited = FromAttrPtr->inherited;
387    ToAttrPtr->obj = text_obj_ptr;
388    ToAttrPtr->next= ToAttrPtr->prev = NULL;
389    text_obj_ptr->detail.t->attr = ToAttrPtr;
390 }
391 
DupAttrs(FromObjPtr,ToObjPtr)392 void DupAttrs(FromObjPtr, ToObjPtr)
393    struct ObjRec *FromObjPtr, *ToObjPtr;
394 {
395    struct AttrRec *to_attr_ptr, *from_attr_ptr;
396 
397    topAttr = botAttr = NULL;
398    from_attr_ptr = FromObjPtr->lattr;
399    for ( ; from_attr_ptr != NULL; from_attr_ptr=from_attr_ptr->prev) {
400       to_attr_ptr = (struct AttrRec *)malloc(sizeof(struct AttrRec));
401       if (to_attr_ptr == NULL) FailAllocMessage();
402       memset(to_attr_ptr, 0, sizeof(struct AttrRec));
403       to_attr_ptr->owner = ToObjPtr;
404       DupAnAttr(from_attr_ptr, to_attr_ptr);
405       LinkInAttr((struct AttrRec *)NULL, topAttr, to_attr_ptr);
406    }
407    ToObjPtr->fattr = topAttr;
408    ToObjPtr->lattr = botAttr;
409 }
410 
411 static
AddAttr(ObjPtr,TextObjPtr)412 void AddAttr(ObjPtr, TextObjPtr)
413    struct ObjRec *ObjPtr, *TextObjPtr;
414 {
415    struct AttrRec *attr_ptr=NULL;
416    struct TextRec *text_ptr=TextObjPtr->detail.t;
417 
418    topAttr = ObjPtr->fattr;
419    botAttr = ObjPtr->lattr;
420 
421    UnlinkObj(TextObjPtr);
422    TextObjPtr->next = TextObjPtr->prev = NULL;
423    attr_ptr = NewAttr(ObjPtr, TextObjPtr, FALSE);
424    UpdateAttr(text_ptr, attr_ptr);
425    LinkInAttr((struct AttrRec *)NULL, topAttr, attr_ptr);
426 
427    ObjPtr->fattr = topAttr;
428    ObjPtr->lattr = botAttr;
429 }
430 
AddAttrs()431 void AddAttrs()
432 {
433    struct ObjRec *owner_ptr=NULL;
434    struct SelRec *sel_ptr;
435    int text_count=0, sel_ltx, sel_lty, sel_rbx, sel_rby;
436    int locked_text_count=0;
437 
438    if (topSel == NULL) {
439       MsgBox(TgLoadString(STID_SELECT_AT_LEAST_ONE_TEXT), TOOL_NAME, INFO_MB);
440       return;
441    }
442    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
443       switch (sel_ptr->obj->type) {
444       case OBJ_TEXT: text_count++; break;
445 
446       default:
447          if (owner_ptr != NULL) {
448             MsgBox(TgLoadString(STID_MORE_THAN_ONE_NON_TEXT), TOOL_NAME,
449                   INFO_MB);
450             return;
451          }
452          owner_ptr = sel_ptr->obj;
453          break;
454       }
455    }
456 
457    if (text_count == 0) {
458       MsgBox(TgLoadString(STID_NO_TEXT_SELECTED_ADD_ATTRS), TOOL_NAME,
459             INFO_MB);
460       return;
461    }
462    if (owner_ptr == NULL) {
463       MsgBox(TgLoadString(STID_NO_NON_TEXT_SELECTED), TOOL_NAME, INFO_MB);
464       return;
465    }
466    HighLightReverse();
467    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
468    sel_ltx = selLtX; sel_lty = selLtY;
469    sel_rbx = selRbX; sel_rby = selRbY;
470 
471    for (sel_ptr=botSel;  sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
472       if (sel_ptr->obj->type == OBJ_TEXT) {
473          if (sel_ptr->obj->locked) {
474             locked_text_count++;
475             sel_ptr->obj->locked = FALSE;
476          }
477          AddAttr(owner_ptr, sel_ptr->obj);
478       }
479    }
480 
481    RemoveAllSel();
482    UnlinkObj(owner_ptr);
483    AddObj(NULL, topObj, owner_ptr);
484    topSel = botSel = (struct SelRec *)malloc(sizeof(struct SelRec));
485    if (topSel == NULL) FailAllocMessage();
486    topSel->obj = owner_ptr;
487    topSel->prev = NULL;
488    botSel->next = NULL;
489    AdjObjBBox(owner_ptr);
490    UpdSelBBox();
491    RecordCmd(CMD_MANY_TO_ONE, NULL, topSel, botSel, 1);
492    RedrawAreas(botObj, sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
493          sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
494          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
495          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
496    HighLightForward();
497    SetFileModified(TRUE);
498    justDupped = FALSE;
499    if (locked_text_count==0) {
500       Msg(TgLoadString(STID_TEXT_ATTACHED));
501    } else {
502       Msg(TgLoadString(STID_TEXT_UNLOCKED_AND_ATTACHED));
503    }
504 }
505 
506 static
SaveAttr(FP,AttrPtr)507 void SaveAttr(FP, AttrPtr)
508    FILE *FP;
509    struct AttrRec *AttrPtr;
510 {
511    struct TextRec *text_ptr=AttrPtr->obj->detail.t;
512 
513    if (fprintf(FP, "attr(\"") == EOF) writeFileFailed = TRUE;
514    if (GetTextPtrFirstStrSeg(text_ptr)->double_byte) {
515       SaveDoubleByteString(FP, AttrPtr->attr_name.s);
516    } else {
517       SaveString(FP, AttrPtr->attr_name.s);
518    }
519    if (fprintf(FP, "\", \"") == EOF) writeFileFailed = TRUE;
520    if (GetTextPtrFirstStrSeg(text_ptr)->double_byte) {
521       SaveDoubleByteString(FP, AttrPtr->attr_value.s);
522    } else {
523       SaveString(FP, AttrPtr->attr_value.s);
524    }
525    if (fprintf(FP, "\", %1d, %1d, %1d,\n",
526          AttrPtr->shown, AttrPtr->nameshown, AttrPtr->inherited) == EOF) {
527       writeFileFailed = TRUE;
528    }
529    SaveTextObj(FP, AttrPtr->obj);
530    if (fprintf(FP, ")") == EOF) writeFileFailed = TRUE;
531 }
532 
SaveAttrs(FP,BotAttrPtr)533 void SaveAttrs(FP, BotAttrPtr)
534    FILE *FP;
535    struct AttrRec *BotAttrPtr;
536 {
537    struct AttrRec *ptr;
538 
539    if (fprintf(FP, "[\n") == EOF) writeFileFailed = TRUE;
540 
541    for (ptr=BotAttrPtr; ptr != NULL; ptr=ptr->prev) {
542       SaveAttr(FP, ptr);
543       if (ptr->prev != NULL) {
544          if (fprintf(FP, ",\n") == EOF) {
545             writeFileFailed = TRUE;
546          }
547       }
548    }
549    if (BotAttrPtr == NULL) {
550       if (fprintf(FP, "]") == EOF) writeFileFailed = TRUE;
551    } else {
552       if (fprintf(FP, "\n]") == EOF) writeFileFailed = TRUE;
553    }
554 }
555 
ReadAttrString(Str)556 char *ReadAttrString(Str)
557    char *Str;
558 {
559    char *s;
560 
561    for (s=Str; *s != '\0'; s++) {
562       if (*s == '"') {
563          if (s[1] == '"') {
564             s++;
565          } else {
566             break;
567          }
568       } else if (*s == '\\') {
569          s++;
570       }
571    }
572    if (*s == '"') s++;
573    return s;
574 }
575 
576 static
FreeBufAndReturn(line,rc)577 int FreeBufAndReturn(line, rc)
578    char *line;
579    int rc;
580 {
581    if (line != NULL) free(line);
582    return rc;
583 }
584 
ReadAttr(FP,AttrPtr)585 int ReadAttr(FP, AttrPtr)
586    FILE *FP;
587    struct AttrRec **AttrPtr;
588 {
589    struct ObjRec *TextObj;
590    char *s, *line;
591    char *name, *value;
592    int len, shown, nameshown, inherited;
593 
594    if ((line=UtilGetALine(FP)) == NULL) {
595       sprintf(gszMsgBox, TgLoadString(STID_UNEXPECTED_EOF_IN_ABORT_READ),
596             scanFileName, scanLineNum, "ReadAttr()");
597       if (PRTGIF) {
598          fprintf(stderr, "%s\n", gszMsgBox);
599       } else {
600          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
601       }
602       return FALSE;
603    }
604    scanLineNum++;
605 
606    if (*line == ']') return FreeBufAndReturn(line, FALSE);
607 
608    *AttrPtr = NULL;
609    len = strlen(line);
610 
611    name = (char *)malloc((len+1)*sizeof(char));
612    if (name == NULL) FailAllocMessage();
613    s = FindChar((int)'"', line);
614    strcpy(name, s);
615    s = ReadAttrString(s);
616    s = FindChar((int)',', s);
617    value = (char *)malloc((len+1)*sizeof(char));
618    if (value == NULL) FailAllocMessage();
619    strcpy(value, FindChar((int)'"', s));
620    s = ReadAttrString(value);
621    s = FindChar((int)',', s);
622 
623    InitScan(s, "\t\n ,");
624    if (GETINT("attribute", shown, "shown") == INVALID ||
625        GETINT("attribute", nameshown, "name shown") == INVALID ||
626        GETINT("attribute", inherited, "inherited") == INVALID) {
627       free(name);
628       free(value);
629       return FreeBufAndReturn(line, FALSE);
630    }
631 
632    *AttrPtr = (struct AttrRec *)malloc(sizeof(struct AttrRec));
633    if (*AttrPtr == NULL) FailAllocMessage();
634    memset(*AttrPtr, 0, sizeof(struct AttrRec));
635 
636    s = ReadString(name);
637    *(--s) = '\0';
638    DynStrSet(&((*AttrPtr)->attr_name), name);
639    s = ReadString(value);
640    *(--s) = '\0';
641    DynStrSet(&((*AttrPtr)->attr_value), value);
642 
643    free(name);
644    free(value);
645 
646    (*AttrPtr)->shown = shown;
647    (*AttrPtr)->nameshown = nameshown;
648    (*AttrPtr)->inherited = inherited;
649 
650    if (ReadObj(FP, &TextObj) == FALSE) {
651      free(*AttrPtr);
652      *AttrPtr = NULL;
653      return FreeBufAndReturn(line, FALSE);
654    }
655    TextObj->detail.t->attr = *AttrPtr;
656    (*AttrPtr)->obj = TextObj;
657 
658    return FreeBufAndReturn(line, TRUE);
659 }
660 
FindTopLevelObjWithType(type_str)661 struct ObjRec *FindTopLevelObjWithType(type_str)
662    char *type_str;
663 {
664    struct ObjRec *obj_ptr=NULL;
665 
666    for (obj_ptr=botObj; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
667       struct AttrRec *attr_ptr=NULL;
668 
669       for (attr_ptr=obj_ptr->fattr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
670          if (strcmp(attr_ptr->attr_name.s, "type=") == 0 &&
671                strcmp(attr_ptr->attr_value.s, type_str) == 0) {
672             return obj_ptr;
673          }
674       }
675    }
676    return NULL;
677 }
678 
679 static
ObjMightChangeForShowAndUpdAttrNames(ObjPtr)680 int ObjMightChangeForShowAndUpdAttrNames(ObjPtr)
681    struct ObjRec *ObjPtr;
682 {
683    struct AttrRec *attr_ptr;
684 
685    if ((attr_ptr=ObjPtr->fattr) != NULL) {
686       for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
687          if (!(attr_ptr->nameshown)) {
688             return TRUE;
689          }
690       }
691    }
692    return FALSE;
693 }
694 
695 static
UpdateFirstTextStringWithAttrNameAndValue(ObjPtr,AttrPtr)696 void UpdateFirstTextStringWithAttrNameAndValue(ObjPtr, AttrPtr)
697    struct ObjRec *ObjPtr;
698    struct AttrRec *AttrPtr;
699 {
700    char *buf=(char*)malloc(AttrPtr->attr_name.sz+AttrPtr->attr_value.sz);
701 
702    /* one extra char allocated, no big deal */
703    if (buf == NULL) FailAllocMessage();
704    sprintf(buf, "%s%s", AttrPtr->attr_name.s, AttrPtr->attr_value.s);
705    DynStrSet(&GetTextObjFirstStrSeg(ObjPtr)->dyn_str, buf);
706    free(buf);
707 }
708 
709 static
DoShowAndUpdAttrNames(ObjPtr,Force)710 int DoShowAndUpdAttrNames(ObjPtr, Force)
711    struct ObjRec *ObjPtr;
712    int Force;
713    /* Force will force attribute name to be shown whether the attribute */
714    /*    is inherited or not.                                           */
715 {
716    struct AttrRec *attr_ptr=ObjPtr->fattr;
717    int picture_changed=FALSE;
718 
719    for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
720       if (!(attr_ptr->nameshown) && (Force || !(attr_ptr->inherited))) {
721          UpdateFirstTextStringWithAttrNameAndValue(attr_ptr->obj, attr_ptr);
722 
723          attr_ptr->nameshown = TRUE;
724          UpdTextBBox(attr_ptr->obj);
725          if (attr_ptr->shown) {
726             picture_changed = TRUE;
727 
728             if (attr_ptr->obj->detail.t->cached_bitmap != None) {
729                XFreePixmap(mainDisplay,
730                      attr_ptr->obj->detail.t->cached_bitmap);
731             }
732             attr_ptr->obj->detail.t->cached_zoom = 0;
733             attr_ptr->obj->detail.t->cached_bitmap = None;
734          }
735       }
736    }
737    AdjObjBBox(ObjPtr);
738 
739    return picture_changed;
740 }
741 
742 static
ShowAndUpdAttrNames()743 int ShowAndUpdAttrNames()
744    /* returns TRUE if any attribute names are updated                   */
745    /* This routine concatinate the 'name' and 's' first of every        */
746    /*    attribute of the selected object and assign that to the        */
747    /*    first line of the text object the attribute pointer points to. */
748 {
749    struct SelRec *sel_ptr;
750    int picture_changed=FALSE;
751 
752    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
753       if (ObjMightChangeForShowAndUpdAttrNames(sel_ptr->obj)) {
754          PrepareToReplaceAnObj(sel_ptr->obj);
755          picture_changed = DoShowAndUpdAttrNames(sel_ptr->obj, TRUE);
756          RecordReplaceAnObj(sel_ptr->obj);
757       }
758    }
759    return picture_changed;
760 }
761 
ShowAllAttrNames()762 void ShowAllAttrNames()
763 {
764    HighLightReverse();
765    StartCompositeCmd();
766    if (ShowAndUpdAttrNames()) {
767       UpdSelBBox();
768       RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1),
769             selLtY-GRID_ABS_SIZE(1), selRbX+GRID_ABS_SIZE(1),
770             selRbY+GRID_ABS_SIZE(1));
771       SetFileModified(TRUE);
772    }
773    EndCompositeCmd();
774    HighLightForward();
775 }
776 
777 static
ShowAndUpdAttrs()778 int ShowAndUpdAttrs()
779    /* returns TRUE if any attribute was not shown before */
780 {
781    struct SelRec *sel_ptr;
782    struct ObjRec *obj_ptr;
783    struct AttrRec *attr_ptr;
784    int picture_changed=FALSE;
785 
786    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
787       obj_ptr = sel_ptr->obj;
788       attr_ptr = obj_ptr->fattr;
789       if (attr_ptr != NULL) {
790          int obj_changed=FALSE;
791 
792          for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
793             if (!attr_ptr->shown) {
794                obj_changed = TRUE;
795                break;
796             }
797          }
798          if (obj_changed) {
799             PrepareToReplaceAnObj(obj_ptr);
800             for (attr_ptr=obj_ptr->fattr; attr_ptr!=NULL;
801                   attr_ptr=attr_ptr->next) {
802                if (!attr_ptr->shown) {
803                   attr_ptr->shown = TRUE;
804                }
805             }
806             picture_changed = TRUE;
807             AdjObjBBox(obj_ptr);
808             RecordReplaceAnObj(obj_ptr);
809          }
810       }
811    }
812    return picture_changed;
813 }
814 
ShowAllAttrs()815 void ShowAllAttrs()
816 {
817    HighLightReverse();
818    StartCompositeCmd();
819    if (ShowAndUpdAttrs()) {
820       UpdSelBBox();
821       RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1),
822             selLtY-GRID_ABS_SIZE(1), selRbX+GRID_ABS_SIZE(1),
823             selRbY+GRID_ABS_SIZE(1));
824       SetFileModified(TRUE);
825    }
826    EndCompositeCmd();
827    HighLightForward();
828 }
829 
830 static
HideAndUpdAttrs()831 int HideAndUpdAttrs()
832    /* returns TRUE if any attribute was shown */
833 {
834    struct SelRec *sel_ptr;
835    struct ObjRec *obj_ptr;
836    struct AttrRec *attr_ptr;
837    int picture_changed=FALSE;
838 
839    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
840       obj_ptr = sel_ptr->obj;
841       attr_ptr = obj_ptr->fattr;
842       if (attr_ptr != NULL) {
843          int obj_changed= FALSE;
844 
845          for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
846             if (attr_ptr->shown) {
847                obj_changed = TRUE;
848                break;
849             }
850          }
851          if (obj_changed) {
852             PrepareToReplaceAnObj(obj_ptr);
853             for (attr_ptr=obj_ptr->fattr; attr_ptr!=NULL;
854                   attr_ptr=attr_ptr->next) {
855                if (attr_ptr->shown) {
856                   attr_ptr->shown = FALSE;
857                }
858             }
859             picture_changed = TRUE;
860             AdjObjBBox(obj_ptr);
861             RecordReplaceAnObj(obj_ptr);
862          }
863       }
864    }
865    return picture_changed;
866 }
867 
HideAllAttrs()868 void HideAllAttrs()
869 {
870    int sel_ltx, sel_lty, sel_rbx, sel_rby;
871 
872    sel_ltx = selLtX; sel_lty = selLtY; sel_rbx = selRbX; sel_rby = selRbY;
873 
874    HighLightReverse();
875    StartCompositeCmd();
876    if (HideAndUpdAttrs()) {
877       UpdSelBBox();
878       RedrawAnArea(botObj, sel_ltx-GRID_ABS_SIZE(1),
879             sel_lty-GRID_ABS_SIZE(1), sel_rbx+GRID_ABS_SIZE(1),
880             sel_rby+GRID_ABS_SIZE(1));
881       SetFileModified(TRUE);
882    }
883    EndCompositeCmd();
884    HighLightForward();
885 }
886 
887 static
HideAndUpdAttrNames()888 int HideAndUpdAttrNames()
889    /* returns TRUE if any attribute names are updated */
890    /* For all the first line of the selected object's attributes,    */
891    /*    this routine change them to the 's' field of the attribute. */
892 {
893    struct SelRec *sel_ptr;
894    struct ObjRec *obj_ptr;
895    struct AttrRec *attr_ptr;
896    int picture_changed=FALSE;
897 
898    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
899       obj_ptr = sel_ptr->obj;
900       attr_ptr = obj_ptr->fattr;
901       if (attr_ptr != NULL) {
902          int obj_change=FALSE;
903 
904          for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
905             if (attr_ptr->nameshown && *(attr_ptr->attr_name.s) != '\0') {
906                obj_change = TRUE;
907                break;
908             }
909          }
910          if (obj_change) {
911             PrepareToReplaceAnObj(obj_ptr);
912             for (attr_ptr=obj_ptr->fattr; attr_ptr!=NULL;
913                   attr_ptr=attr_ptr->next) {
914                if (attr_ptr->nameshown && *(attr_ptr->attr_name.s) != '\0') {
915                   struct TextRec *text_ptr=attr_ptr->obj->detail.t;
916 
917                   attr_ptr->nameshown = FALSE;
918                   DynStrCpy(
919                         &GetTextPtrFirstStrSeg(text_ptr)->dyn_str,
920                         &attr_ptr->attr_value);
921                   UpdTextBBox(attr_ptr->obj);
922                   if (attr_ptr->shown) {
923                      picture_changed = TRUE;
924 
925                      if (attr_ptr->obj->detail.t->cached_bitmap != None) {
926                         XFreePixmap(mainDisplay,
927                               attr_ptr->obj->detail.t->cached_bitmap);
928                      }
929                      attr_ptr->obj->detail.t->cached_zoom = 0;
930                      attr_ptr->obj->detail.t->cached_bitmap = None;
931                   }
932                }
933             }
934             AdjObjBBox(obj_ptr);
935             RecordReplaceAnObj(obj_ptr);
936          }
937       }
938    }
939    return picture_changed;
940 }
941 
HideAllAttrNames()942 void HideAllAttrNames()
943 {
944    int sel_ltx, sel_lty, sel_rbx, sel_rby;
945 
946    sel_ltx = selLtX; sel_lty = selLtY; sel_rbx = selRbX; sel_rby = selRbY;
947 
948    HighLightReverse();
949    StartCompositeCmd();
950    if (HideAndUpdAttrNames()) {
951       UpdSelBBox();
952       RedrawAnArea(botObj, sel_ltx-GRID_ABS_SIZE(1),
953             sel_lty-GRID_ABS_SIZE(1), sel_rbx+GRID_ABS_SIZE(1),
954             sel_rby+GRID_ABS_SIZE(1));
955       SetFileModified(TRUE);
956    }
957    EndCompositeCmd();
958    HighLightForward();
959 }
960 
DetachGroupAttrs(ObjPtr,TopSelPtr,BotSelPtr)961 void DetachGroupAttrs(ObjPtr, TopSelPtr, BotSelPtr)
962    struct ObjRec *ObjPtr;
963    struct SelRec ** TopSelPtr, **BotSelPtr;
964 {
965    struct AttrRec *attr_ptr, *next_attr;
966    struct SelRec *new_sel_ptr;
967 
968    for (attr_ptr=ObjPtr->fattr; attr_ptr!=NULL; attr_ptr=next_attr) {
969       next_attr = attr_ptr->next;
970       if (!(attr_ptr->nameshown)) {
971          UpdateFirstTextStringWithAttrNameAndValue(attr_ptr->obj, attr_ptr);
972          UpdTextBBox(attr_ptr->obj);
973       }
974 
975       attr_ptr->obj->detail.t->attr = NULL;
976 
977       attr_ptr->obj->prev = NULL;
978       attr_ptr->obj->next = ObjPtr->detail.r->first;
979 
980       if (attr_ptr->obj->next == NULL) {
981          ObjPtr->detail.r->last = attr_ptr->obj;
982       } else {
983          attr_ptr->obj->next->prev = attr_ptr->obj;
984       }
985       ObjPtr->detail.r->first = attr_ptr->obj;
986 
987       new_sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
988       if (new_sel_ptr == NULL) FailAllocMessage();
989       new_sel_ptr->obj = attr_ptr->obj;
990 
991       new_sel_ptr->prev = NULL;
992       new_sel_ptr->next = *TopSelPtr;
993 
994       if (new_sel_ptr->next == NULL) {
995          *BotSelPtr = new_sel_ptr;
996       } else {
997          (*TopSelPtr)->prev = new_sel_ptr;
998       }
999       *TopSelPtr = new_sel_ptr;
1000 
1001       FreeAttr(attr_ptr);
1002    }
1003 }
1004 
DetachAllObjAttrs(obj_ptr,TopSelPtr,BotSelPtr)1005 void DetachAllObjAttrs(obj_ptr, TopSelPtr, BotSelPtr)
1006    struct ObjRec *obj_ptr;
1007    struct SelRec **TopSelPtr, **BotSelPtr;
1008 {
1009    struct AttrRec *attr_ptr, *prev_attr;
1010 
1011    DoShowAndUpdAttrNames(obj_ptr, FALSE);
1012 
1013    (*TopSelPtr) = (*BotSelPtr) = (struct SelRec *)malloc(sizeof(struct SelRec));
1014    if (*TopSelPtr == NULL) FailAllocMessage();
1015    (*TopSelPtr)->next = (*TopSelPtr)->prev = NULL;
1016    (*TopSelPtr)->obj = obj_ptr;
1017 
1018    topAttr = botAttr = NULL;
1019    for (attr_ptr=obj_ptr->lattr; attr_ptr!=NULL; attr_ptr=prev_attr) {
1020       struct SelRec *tmp_sel_ptr;
1021 
1022       prev_attr = attr_ptr->prev;
1023 
1024       tmp_sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
1025       if (tmp_sel_ptr == NULL) FailAllocMessage();
1026 
1027       tmp_sel_ptr->prev = NULL;
1028       tmp_sel_ptr->next = (*TopSelPtr);
1029       (*TopSelPtr)->prev = tmp_sel_ptr;
1030       (*TopSelPtr) = tmp_sel_ptr;
1031       tmp_sel_ptr->obj = attr_ptr->obj;
1032 
1033       attr_ptr->obj->detail.t->attr = NULL;
1034       AddObj(obj_ptr->prev, obj_ptr, attr_ptr->obj);
1035       FreeAttr(attr_ptr);
1036    }
1037    obj_ptr->fattr = obj_ptr->lattr = NULL;
1038 }
1039 
DetachAttrs()1040 void DetachAttrs()
1041 {
1042    struct SelRec *sel_ptr, *new_sel_ptr;
1043    struct ObjRec *obj_ptr;
1044    struct AttrRec *attr_ptr, *next_attr;
1045    int picture_changed=FALSE, name_changed=FALSE;
1046 
1047    HighLightReverse();
1048    StartCompositeCmd();
1049 
1050    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
1051       obj_ptr = sel_ptr->obj;
1052       attr_ptr = obj_ptr->fattr;
1053       if (attr_ptr != NULL) {
1054          int obj_change=FALSE;
1055 
1056          for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
1057             if (!attr_ptr->inherited) {
1058                obj_change = TRUE;
1059                break;
1060             }
1061          }
1062          if (obj_change) {
1063             struct SelRec *tmp_top_sel, *tmp_bot_sel;
1064             struct SelRec *tmp_sel_ptr, *next_sel;
1065             int count;
1066 
1067             PrepareToReplaceAnObj(obj_ptr);
1068             if (DoShowAndUpdAttrNames(obj_ptr, FALSE)) name_changed = TRUE;
1069 
1070             tmp_top_sel = tmp_bot_sel = (struct SelRec *)malloc(
1071                   sizeof(struct SelRec));
1072             if (tmp_top_sel == NULL) FailAllocMessage();
1073             tmp_top_sel->next = tmp_top_sel->prev = NULL;
1074             tmp_top_sel->obj = obj_ptr;
1075             count = 1;
1076 
1077             topAttr = botAttr = NULL;
1078             for (attr_ptr=obj_ptr->fattr; attr_ptr!=NULL; attr_ptr=next_attr) {
1079                next_attr = attr_ptr->next;
1080                if (attr_ptr->inherited &&
1081                      (obj_ptr->type == OBJ_ICON || obj_ptr->type == OBJ_PIN)) {
1082                   LinkInAttr((struct AttrRec *)NULL, topAttr, attr_ptr);
1083                   continue;
1084                }
1085                picture_changed = TRUE;
1086 
1087                tmp_sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
1088                if (tmp_sel_ptr == NULL) FailAllocMessage();
1089                tmp_sel_ptr->next = tmp_bot_sel;
1090                tmp_sel_ptr->obj = attr_ptr->obj;
1091                if (tmp_top_sel == tmp_bot_sel) {
1092                   tmp_sel_ptr->prev = NULL;
1093                   tmp_top_sel->prev = tmp_sel_ptr;
1094                   tmp_top_sel = tmp_sel_ptr;
1095                } else {
1096                   tmp_sel_ptr->prev = tmp_bot_sel->prev;
1097                   tmp_bot_sel->prev->next = tmp_sel_ptr;
1098                   tmp_bot_sel->prev = tmp_sel_ptr;
1099                }
1100                count++;
1101 
1102                attr_ptr->obj->detail.t->attr = NULL;
1103                AddObj(obj_ptr->prev, obj_ptr, attr_ptr->obj);
1104                new_sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
1105                if (new_sel_ptr == NULL) FailAllocMessage();
1106                new_sel_ptr->obj = obj_ptr->prev;
1107                AddSel(sel_ptr->prev, sel_ptr, new_sel_ptr);
1108                FreeAttr(attr_ptr);
1109             }
1110             obj_ptr->fattr = topAttr;
1111             obj_ptr->lattr = botAttr;
1112             AdjObjBBox(obj_ptr);
1113             RecordCmd(CMD_ONE_TO_MANY, NULL, tmp_top_sel, tmp_bot_sel, count);
1114 
1115             for (tmp_sel_ptr=tmp_top_sel; tmp_sel_ptr!=NULL;
1116                   tmp_sel_ptr=next_sel) {
1117                next_sel = tmp_sel_ptr->next;
1118                free(tmp_sel_ptr);
1119             }
1120          }
1121       }
1122    }
1123    EndCompositeCmd();
1124    UpdSelBBox();
1125    if (picture_changed || name_changed) {
1126       RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1),
1127             selLtY-GRID_ABS_SIZE(1), selRbX+GRID_ABS_SIZE(1),
1128             selRbY+GRID_ABS_SIZE(1));
1129       SetFileModified(TRUE);
1130    }
1131    HighLightForward();
1132 }
1133 
UpdAttr(AttrPtr)1134 void UpdAttr(AttrPtr)
1135    struct AttrRec *AttrPtr;
1136    /* Update the text object's string value associated with AttrPtr */
1137 {
1138    if (AttrPtr->nameshown) {
1139       UpdateFirstTextStringWithAttrNameAndValue(AttrPtr->obj, AttrPtr);
1140    } else {
1141       struct TextRec *text_ptr=AttrPtr->obj->detail.t;
1142 
1143       DynStrCpy(&GetTextPtrFirstStrSeg(text_ptr)->dyn_str,
1144             &AttrPtr->attr_value);
1145    }
1146    UpdTextBBox(AttrPtr->obj);
1147 }
1148 
1149 static
MoveOneAttr(ObjPtr,AttrPtr)1150 int MoveOneAttr(ObjPtr, AttrPtr)
1151    struct ObjRec *ObjPtr;
1152    struct AttrRec *AttrPtr;
1153 {
1154    struct ObjRec *text_obj_ptr;
1155    int x, y, grid_x, grid_y, dx, dy, placing=TRUE;
1156    int ltx, lty, rbx, rby;
1157    int orig_x, orig_y, grid_orig_x, grid_orig_y;
1158    char buf[80], x_buf[80], y_buf[80];
1159    XEvent input, ev;
1160 
1161    text_obj_ptr = AttrPtr->obj;
1162    Msg(TgLoadString(STID_MOVE_ONE_ATTR_MOUSE_MSG));
1163    SetMouseStatus(TgLoadString(STID_SHOW_MOVE_ATTR),
1164          TgLoadString(STID_TOGGLE_NAME_SHOWN), TgLoadString(STID_HIDE_ATTR));
1165 
1166    orig_x = OFFSET_X(text_obj_ptr->x);
1167    orig_y = OFFSET_Y(text_obj_ptr->y);
1168    GridXY(orig_x, orig_y, &grid_orig_x, &grid_orig_y);
1169    ltx = OFFSET_X(text_obj_ptr->bbox.ltx);
1170    lty = OFFSET_Y(text_obj_ptr->bbox.lty);
1171    rbx = OFFSET_X(text_obj_ptr->bbox.rbx)+1;
1172    rby = OFFSET_Y(text_obj_ptr->bbox.rby)+1;
1173 
1174    if (!debugNoPointerGrab) {
1175       XGrabPointer(mainDisplay, drawWindow, FALSE,
1176             PointerMotionMask | ButtonPressMask,
1177             GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
1178    }
1179    XWarpPointer(mainDisplay, None, drawWindow, 0, 0, 0, 0, orig_x, orig_y);
1180 
1181    dx = dy = 0;
1182    grid_x = grid_orig_x; grid_y = grid_orig_y;
1183 
1184    SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1185    PixelToMeasurementUnit(x_buf, 0);
1186    PixelToMeasurementUnit(y_buf, 0);
1187    sprintf(buf, "dx=%s\ndy=%s", x_buf, y_buf);
1188    StartShowMeasureCursor(ltx, lty-(defaultFontHeight<<1), buf, TRUE);
1189    while (placing) {
1190       XNextEvent(mainDisplay, &input);
1191 
1192       if (input.type == Expose || input.type == VisibilityNotify) {
1193          PixelToMeasurementUnit(x_buf, ABS_SIZE(dx));
1194          PixelToMeasurementUnit(y_buf, ABS_SIZE(dy));
1195          sprintf(buf, "dx=%s\ndy=%s", x_buf, y_buf);
1196          ShowMeasureCursor(ltx+dx, lty-(defaultFontHeight<<1)+dy, buf, TRUE);
1197          SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1198          ExposeEventHandler(&input, TRUE);
1199          SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1200          PixelToMeasurementUnit(x_buf, ABS_SIZE(dx));
1201          PixelToMeasurementUnit(y_buf, ABS_SIZE(dy));
1202          sprintf(buf, "dx=%s\ndy=%s", x_buf, y_buf);
1203          ShowMeasureCursor(ltx+dx, lty-(defaultFontHeight<<1)+dy, buf, TRUE);
1204       } else if (input.type == MotionNotify) {
1205          x = input.xmotion.x;
1206          y = input.xmotion.y;
1207          GridXY(x, y, &grid_x, &grid_y);
1208 
1209          if (grid_x != grid_orig_x+dx || grid_y != grid_orig_y+dy) {
1210             PixelToMeasurementUnit(x_buf, ABS_SIZE(dx));
1211             PixelToMeasurementUnit(y_buf, ABS_SIZE(dy));
1212             sprintf(buf, "dx=%s\ndy=%s", x_buf, y_buf);
1213             ShowMeasureCursor(ltx+dx, lty-(defaultFontHeight<<1)+dy, buf, TRUE);
1214             SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1215             dx = grid_x - grid_orig_x;
1216             dy = grid_y - grid_orig_y;
1217             SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1218             PixelToMeasurementUnit(x_buf, ABS_SIZE(dx));
1219             PixelToMeasurementUnit(y_buf, ABS_SIZE(dy));
1220             sprintf(buf, "dx=%s\ndy=%s", x_buf, y_buf);
1221             ShowMeasureCursor(ltx+dx, lty-(defaultFontHeight<<1)+dy, buf, TRUE);
1222             MarkRulers(grid_x, grid_y);
1223          }
1224          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
1225       } else if (input.type == ButtonPress) {
1226          XUngrabPointer(mainDisplay, CurrentTime);
1227          XSync(mainDisplay, False);
1228          placing = FALSE;
1229          SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1230          PixelToMeasurementUnit(x_buf, ABS_SIZE(dx));
1231          PixelToMeasurementUnit(y_buf, ABS_SIZE(dy));
1232          sprintf(buf, "dx=%s\ndy=%s", x_buf, y_buf);
1233          EndShowMeasureCursor(ltx+dx, lty-(defaultFontHeight<<1)+dy, buf, TRUE);
1234          Msg("");
1235          SetMouseStatus("", "", "");
1236          switch (input.xbutton.button) {
1237          case Button1:
1238             if (dx != 0 || dy != 0) {
1239                if (ObjPtr->locked) {
1240                   MsgBox(TgLoadString(STID_CANNOT_MOVE_ATTR_IF_LOCKED),
1241                         TOOL_NAME, INFO_MB);
1242                   return FALSE;
1243                }
1244                HighLightReverse();
1245                PrepareToReplaceAnObj(ObjPtr);
1246                AttrPtr->shown = TRUE;
1247                MoveObj(text_obj_ptr, ABS_SIZE(dx), ABS_SIZE(dy));
1248                AdjObjBBox(ObjPtr);
1249                RecordReplaceAnObj(ObjPtr);
1250                return TRUE;
1251             } else if (!(AttrPtr->shown)) {
1252                HighLightReverse();
1253                PrepareToReplaceAnObj(ObjPtr);
1254                AttrPtr->shown = TRUE;
1255                AdjObjBBox(ObjPtr);
1256                RecordReplaceAnObj(ObjPtr);
1257                return TRUE;
1258             }
1259             return FALSE;
1260          case Button2:
1261             if (*(AttrPtr->attr_name.s) != '\0') {
1262                PrepareToReplaceAnObj(ObjPtr);
1263             }
1264             if (!(AttrPtr->nameshown && *(AttrPtr->attr_name.s)=='\0')) {
1265                AttrPtr->nameshown = !AttrPtr->nameshown;
1266             }
1267             UpdAttr(AttrPtr);
1268             if (AttrPtr->shown) {
1269                HighLightReverse();
1270                AdjObjCache(ObjPtr);
1271                AdjObjBBox(ObjPtr);
1272                if (*(AttrPtr->attr_name.s) != '\0') {
1273                   RecordReplaceAnObj(ObjPtr);
1274                }
1275                return TRUE;
1276             }
1277             if (*(AttrPtr->attr_name.s) != '\0') RecordReplaceAnObj(ObjPtr);
1278             return FALSE;
1279          case Button3:
1280             if (AttrPtr->shown) {
1281                HighLightReverse();
1282                PrepareToReplaceAnObj(ObjPtr);
1283                AttrPtr->shown = FALSE;
1284                AdjObjBBox(ObjPtr);
1285                RecordReplaceAnObj(ObjPtr);
1286                return TRUE;
1287             }
1288             return FALSE;
1289          default: break;
1290          }
1291       }
1292    }
1293    return FALSE;
1294 }
1295 
1296 static
ChangeAttrJust(ObjPtr,AttrPtr)1297 int ChangeAttrJust(ObjPtr, AttrPtr)
1298    struct ObjRec *ObjPtr;
1299    struct AttrRec *AttrPtr;
1300 {
1301    struct ObjRec *text_obj_ptr;
1302    int x, y, grid_x, grid_y, dx, dy, placing=TRUE;
1303    int ltx, lty, rbx, rby;
1304    int orig_x, orig_y, grid_orig_x, grid_orig_y;
1305    int old_just, new_just=0;
1306    XEvent input, ev;
1307 
1308    text_obj_ptr = AttrPtr->obj;
1309    Msg(TgLoadString(STID_CHANGE_ATTR_JUST_MOUSE_MSG));
1310    SetMouseStatus(TgLoadString(STID_LEFT_JUSTIFY),
1311          TgLoadString(STID_CENTER_JUSTIFY), TgLoadString(STID_RIGHT_JUSTIFY));
1312 
1313    orig_x = OFFSET_X(text_obj_ptr->x);
1314    orig_y = OFFSET_Y(text_obj_ptr->y);
1315    GridXY(orig_x, orig_y, &grid_orig_x, &grid_orig_y);
1316    ltx = OFFSET_X(text_obj_ptr->bbox.ltx);
1317    lty = OFFSET_Y(text_obj_ptr->bbox.lty);
1318    rbx = OFFSET_X(text_obj_ptr->bbox.rbx)+1;
1319    rby = OFFSET_Y(text_obj_ptr->bbox.rby)+1;
1320 
1321    if (!debugNoPointerGrab) {
1322       XGrabPointer(mainDisplay, drawWindow, FALSE,
1323             PointerMotionMask | ButtonPressMask,
1324             GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
1325    }
1326    XWarpPointer(mainDisplay, None, drawWindow, 0, 0, 0, 0, orig_x, orig_y);
1327 
1328    dx = dy = 0;
1329    grid_x = grid_orig_x; grid_y = grid_orig_y;
1330 
1331    SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1332    StartShowMeasureCursor(ltx, lty, NULL, TRUE);
1333    while (placing) {
1334       XNextEvent(mainDisplay, &input);
1335 
1336       if (input.type == Expose || input.type == VisibilityNotify) {
1337          SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1338          ExposeEventHandler(&input, TRUE);
1339          SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1340       } else if (input.type == MotionNotify) {
1341          x = input.xmotion.x;
1342          y = input.xmotion.y;
1343          GridXY(x, y, &grid_x, &grid_y);
1344 
1345          if (grid_x != grid_orig_x+dx || grid_y != grid_orig_y+dy) {
1346             SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1347             dx = grid_x - grid_orig_x;
1348             dy = grid_y - grid_orig_y;
1349             SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1350             MarkRulers(grid_x, grid_y);
1351          }
1352          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
1353       } else if (input.type == ButtonPress) {
1354          XUngrabPointer(mainDisplay, CurrentTime);
1355          XSync(mainDisplay, False);
1356          placing = FALSE;
1357          SelBox(drawWindow, revDefaultGC, ltx+dx, lty+dy, rbx+dx, rby+dy);
1358          EndShowMeasureCursor(ltx+dx, lty+dy, NULL, TRUE);
1359          Msg("");
1360          SetMouseStatus("", "", "");
1361          old_just = text_obj_ptr->detail.t->minilines.just;
1362          switch (input.xbutton.button) {
1363          case Button1: new_just = JUST_L; break;
1364          case Button2: new_just = JUST_C; break;
1365          case Button3: new_just = JUST_R; break;
1366          }
1367          if (old_just != new_just) {
1368             HighLightReverse();
1369             PrepareToReplaceAnObj(ObjPtr);
1370             text_obj_ptr->detail.t->minilines.just = new_just;
1371             /* UpdAttr(AttrPtr); */ /* ALERT - not sure if this is needed. */
1372             AdjObjCache(ObjPtr);
1373             UpdTextBBox(text_obj_ptr);
1374             AdjObjBBox(ObjPtr);
1375             RecordReplaceAnObj(ObjPtr);
1376             return TRUE;
1377          }
1378       }
1379    }
1380    return FALSE;
1381 }
1382 
CreateAttrMenu(parent_menu,x,y,num_items,menu_strings,status_strings,pixels)1383 TgMenu *CreateAttrMenu(parent_menu, x, y, num_items, menu_strings,
1384       status_strings, pixels)
1385    TgMenu *parent_menu;
1386    int x, y, num_items, *pixels;
1387    char **menu_strings, **status_strings;
1388 {
1389    int i=0;
1390    TgMenu *menu=NULL;
1391    TgMenuInfo stMenuInfo;
1392    TgMenuItemInfo *item_info=NULL;
1393 
1394    memset(&stMenuInfo, 0, sizeof(TgMenuInfo));
1395    stMenuInfo.type = TGMUTYPE_TEXT;
1396    stMenuInfo.items = (TgMenuItemInfo*)malloc(
1397          (num_items+1)*sizeof(TgMenuItemInfo));
1398    if (stMenuInfo.items == NULL) FailAllocMessage();
1399    memset(stMenuInfo.items, 0, (num_items+1)*sizeof(TgMenuItemInfo));
1400    for (item_info=stMenuInfo.items, i=0; i < num_items; item_info++, i++) {
1401       if (menu_strings[i] == TGMUITEM_SEPARATOR) {
1402          item_info->menu_str = TGMUITEM_SEPARATOR;
1403       } else {
1404          item_info->menu_str = UtilStrDup(menu_strings[i]);
1405          if (item_info->menu_str == NULL) FailAllocMessage();
1406       }
1407       if (status_strings != NULL && status_strings[i] != NULL) {
1408          item_info->status_str = UtilStrDup(status_strings[i]);
1409          if (item_info->status_str == NULL) FailAllocMessage();
1410       }
1411       item_info->submenu_info = NULL;
1412       item_info->cmdid = INVALID;
1413    }
1414    stMenuInfo.items[num_items].cmdid = INVALID;
1415 
1416    if (num_items >= menuRowsBeforeScroll) {
1417       stMenuInfo.type |= TGMUTYPE_CANSCROLL;
1418    }
1419    /*
1420     * Since these are real attribute names and values, no translation is needed.
1421     */
1422    menu = TgCreateMenuFromMenuInfo(parent_menu, x, y, &stMenuInfo, TRUE);
1423    for (item_info=stMenuInfo.items, i=0; i < num_items; item_info++, i++) {
1424       UtilFree(item_info->status_str);
1425    }
1426    memset(stMenuInfo.items, 0, (num_items+1)*sizeof(TgMenuItemInfo));
1427    free(stMenuInfo.items);
1428    stMenuInfo.items = NULL;
1429    if (menu != NULL) {
1430       TgMenuItem stMenuItem;
1431 
1432       menu->track_menubar = FALSE;
1433       memset(&stMenuItem, 0, sizeof(TgMenuItem));
1434       for (i=0; i < num_items; i++) {
1435          TgMenuItem *menu_item=(&menu->menuitems[i]);
1436 
1437          if (pixels != NULL) {
1438             stMenuItem.multicolor_pixel = pixels[i];
1439             if (!TgSetMenuItemInfo(menu_item, TGMU_MASK_MULTICOLOR,
1440                   &stMenuItem)) {
1441                return TgDestroyMenu(menu, TRUE);
1442             }
1443          }
1444          menu_item->menu_str_allocated = TRUE;
1445       }
1446    }
1447    return menu;
1448 }
1449 
FreeEditAttrInfo(pEditAttrInfo)1450 void FreeEditAttrInfo(pEditAttrInfo)
1451    EditAttrInfo *pEditAttrInfo;
1452    /* pEditAttrInfo will also be freed before returning */
1453 {
1454    int i=0, num_attrs=0, *fore_colors=NULL, *attr_indices=NULL;
1455    char **attr_names=NULL, **attr_values=NULL, **attr_strings=NULL;
1456    char **status_strings=NULL;
1457 
1458    if (pEditAttrInfo == NULL) return;
1459 
1460    num_attrs = pEditAttrInfo->num_attrs;
1461    fore_colors = pEditAttrInfo->fore_colors;
1462    attr_indices = pEditAttrInfo->attr_indices;
1463    attr_names = pEditAttrInfo->attr_names;
1464    attr_values = pEditAttrInfo->attr_values;
1465    attr_strings = pEditAttrInfo->attr_strings;
1466    status_strings = pEditAttrInfo->status_strings;
1467 
1468    if (pEditAttrInfo->fore_colors != NULL) free(pEditAttrInfo->fore_colors);
1469    if (pEditAttrInfo->attr_indices != NULL) free(pEditAttrInfo->attr_indices);
1470 
1471    for (i=0; i < num_attrs; i++) {
1472       if (attr_names != NULL) UtilFree(attr_names[i]);
1473       if (attr_values != NULL) UtilFree(attr_values[i]);
1474       if (attr_strings != NULL) {
1475          if (attr_strings[i] != TGMUITEM_SEPARATOR) UtilFree(attr_strings[i]);
1476       }
1477       if (status_strings != NULL) UtilFree(status_strings[i]);
1478    }
1479    if (attr_names != NULL) free(attr_names);
1480    if (attr_values != NULL) free(attr_values);
1481    if (attr_strings != NULL) free(attr_strings);
1482    if (status_strings != NULL) free(status_strings);
1483 
1484    free(pEditAttrInfo);
1485 }
1486 
HasEditAttrsInContextMenu(obj_ptr,pp_name_attr)1487 int HasEditAttrsInContextMenu(obj_ptr, pp_name_attr)
1488    struct ObjRec *obj_ptr;
1489    struct AttrRec **pp_name_attr;
1490 {
1491    struct AttrRec *attr_ptr=NULL;
1492 
1493    if (pp_name_attr != NULL) *pp_name_attr = NULL;
1494    for (attr_ptr=obj_ptr->lattr; attr_ptr!=NULL; attr_ptr=attr_ptr->prev) {
1495       if (strcmp(attr_ptr->attr_name.s, "edit_attrs_in_context_menu=") == 0) {
1496          if (pp_name_attr != NULL) *pp_name_attr = attr_ptr;
1497          return TRUE;
1498       }
1499    }
1500    return FALSE;
1501 }
1502 
FreeRestrictedAttrNames(ppsz_restricted,num_restricted)1503 void FreeRestrictedAttrNames(ppsz_restricted, num_restricted)
1504    char **ppsz_restricted;
1505    int num_restricted;
1506 {
1507    if (ppsz_restricted != NULL) {
1508       int i=0;
1509 
1510       for (i=0; i < num_restricted; i++) {
1511          if (ppsz_restricted[i] != NULL) {
1512             UtilFree(ppsz_restricted[i]);
1513          }
1514       }
1515       free(ppsz_restricted);
1516    }
1517 }
1518 
GetRestrictedAttrNames(restricted_attr_obj,pppsz_restricted,pn_num_restricted)1519 void GetRestrictedAttrNames(restricted_attr_obj, pppsz_restricted,
1520       pn_num_restricted)
1521    struct ObjRec *restricted_attr_obj;
1522    char ***pppsz_restricted;
1523    int *pn_num_restricted;
1524 {
1525    MiniLinesInfo *minilines=(&restricted_attr_obj->detail.t->minilines);
1526    MiniLineInfo *miniline=minilines->first->next;
1527    char **ppsz_restricted=NULL;
1528    int num_restricted=0, index=0;
1529 
1530    if (pppsz_restricted != NULL) (*pppsz_restricted) = NULL;
1531    if (pn_num_restricted != NULL) (*pn_num_restricted) = 0;
1532    if (miniline == NULL) return;
1533 
1534    for ( ; miniline != NULL; miniline=miniline->next, num_restricted++) {
1535    }
1536    ppsz_restricted = (char**)malloc(num_restricted*sizeof(char*));
1537    if (ppsz_restricted == NULL) FailAllocMessage();
1538    memset(ppsz_restricted, 0, num_restricted*sizeof(char*));
1539 
1540    miniline = minilines->first->next;
1541    for ( ; miniline != NULL; miniline=miniline->next) {
1542       int need_to_free_tmp_buf=FALSE;
1543       char *tmp_buf=ConvertMiniLineToString(miniline, &need_to_free_tmp_buf);
1544 
1545       if (tmp_buf != NULL) {
1546          UtilTrimBlanks(tmp_buf);
1547          if (*tmp_buf == '\0') {
1548             num_restricted--;
1549          } else {
1550             int len=strlen(tmp_buf)+2; /* extra char for '=' */
1551 
1552             ppsz_restricted[index] = (char*)malloc((len+1)*sizeof(char));
1553             if (ppsz_restricted[index] == NULL) FailAllocMessage();
1554             sprintf(ppsz_restricted[index], "%s=", tmp_buf);
1555             index++;
1556          }
1557          if (need_to_free_tmp_buf) UtilFree(tmp_buf);
1558       } else {
1559          num_restricted--;
1560       }
1561       if (num_restricted <= 0) break;
1562    }
1563    if (num_restricted <= 0) {
1564       free(pppsz_restricted);
1565    } else {
1566       if (pppsz_restricted != NULL) (*pppsz_restricted) = ppsz_restricted;
1567       if (pn_num_restricted != NULL) (*pn_num_restricted) = num_restricted;
1568    }
1569 }
1570 
GetAttrGroupAttrNames(restricted_attr_obj,group_index,pppsz_restricted,pn_num_restricted)1571 void GetAttrGroupAttrNames(restricted_attr_obj, group_index, pppsz_restricted,
1572       pn_num_restricted)
1573    struct ObjRec *restricted_attr_obj;
1574    int group_index, *pn_num_restricted;
1575    char ***pppsz_restricted;
1576 {
1577    char **ppsz_restricted=NULL, **attr_name_array=NULL;
1578    int num_attrs_in_attr_group=0, i=0;
1579 
1580    num_attrs_in_attr_group = gAttrGroupInfo[group_index]->num_attrs;
1581    attr_name_array = gAttrGroupInfo[group_index]->attr_name;
1582 
1583    if (pppsz_restricted != NULL) (*pppsz_restricted) = NULL;
1584    if (pn_num_restricted != NULL) (*pn_num_restricted) = 0;
1585 
1586    ppsz_restricted = (char**)malloc(num_attrs_in_attr_group*sizeof(char*));
1587    if (ppsz_restricted == NULL) FailAllocMessage();
1588    memset(ppsz_restricted, 0, num_attrs_in_attr_group*sizeof(char*));
1589 
1590    for (i=0; i < num_attrs_in_attr_group; i++) {
1591       int len=strlen(attr_name_array[i])+2; /* extra char for '=' */
1592 
1593       ppsz_restricted[i] = (char*)malloc((len+1)*sizeof(char));
1594       if (ppsz_restricted[i] == NULL) FailAllocMessage();
1595       sprintf(ppsz_restricted[i], "%s=", attr_name_array[i]);
1596    }
1597    if (pppsz_restricted != NULL) (*pppsz_restricted) = ppsz_restricted;
1598    if (pn_num_restricted != NULL) {
1599       (*pn_num_restricted) = num_attrs_in_attr_group;
1600    }
1601 }
1602 
IsRestrictedAttr(attr_name,ppsz_restricted,num_restricted)1603 int IsRestrictedAttr(attr_name, ppsz_restricted, num_restricted)
1604    char *attr_name, **ppsz_restricted;
1605    int num_restricted;
1606 {
1607    int i=0;
1608 
1609    for (i=0; i < num_restricted; i++) {
1610       if (strcmp(attr_name, ppsz_restricted[i]) == 0) {
1611          return TRUE;
1612       }
1613    }
1614    return FALSE;
1615 }
1616 
CreateEditAttrInfo(obj_ptr)1617 EditAttrInfo *CreateEditAttrInfo(obj_ptr)
1618    struct ObjRec *obj_ptr;
1619 {
1620    int i=0, num_attrs=0, *fore_colors=NULL, *pixel_ptr=NULL, restricted=FALSE;
1621    int num_restricted=0, num_created=0, *attr_indices=NULL;
1622    struct AttrRec *attr_ptr=NULL, *attr_ptr1=NULL, *restricted_attr=NULL;
1623    EditAttrInfo *pEditAttrInfo=NULL;
1624    char **attr_names=NULL, **attr_values=NULL, **attr_strings=NULL;
1625    char **status_strings=NULL, **ppsz_restricted=NULL;
1626 
1627    pEditAttrInfo = (EditAttrInfo*)malloc(sizeof(EditAttrInfo));
1628    if (pEditAttrInfo == NULL) FailAllocMessage();
1629    memset(pEditAttrInfo, 0, sizeof(EditAttrInfo));
1630 
1631    attr_ptr1 = attr_ptr = obj_ptr->fattr;
1632    for ( ; attr_ptr1 != NULL; attr_ptr1=attr_ptr1->next, num_attrs++) {
1633    }
1634    if (num_attrs == 0) {
1635       free(pEditAttrInfo);
1636       return NULL;
1637    }
1638    restricted = HasEditAttrsInContextMenu(obj_ptr, &restricted_attr);
1639    if (restricted) {
1640       GetRestrictedAttrNames(restricted_attr->obj, &ppsz_restricted,
1641             &num_restricted);
1642       if (ppsz_restricted == NULL || num_restricted <= 0) {
1643          free(pEditAttrInfo);
1644          return NULL;
1645       }
1646    }
1647    attr_names = (char**)malloc(num_attrs*sizeof(char*));
1648    attr_values = (char**)malloc(num_attrs*sizeof(char*));
1649    attr_strings = (char**)malloc(num_attrs*sizeof(char*));
1650    status_strings = (char**)malloc(num_attrs*sizeof(char*));
1651    fore_colors = pixel_ptr = (int*)malloc(num_attrs*sizeof(int));
1652    attr_indices = (int*)malloc(num_attrs*sizeof(int));
1653    if (attr_names == NULL || attr_values == NULL || attr_strings == NULL ||
1654          status_strings == NULL || fore_colors == NULL ||
1655          attr_indices == NULL) {
1656       FailAllocMessage();
1657    }
1658    attr_ptr1 = attr_ptr;
1659    for (i=0; i < num_attrs; i++, attr_ptr1=attr_ptr1->next) {
1660       int need_to_free_tmp_buf=FALSE;
1661       char *tmp_buf=NULL;
1662       MiniLinesInfo *minilines=(&attr_ptr1->obj->detail.t->minilines);
1663 
1664       tmp_buf = ConvertAttrNameFirstMiniLineToString(attr_ptr1,
1665             &need_to_free_tmp_buf);
1666 
1667       if (restricted) {
1668          if (!IsRestrictedAttr(attr_ptr1->attr_name.s, ppsz_restricted,
1669                num_restricted)) {
1670             if (need_to_free_tmp_buf) UtilFree(tmp_buf);
1671             continue;
1672          }
1673       }
1674       attr_names[num_created] = UtilStrDup(attr_ptr1->attr_name.s);
1675       attr_values[num_created] = UtilStrDup(attr_ptr1->attr_value.s);
1676       attr_strings[num_created] = UtilStrDup(tmp_buf);
1677       sprintf(gszMsgBox, TgLoadCachedString(CSTID_EDIT_ATTR_IN_EDITOR),
1678             tmp_buf);
1679       status_strings[num_created] = UtilStrDup(gszMsgBox);
1680       if (attr_names[num_created] == NULL || attr_values[num_created] == NULL ||
1681             attr_strings[num_created] == NULL ||
1682             status_strings[num_created] == NULL) {
1683          FailAllocMessage();
1684       }
1685       pixel_ptr[num_created] =
1686             colorPixels[minilines->first->first_block->seg->color];
1687       attr_indices[num_created] = i;
1688 
1689       if (need_to_free_tmp_buf) UtilFree(tmp_buf);
1690       num_created++;
1691    }
1692    pEditAttrInfo->num_attrs = num_created;
1693    pEditAttrInfo->fore_colors = fore_colors;
1694    pEditAttrInfo->attr_indices = attr_indices;
1695    pEditAttrInfo->attr_names = attr_names;
1696    pEditAttrInfo->attr_values = attr_values;
1697    pEditAttrInfo->attr_strings = attr_strings;
1698    pEditAttrInfo->status_strings = status_strings;
1699 
1700    FreeRestrictedAttrNames(ppsz_restricted, num_restricted);
1701 
1702    return pEditAttrInfo;
1703 }
1704 
CreateEditAttrGroupInfo(obj_ptr)1705 EditAttrInfo *CreateEditAttrGroupInfo(obj_ptr)
1706    struct ObjRec *obj_ptr;
1707 {
1708    int i=0, num_attrs=maxAttrGroups, *fore_colors=NULL;
1709    EditAttrInfo *pEditAttrInfo=NULL;
1710    char **attr_strings=NULL, **status_strings=NULL;
1711 
1712    if (num_attrs == 0) {
1713       return NULL;
1714    }
1715    pEditAttrInfo = (EditAttrInfo*)malloc(sizeof(EditAttrInfo));
1716    if (pEditAttrInfo == NULL) FailAllocMessage();
1717    memset(pEditAttrInfo, 0, sizeof(EditAttrInfo));
1718 
1719    /*
1720     * For this function, we do not need to call HasEditAttrsInContextMenu()
1721     *     because the "edit_attrs_in_context_menu=" attribute is only for the
1722     *     "Edit Attribute In Editor" submenu and *not* for the
1723     *     "Edit Attribute Group In Editor" submenu.
1724     */
1725    attr_strings = (char**)malloc(num_attrs*sizeof(char*));
1726    status_strings = (char**)malloc(num_attrs*sizeof(char*));
1727    fore_colors = (int*)malloc(num_attrs*sizeof(int));
1728 
1729    if (attr_strings == NULL || status_strings == NULL || fore_colors == NULL) {
1730       FailAllocMessage();
1731    }
1732    for (i=0; i < num_attrs; i++) {
1733       attr_strings[i] = UtilStrDup(gAttrGroupInfo[i]->group_name);
1734       sprintf(gszMsgBox, TgLoadCachedString(CSTID_EDIT_ATTR_GROUP_IN_EDITOR),
1735             gAttrGroupInfo[i]->group_name, gAttrGroupInfo[i]->displayed_names);
1736       status_strings[i] = UtilStrDup(gszMsgBox);
1737 
1738       if (attr_strings[i] == NULL || status_strings[i] == NULL) {
1739          FailAllocMessage();
1740       }
1741       fore_colors[i] = myFgPixel;
1742    }
1743    pEditAttrInfo->num_attrs = num_attrs;
1744    pEditAttrInfo->attr_strings = attr_strings;
1745    pEditAttrInfo->status_strings = status_strings;
1746    pEditAttrInfo->fore_colors = fore_colors;
1747 
1748    return pEditAttrInfo;
1749 }
1750 
MoveAttr()1751 void MoveAttr()
1752 {
1753    struct ObjRec *obj_ptr=NULL;
1754    struct AttrRec *attr_ptr=NULL, *attr_ptr1=NULL;
1755    int num_attrs=0, i, index=INVALID, x, y;
1756    int sel_ltx, sel_lty, sel_rbx, sel_rby;
1757    int *fore_colors=NULL, *pixel_ptr=NULL;
1758    char **attr_strings=NULL;
1759    unsigned int button;
1760 
1761    if (topSel != botSel || topSel == NULL) {
1762       MsgBox(TgLoadString(STID_SELECT_ONLY_ONE_OBJ), TOOL_NAME, INFO_MB);
1763       return;
1764    }
1765    obj_ptr = topSel->obj;
1766    attr_ptr1 = attr_ptr = obj_ptr->fattr;
1767    for ( ; attr_ptr1 != NULL; attr_ptr1=attr_ptr1->next, num_attrs++) {
1768    }
1769    if (num_attrs == 0) {
1770       MsgBox(TgLoadString(STID_SELECT_HAS_NO_ATTR), TOOL_NAME, INFO_MB);
1771       return;
1772    }
1773    attr_strings = (char**)malloc(num_attrs*sizeof(char*));
1774    fore_colors = pixel_ptr = (int*)malloc(num_attrs*sizeof(int));
1775    if (attr_strings == NULL || fore_colors == NULL) {
1776       FailAllocMessage();
1777    }
1778    attr_ptr1 = attr_ptr;
1779    for (i=0; i < num_attrs; i++, attr_ptr1=attr_ptr1->next) {
1780       int need_to_free_tmp_buf=FALSE;
1781       char *tmp_buf=NULL;
1782       MiniLinesInfo *minilines=(&attr_ptr1->obj->detail.t->minilines);
1783 
1784       tmp_buf = ConvertAttrNameFirstMiniLineToString(attr_ptr1,
1785             &need_to_free_tmp_buf);
1786       if ((attr_strings[i]=UtilStrDup(tmp_buf)) == NULL) FailAllocMessage();
1787 
1788       *pixel_ptr++ = colorPixels[minilines->first->first_block->seg->color];
1789 
1790       if (need_to_free_tmp_buf) UtilFree(tmp_buf);
1791    }
1792 
1793    Msg(TgLoadString(STID_DRAG_LEFT_BTN_TO_SEE_ATTR));
1794    SetMouseStatus(TgLoadString(STID_SHOW_MOVE_ATTR),
1795          TgLoadString(STID_CHANGE_ATTR_JUST),
1796          TgLoadCachedString(CSTID_PARANED_NONE));
1797    button = CornerLoop(&x, &y);
1798    activeMenu = INVALID;
1799    if (button == Button1 || button == Button2) {
1800       TgMenu *menu=CreateAttrMenu(NULL, x, y, num_attrs, attr_strings, NULL,
1801             fore_colors);
1802 
1803       if (menu != NULL) {
1804          index = TgMenuLoop(menu);
1805          TgDestroyMenu(menu, TRUE);
1806       }
1807       free(fore_colors);
1808    }
1809    if (index != INVALID) {
1810       attr_ptr1 = attr_ptr;
1811       for (i=0; i < index; i++, attr_ptr1=attr_ptr1->next) {
1812       }
1813       sel_ltx = selLtX; sel_lty = selLtY; sel_rbx = selRbX; sel_rby = selRbY;
1814       if (button == Button1) {
1815          if (MoveOneAttr(obj_ptr, attr_ptr1)) {
1816             /* HighLightReverse() is expected to be called */
1817             UpdSelBBox();
1818             RedrawAreas(botObj, sel_ltx-GRID_ABS_SIZE(1),
1819                   sel_lty-GRID_ABS_SIZE(1),
1820                   sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
1821                   selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
1822                   selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
1823             SetFileModified(TRUE);
1824             HighLightForward();
1825          }
1826       } else if (button == Button2) {
1827          if (ChangeAttrJust(obj_ptr, attr_ptr1)) {
1828             /* HighLightReverse() is expected to be called */
1829             UpdSelBBox();
1830             RedrawAreas(botObj, sel_ltx-GRID_ABS_SIZE(1),
1831                   sel_lty-GRID_ABS_SIZE(1), sel_rbx+GRID_ABS_SIZE(1),
1832                   sel_rby+GRID_ABS_SIZE(1), selLtX-GRID_ABS_SIZE(1),
1833                   selLtY-GRID_ABS_SIZE(1), selRbX+GRID_ABS_SIZE(1),
1834                   selRbY+GRID_ABS_SIZE(1));
1835             SetFileModified(TRUE);
1836             HighLightForward();
1837          }
1838       }
1839    }
1840    for (i=0; i < num_attrs; i++) UtilFree(attr_strings[i]);
1841    free(attr_strings);
1842    justDupped = FALSE;
1843 }
1844 
CopyAndUpdateAttrs(ToObjPtr,FromObjPtr)1845 void CopyAndUpdateAttrs(ToObjPtr, FromObjPtr)
1846    struct ObjRec *ToObjPtr, *FromObjPtr;
1847 {
1848    struct AttrRec *to_attr_ptr, *from_attr_ptr;
1849    int confirm_status=MB_ID_CANCEL;
1850 
1851    topAttr = botAttr = NULL;
1852    from_attr_ptr = FromObjPtr->fattr;
1853    for ( ; from_attr_ptr != NULL; from_attr_ptr=from_attr_ptr->next) {
1854       to_attr_ptr = ToObjPtr->fattr;
1855       for ( ; to_attr_ptr != NULL; to_attr_ptr=to_attr_ptr->next) {
1856          if (from_attr_ptr->obj->color==to_attr_ptr->obj->color &&
1857                strcmp(from_attr_ptr->attr_name.s,
1858                to_attr_ptr->attr_name.s)==0) {
1859             if (*(from_attr_ptr->attr_value.s) != '\0') {
1860                DynStrCpy(&to_attr_ptr->attr_value, &from_attr_ptr->attr_value);
1861                UpdAttr(to_attr_ptr);
1862             }
1863             break;
1864          }
1865       }
1866       if (to_attr_ptr == NULL) {
1867          /* cannot find the same attribute in the new one */
1868          confirm_status = MB_ID_NO;
1869 
1870          if (from_attr_ptr->inherited) {
1871             char msg[MAXSTRING];
1872 
1873             /* obsolete attribute? */
1874             confirm_status = (dropObsIconAttrWhenUpdate) ? MB_ID_YES :
1875                   MB_ID_CANCEL;
1876             if (*from_attr_ptr->attr_name.s == '\0') {
1877                sprintf(msg, TgLoadString(STID_IS_ATTR_OBSOLETE_FOR_ICON_YNC),
1878                      from_attr_ptr->attr_value.s, FromObjPtr->detail.r->s);
1879             } else {
1880                sprintf(msg, TgLoadString(STID_IS_ATTR_OBSOLETE_FOR_ICON_YNC),
1881                      from_attr_ptr->attr_name.s, FromObjPtr->detail.r->s);
1882             }
1883             while (confirm_status == MB_ID_CANCEL) {
1884                confirm_status = MsgBox(msg, TOOL_NAME, YN_MB);
1885                if (confirm_status == MB_ID_CANCEL) {
1886                   MsgBox(TgLoadCachedString(CSTID_CANCEL_NOT_AVAIL), TOOL_NAME,
1887                         INFO_MB);
1888                }
1889             }
1890          }
1891          if (confirm_status == MB_ID_NO) {
1892             /* new attribute */
1893             to_attr_ptr = (struct AttrRec *)malloc(sizeof(struct AttrRec));
1894             if (to_attr_ptr == NULL) FailAllocMessage();
1895             memset(to_attr_ptr, 0, sizeof(struct AttrRec));
1896             to_attr_ptr->owner = ToObjPtr;
1897             DupAnAttr(from_attr_ptr, to_attr_ptr);
1898             LinkInAttr((struct AttrRec *)NULL, topAttr, to_attr_ptr);
1899          }
1900       } else {
1901          to_attr_ptr->shown = from_attr_ptr->shown;
1902          to_attr_ptr->nameshown = from_attr_ptr->nameshown;
1903       }
1904    }
1905    if (topAttr != NULL) {
1906       topAttr->prev = NULL;
1907       botAttr->next = ToObjPtr->fattr;
1908 
1909       if (ToObjPtr->fattr != NULL) ToObjPtr->fattr->prev = botAttr;
1910       ToObjPtr->fattr = topAttr;
1911       if (ToObjPtr->lattr == NULL) ToObjPtr->lattr = botAttr;
1912    }
1913    AdjObjBBox(ToObjPtr);
1914 }
1915 
ToggleNamedAttrShown(attr_name)1916 void ToggleNamedAttrShown(attr_name)
1917    char *attr_name;
1918 {
1919    char *paren_ptr;
1920    struct AttrRec *attr_ptr;
1921    int ltx, lty, rbx, rby;
1922 
1923    if (topSel != botSel || topSel == NULL) {
1924       MsgBox(TgLoadString(STID_SELECT_ONLY_ONE_OBJ), TOOL_NAME, INFO_MB);
1925       return;
1926    }
1927    if ((paren_ptr=strchr(attr_name, ')')) == NULL) {
1928       MsgBox(TgLoadString(STID_BAD_FORMAT_IN_SHORTCUT_SPEC), TOOL_NAME,
1929             INFO_MB);
1930       return;
1931    }
1932    *paren_ptr = '\0';
1933    if ((attr_ptr=FindAttrWithName(topSel->obj, attr_name, NULL)) == NULL) {
1934       char msg[MAXSTRING+1];
1935 
1936       sprintf(msg, TgLoadString(STID_CANNOT_FIND_NAMED_ATTR), attr_name);
1937       MsgBox(msg, TOOL_NAME, INFO_MB);
1938       return;
1939    }
1940    ltx = selLtX; lty = selLtY; rbx = selRbX; rby = selRbY;
1941    HighLightReverse();
1942    PrepareToReplaceAnObj(topSel->obj);
1943    attr_ptr->shown = !attr_ptr->shown;
1944    AdjObjBBox(topSel->obj);
1945    RecordReplaceAnObj(topSel->obj);
1946    UpdSelBBox();
1947    RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
1948          rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
1949          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
1950          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
1951    SetFileModified(TRUE);
1952    HighLightForward();
1953 }
1954 
ToggleEqAttrShown()1955 void ToggleEqAttrShown()
1956 {
1957    char buf[80];
1958 
1959    strcpy(buf, "eq=)");
1960    ToggleNamedAttrShown(buf);
1961 }
1962 
AddFileAttrs()1963 void AddFileAttrs()
1964 {
1965    struct SelRec *sel_ptr;
1966    int ltx, lty, rbx, rby;
1967 
1968    if (gstWBInfo.do_whiteboard) {
1969       /*
1970        * Now it's allowed.
1971        *
1972        * MsgBox(TgLoadString(STID_ATTACH_FILE_ATTRS_IN_WB), TOOL_NAME, INFO_MB);
1973        * return;
1974        */
1975    }
1976    if (topSel == NULL) {
1977       MsgBox(TgLoadString(STID_SELECT_AT_LEAST_ONE_TEXT), TOOL_NAME, INFO_MB);
1978       return;
1979    }
1980    for (sel_ptr=topSel; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
1981       if (sel_ptr->obj->type != OBJ_TEXT) {
1982          MsgBox(TgLoadString(STID_CANNOT_ATTACH_NON_TEXT_OBJ), TOOL_NAME,
1983                INFO_MB);
1984          return;
1985       }
1986    }
1987 
1988    ltx = selLtX; lty = selLtY; rbx = selRbX; rby = selRbY;
1989 
1990    HighLightReverse();
1991    AddObj(NULL, topObj, tgifObj);
1992    AddNewSelObj(topObj);
1993    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
1994 
1995    for (sel_ptr=topSel->next; sel_ptr!=NULL; sel_ptr=sel_ptr->next) {
1996       AddAttr(topObj, sel_ptr->obj);
1997    }
1998    RemoveAllSel();
1999    AddNewSelObj(topObj);
2000    UpdSelBBox();
2001 
2002    recordCmdIncludeTgifObj = TRUE;
2003    RecordCmd(CMD_MANY_TO_ONE, NULL, topSel, botSel, 1);
2004    recordCmdIncludeTgifObj = FALSE;
2005    RemoveAllSel();
2006    UnlinkObj(topObj);
2007    RedrawAnArea(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
2008          rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1));
2009    SetFileModified(TRUE);
2010    justDupped = FALSE;
2011 }
2012 
DetachFileAttrs()2013 void DetachFileAttrs()
2014 {
2015    struct AttrRec *attr_ptr, *next_attr;
2016    struct SelRec *sel_ptr;
2017 
2018    if (gstWBInfo.do_whiteboard) {
2019       /*
2020        * Now it's allowed.
2021        *
2022        * MsgBox(TgLoadString(STID_DETACH_FILE_ATTRS_IN_WB), TOOL_NAME, INFO_MB);
2023        * return;
2024        */
2025    }
2026    if (tgifObj->fattr == NULL) {
2027       MsgBox(TgLoadString(STID_FILE_CONTAINS_NO_ATTRS), TOOL_NAME, INFO_MB);
2028       return;
2029    }
2030    MakeQuiescent();
2031 
2032    AddObj(NULL, topObj, tgifObj);
2033    AddNewSelObj(topObj);
2034    PrepareToReplaceAnObj(topObj);
2035 
2036    for (attr_ptr=topObj->fattr; attr_ptr!=NULL; attr_ptr=next_attr) {
2037       next_attr = attr_ptr->next;
2038 
2039       sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
2040       if (sel_ptr == NULL) FailAllocMessage();
2041       sel_ptr->obj = attr_ptr->obj;
2042       AddSel(topSel, topSel->next, sel_ptr);
2043 
2044       attr_ptr->obj->detail.t->attr = NULL;
2045       AddObj(topObj, topObj->next, attr_ptr->obj);
2046       FreeAttr(attr_ptr);
2047    }
2048    topObj->fattr = topObj->lattr = NULL;
2049    recordCmdIncludeTgifObj = TRUE;
2050    RecordCmd(CMD_ONE_TO_MANY, NULL, topSel, botSel, numObjSelected);
2051    recordCmdIncludeTgifObj = FALSE;
2052 
2053    UnlinkObj(topObj);
2054    FreeTopSel();
2055 
2056    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
2057          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
2058    HighLightForward();
2059    SetFileModified(TRUE);
2060    justDupped = FALSE;
2061    SetCurChoice(curChoiceBeforeMakeQuiescent);
2062 }
2063 
2064 #define SHOWN_COL  0
2065 #define NSHOWN_COL 1
2066 
2067 static DspList *fileAttrNameDspPtr=NULL;
2068 
2069 static
FileAttrNameListing(ObjPtr,pn_entries,pCheckArray)2070 DspList *FileAttrNameListing(ObjPtr, pn_entries, pCheckArray)
2071    /*
2072     * If pCheckArray != NULL, this routine sets pCheckArray->num_cols,
2073     *     pCheckArray->num_rows and pCheckArray->value.
2074     */
2075    struct ObjRec *ObjPtr;
2076    int *pn_entries;
2077    struct CheckArrayRec *pCheckArray;
2078 {
2079    int i, num_rows=0;
2080    DspList *dsp_ptr;
2081    struct AttrRec *attr_ptr;
2082 
2083    for (num_rows=0, attr_ptr=ObjPtr->fattr; attr_ptr!=NULL;
2084          attr_ptr=attr_ptr->next, num_rows++) {
2085    }
2086    if (pCheckArray != NULL) {
2087       pCheckArray->num_cols = 2;
2088       pCheckArray->num_rows = num_rows;
2089       pCheckArray->value = (int**)malloc(2*sizeof(int*));
2090       if (pCheckArray->value == NULL) FailAllocMessage();
2091       memset(pCheckArray->value, 0, 2*sizeof(int*));
2092       pCheckArray->value[SHOWN_COL] = (int*)malloc(num_rows*sizeof(int));
2093       pCheckArray->value[NSHOWN_COL] = (int*)malloc(num_rows*sizeof(int));
2094       if (pCheckArray->value[SHOWN_COL] == NULL ||
2095             pCheckArray->value[NSHOWN_COL] == NULL) {
2096          FailAllocMessage();
2097       }
2098       memset(pCheckArray->value[SHOWN_COL], 0, num_rows*sizeof(int));
2099       memset(pCheckArray->value[NSHOWN_COL], 0, num_rows*sizeof(int));
2100    }
2101    fileAttrNameDspPtr = (DspList*)malloc(num_rows*sizeof(DspList));
2102    if (fileAttrNameDspPtr == NULL) FailAllocMessage();
2103    memset(fileAttrNameDspPtr, 0, num_rows*sizeof(DspList));
2104    for (i=1, dsp_ptr=fileAttrNameDspPtr, attr_ptr=ObjPtr->fattr;
2105          i<=num_rows; i++, dsp_ptr++, attr_ptr=attr_ptr->next) {
2106       if (*attr_ptr->attr_name.s == '\0') {
2107          UtilStrCpyN(dsp_ptr->itemstr, sizeof(dsp_ptr->itemstr),
2108                attr_ptr->attr_value.s);
2109       } else {
2110          sprintf(gszMsgBox, "%s%s", attr_ptr->attr_name.s,
2111                attr_ptr->attr_value.s);
2112          UtilStrCpyN(dsp_ptr->itemstr, sizeof(dsp_ptr->itemstr), gszMsgBox);
2113       }
2114       /* use the directory field for inherited */
2115       dsp_ptr->directory = attr_ptr->inherited;
2116       dsp_ptr->next = ((i == num_rows) ? NULL : &dsp_ptr[1]);
2117       if (pCheckArray != NULL) {
2118          if (attr_ptr->shown) pCheckArray->value[SHOWN_COL][i-1] = TRUE;
2119          if (attr_ptr->nameshown) pCheckArray->value[NSHOWN_COL][i-1] = TRUE;
2120       }
2121    }
2122    *pn_entries = num_rows;
2123    return fileAttrNameDspPtr;
2124 }
2125 
2126 static
EditAttrCheckUpdate(pp_dsp_ptr,ppsz_entries,pn_num_entries,pn_marked_index,pp_check_array,cur_buf,col,row,p_void)2127 int EditAttrCheckUpdate(pp_dsp_ptr, ppsz_entries, pn_num_entries,
2128       pn_marked_index, pp_check_array, cur_buf, col, row, p_void)
2129    DspList **pp_dsp_ptr;
2130    char ***ppsz_entries, *cur_buf;
2131    int *pn_num_entries, *pn_marked_index, col, row;
2132    struct CheckArrayRec **pp_check_array;
2133    void *p_void;
2134 {
2135    char *psz=NULL, saved_ch='\0', *buf=NULL;
2136    int shown=FALSE, nameshown=FALSE;
2137 
2138    if (ppsz_entries == NULL || *ppsz_entries == NULL ||
2139          (*ppsz_entries)[row] == NULL || pp_check_array == NULL ||
2140          (*pp_check_array) == NULL || (*pp_check_array)->value == NULL) {
2141       return FALSE;
2142    }
2143    buf = (*ppsz_entries)[row];
2144    shown = (*pp_check_array)->value[SHOWN_COL][row];
2145    nameshown = (*pp_check_array)->value[NSHOWN_COL][row];
2146 
2147    psz = strchr(buf, '=');
2148    if (psz != NULL) {
2149       saved_ch = *psz;
2150       *psz = '\0';
2151       if (shown) {
2152          if (nameshown) {
2153             sprintf(gszMsgBox, TgLoadString(STID_SHOW_ATTR_NAME_AND_VALUE),
2154                   buf);
2155          } else {
2156             sprintf(gszMsgBox, TgLoadString(STID_SHOW_ATTR_VALUE_ONLY), buf);
2157          }
2158       } else {
2159          sprintf(gszMsgBox, TgLoadString(STID_NOT_SHOW_ATTR), buf);
2160       }
2161       *psz = saved_ch;
2162    } else {
2163       if (shown) {
2164          sprintf(gszMsgBox, TgLoadString(STID_SHOW_ATTR), buf);
2165       } else {
2166          sprintf(gszMsgBox, TgLoadString(STID_WONT_SHOW_VALUE), buf);
2167       }
2168    }
2169    SetStringStatus(gszMsgBox);
2170 
2171    return TRUE;
2172 }
2173 
2174 static
EditAttrNames(TopStr,ObjAttrDspList,entries,num_entries,pCheckArray)2175 int EditAttrNames(TopStr, ObjAttrDspList, entries, num_entries, pCheckArray)
2176    char *TopStr, **entries;
2177    DspList *ObjAttrDspList;
2178    int num_entries;
2179    struct CheckArrayRec *pCheckArray;
2180 {
2181    char win_name[128];
2182 
2183    sprintf(win_name, TgLoadString(STID_TGIF_EDIT_ATTR_NAMES), TOOL_NAME);
2184    ResetNamesInfo();
2185    NamesSetTitle(TopStr);
2186    NamesAddButton(TgLoadCachedString(CSTID_OK), BUTTON_OK);
2187    NamesAddButton(TgLoadCachedString(CSTID_CANCEL), BUTTON_CANCEL);
2188    /* ignore double-click and <CR> */
2189    NamesSetDefaultBtnId(BUTTON_OK, INVALID);
2190    NamesSetStyle(NAMES_EDIT_ATTR, NAMES_LOOP_ONCE);
2191    if (pCheckArray != NULL) {
2192       NamesSetCallback(NULL, NULL, (CheckUpdateFunc*)EditAttrCheckUpdate);
2193    }
2194    NamesSetEntries(ObjAttrDspList, entries, num_entries, pCheckArray,
2195          TRUE, INVALID, 0);
2196    return (Names(win_name, NULL, NULL, 0, NULL)==BUTTON_OK);
2197 }
2198 
BlankStr(s)2199 int BlankStr(s)
2200    char *s;
2201 {
2202    while (*s == ' ') s++;
2203    return (*s == '\0');
2204 }
2205 
EditFileAttrs()2206 void EditFileAttrs()
2207 {
2208    int i, num_entries=0;
2209    DspList *dsp_ptr;
2210    char **entries=NULL;
2211 
2212    if (gstWBInfo.do_whiteboard) {
2213       /*
2214        * Now it's allowed.
2215        *
2216        * MsgBox(TgLoadString(STID_EDIT_FILE_ATTRS_IN_WB), TOOL_NAME, INFO_MB);
2217        * return;
2218        */
2219    }
2220    if (tgifObj->fattr == NULL) {
2221       MsgBox(TgLoadString(STID_FILE_CONTAINS_NO_ATTRS), TOOL_NAME, INFO_MB);
2222       return;
2223    }
2224    MakeQuiescent();
2225 
2226    dsp_ptr = FileAttrNameListing(tgifObj, &num_entries, NULL);
2227    ignoreDirectoryFlag = TRUE;
2228    entries = MakeNameDspItemArray(num_entries, dsp_ptr);
2229    ignoreDirectoryFlag = FALSE;
2230    if (EditAttrNames(TgLoadString(STID_EDIT_FILE_ATTRS_DOTS), NULL, entries,
2231          num_entries, NULL)) {
2232       int modified=FALSE;
2233       struct AttrRec *attr_ptr, *next_attr;
2234 
2235       AddObj(NULL, topObj, tgifObj);
2236       AddNewSelObj(topObj);
2237       PrepareToReplaceAnObj(topObj);
2238 
2239       for (attr_ptr=tgifObj->fattr, i=0; attr_ptr!=NULL;
2240             attr_ptr=next_attr, i++) {
2241          int blank_str=BlankStr(entries[i]);
2242          struct TextRec *text_ptr=attr_ptr->obj->detail.t;
2243 
2244          next_attr = attr_ptr->next;
2245          if ((blank_str &&
2246                text_ptr->minilines.first==text_ptr->minilines.last) ||
2247                strcmp(GetTextPtrFirstStrSeg(text_ptr)->dyn_str.s,
2248                entries[i]) != 0) {
2249             modified = TRUE;
2250             if (blank_str &&
2251                   text_ptr->minilines.first==text_ptr->minilines.last) {
2252                UnlinkAttr(attr_ptr);
2253                FreeTextObj(attr_ptr->obj);
2254                FreeAttr(attr_ptr);
2255             } else {
2256                DynStrSet(&GetTextPtrFirstStrSeg(text_ptr)->dyn_str,
2257                      entries[i]);
2258                UpdateAttr(text_ptr, attr_ptr);
2259             }
2260          }
2261       }
2262       if (modified) {
2263          recordCmdIncludeTgifObj = TRUE;
2264          RecordReplaceAnObj(topObj);
2265          recordCmdIncludeTgifObj = FALSE;
2266 
2267          RemoveAllSel();
2268          UnlinkObj(topObj);
2269 
2270          SetFileModified(TRUE);
2271          Msg(TgLoadString(STID_FILE_ATTRS_UPDATED));
2272       } else {
2273          AbortPrepareCmd(CMD_REPLACE);
2274 
2275          RemoveAllSel();
2276          UnlinkObj(topObj);
2277       }
2278    }
2279    free(dsp_ptr);
2280    free(*entries);
2281    free(entries);
2282    fileAttrNameDspPtr = NULL;
2283    Msg("");
2284    SetCurChoice(curChoiceBeforeMakeQuiescent);
2285 }
2286 
EditAttrs()2287 void EditAttrs()
2288 {
2289    int i, num_entries=0;
2290    DspList *dsp_ptr;
2291    char **entries=NULL;
2292    struct CheckArrayRec check_array, orig_check_array;
2293 
2294    if (topSel == NULL || topSel != botSel) {
2295       MsgBox(TgLoadString(STID_SELECT_ONE_OBJ_FOR_EDITATTRS), TOOL_NAME,
2296             INFO_MB);
2297       return;
2298    }
2299    if (topSel->obj->fattr == NULL) {
2300       MsgBox(TgLoadString(STID_OBJ_CONTAINS_NO_ATTRS), TOOL_NAME, INFO_MB);
2301       return;
2302    }
2303    memset(&check_array, 0, sizeof(struct CheckArrayRec));
2304    memset(&orig_check_array, 0, sizeof(struct CheckArrayRec));
2305    dsp_ptr = FileAttrNameListing(topSel->obj, &num_entries, &check_array);
2306    ignoreDirectoryFlag = TRUE;
2307    entries = MakeNameDspItemArray(num_entries, dsp_ptr);
2308    ignoreDirectoryFlag = FALSE;
2309    CopyCheckArray(&orig_check_array, &check_array);
2310    if (EditAttrNames(TgLoadString(STID_EDIT_OBJ_ATTRS_DOTS), dsp_ptr, entries,
2311          num_entries, &check_array)) {
2312       int modified=FALSE, sel_ltx, sel_lty, sel_rbx, sel_rby;
2313       struct AttrRec *attr_ptr, *next_attr;
2314 
2315       sel_ltx = selLtX; sel_lty = selLtY; sel_rbx = selRbX; sel_rby = selRbY;
2316       HighLightReverse();
2317       PrepareToReplaceAnObj(topSel->obj);
2318 
2319       for (attr_ptr=topSel->obj->fattr, i=0; attr_ptr!=NULL;
2320             attr_ptr=next_attr, i++) {
2321          int blank_str=BlankStr(entries[i]);
2322          char orig_str[MAXPATHLENGTH+1];
2323          struct TextRec *text_ptr=attr_ptr->obj->detail.t;
2324 
2325          next_attr = attr_ptr->next;
2326          if (*attr_ptr->attr_name.s == '\0') {
2327             strcpy(orig_str, attr_ptr->attr_value.s);
2328          } else {
2329             sprintf(orig_str, "%s%s", attr_ptr->attr_name.s,
2330                   attr_ptr->attr_value.s);
2331          }
2332          if ((blank_str &&
2333                text_ptr->minilines.first==text_ptr->minilines.last) ||
2334                strcmp(orig_str, entries[i]) != 0 ||
2335                (check_array.value[SHOWN_COL][i] !=
2336                orig_check_array.value[SHOWN_COL][i]) ||
2337                (check_array.value[NSHOWN_COL][i] !=
2338                orig_check_array.value[NSHOWN_COL][i])) {
2339             modified = TRUE;
2340             if (blank_str &&
2341                   text_ptr->minilines.first==text_ptr->minilines.last) {
2342                UnlinkAttr(attr_ptr);
2343                FreeTextObj(attr_ptr->obj);
2344                FreeAttr(attr_ptr);
2345             } else {
2346                int saved_nameshown;
2347 
2348                attr_ptr->shown = check_array.value[SHOWN_COL][i];
2349                attr_ptr->nameshown = check_array.value[NSHOWN_COL][i];
2350                saved_nameshown = attr_ptr->nameshown;
2351                DynStrSet(&GetTextPtrFirstStrSeg(text_ptr)->dyn_str,
2352                      entries[i]);
2353                attr_ptr->nameshown = TRUE;
2354                UpdateAttr(text_ptr, attr_ptr);
2355                if (!saved_nameshown) {
2356                   if (*attr_ptr->attr_name.s != '\0') {
2357                      attr_ptr->nameshown = saved_nameshown;
2358                   }
2359                   UpdAttr(attr_ptr);
2360                   UpdateAttr(text_ptr, attr_ptr);
2361                }
2362             }
2363          }
2364       }
2365       if (modified) {
2366          AdjObjBBox(topSel->obj);
2367          RecordReplaceAnObj(topSel->obj);
2368          UpdSelBBox();
2369          RedrawAreas(botObj, sel_ltx-GRID_ABS_SIZE(1),
2370                   sel_lty-GRID_ABS_SIZE(1),
2371                   sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
2372                   selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
2373                   selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
2374          SetFileModified(TRUE);
2375          justDupped = FALSE;
2376          Msg(TgLoadString(STID_OBJ_ATTRS_UPDATED));
2377       } else {
2378          AbortPrepareCmd(CMD_REPLACE);
2379       }
2380       HighLightForward();
2381    }
2382    free(dsp_ptr);
2383    free(*entries);
2384    free(entries);
2385    CleanUpCheckArray(&orig_check_array);
2386    CleanUpCheckArray(&check_array);
2387    fileAttrNameDspPtr = NULL;
2388    Msg("");
2389 }
2390 
2391 static
GetAttrNamesCount(buf)2392 int GetAttrNamesCount(buf)
2393    char *buf;
2394 {
2395    int count=0;
2396 
2397    while (*buf != '\0') {
2398       char *psz=NULL;
2399 
2400       if (*buf == ':') return INVALID;
2401 
2402       psz = strchr(buf, ':');
2403       if (psz == NULL) {
2404          count++;
2405          break;
2406       } else {
2407          count++;
2408          buf = (&psz[1]);
2409       }
2410    }
2411    return count;
2412 }
2413 
2414 static
SetAttrGroupInfoDisplayedNames(pagi)2415 void SetAttrGroupInfoDisplayedNames(pagi)
2416    AttrGroupInfo *pagi;
2417 {
2418    int i=0, total=0;
2419    char *psz=NULL;
2420 
2421    for (i=0; i < pagi->num_attrs; i++) {
2422       total += strlen(pagi->attr_name[i])+1;
2423    }
2424    total += 1;
2425 
2426    pagi->displayed_names = (char*)malloc(total*sizeof(char));
2427    if (pagi->displayed_names == NULL) FailAllocMessage();
2428    memset(pagi->displayed_names, 0, total*sizeof(char));
2429 
2430    for (i=0, psz=pagi->displayed_names; i < pagi->num_attrs; i++) {
2431       if (i == pagi->num_attrs-1) {
2432          sprintf(psz, "%s", pagi->attr_name[i]);
2433          psz += strlen(pagi->attr_name[i]);
2434       } else {
2435          sprintf(psz, "%s:", pagi->attr_name[i]);
2436          psz += strlen(pagi->attr_name[i])+1;
2437       }
2438    }
2439 }
2440 
2441 static
FreeAttrGroupInfo(pagi)2442 void FreeAttrGroupInfo(pagi)
2443    AttrGroupInfo *pagi;
2444 {
2445    UtilFree(pagi->group_name);
2446    UtilFree(pagi->group_value);
2447    UtilFree(pagi->displayed_names);
2448    if (pagi->num_attrs > 0 && pagi->attr_name != NULL) {
2449       free(pagi->attr_name);
2450    }
2451 }
2452 
2453 static
CleanUpAttrGroup()2454 void CleanUpAttrGroup()
2455 {
2456    int i=0;
2457 
2458    if (maxAttrGroups != 0 && gAttrGroupInfo != NULL) {
2459       for (i=0; i < maxAttrGroups; i++) {
2460          FreeAttrGroupInfo(gAttrGroupInfo[i]);
2461          free(gAttrGroupInfo[i]);
2462       }
2463       free(gAttrGroupInfo);
2464    }
2465    maxAttrGroups = 0;
2466    gAttrGroupInfo = NULL;
2467 }
2468 
CleanUpAttr()2469 void CleanUpAttr()
2470 {
2471     CleanUpAttrGroup();
2472 }
2473 
2474 static
ParseAttrGroupXDef(group_index,xdef_line,buf)2475 int ParseAttrGroupXDef(group_index, xdef_line, buf)
2476    int group_index;
2477    char *xdef_line, *buf;
2478 {
2479    char *psz_attr_names=strchr(buf, ':'), *psz_attr=NULL;
2480    int count=0, index=0;
2481 
2482    if (psz_attr_names == NULL) {
2483       if (xdef_line == NULL) {
2484          fprintf(stderr, TgLoadString(STID_INVALID_COMPILER_OPT),
2485                "DEFATTRGROUP", buf);
2486       } else {
2487          fprintf(stderr, TgLoadString(STID_INVALID_XDEF), TOOL_NAME,
2488                xdef_line, buf);
2489       }
2490       fprintf(stderr, "\n");
2491       return FALSE;
2492    }
2493    *psz_attr_names++ = '\0';
2494    count = GetAttrNamesCount(psz_attr_names);
2495    if (count == INVALID) {
2496       if (xdef_line == NULL) {
2497          fprintf(stderr, TgLoadString(STID_INVALID_COMPILER_OPT),
2498                "DEFATTRGROUP", buf);
2499       } else {
2500          fprintf(stderr, TgLoadString(STID_INVALID_XDEF), TOOL_NAME,
2501                xdef_line, buf);
2502       }
2503       fprintf(stderr, "\n");
2504       return FALSE;
2505    }
2506    gAttrGroupInfo[group_index]->group_name = UtilStrDup(buf);
2507    if (gAttrGroupInfo[group_index]->group_name == NULL) {
2508       FailAllocMessage();
2509    }
2510    UtilTrimBlanks(gAttrGroupInfo[group_index]->group_name);
2511 
2512    gAttrGroupInfo[group_index]->group_value = UtilStrDup(psz_attr_names);
2513    if (gAttrGroupInfo[group_index]->group_value == NULL) {
2514       FailAllocMessage();
2515    }
2516    UtilTrimBlanks(gAttrGroupInfo[group_index]->group_value);
2517 
2518    gAttrGroupInfo[group_index]->attr_name = (char**)malloc(
2519          count*sizeof(char*));
2520    if (gAttrGroupInfo[group_index]->attr_name == NULL) {
2521       FailAllocMessage();
2522    }
2523    memset(gAttrGroupInfo[group_index]->attr_name, 0, count*sizeof(char*));
2524 
2525    psz_attr = gAttrGroupInfo[group_index]->group_value;
2526    while (*psz_attr != '\0') {
2527       char *psz_colon=strchr(psz_attr, ':');
2528 
2529       if (psz_colon != NULL) *psz_colon = '\0';
2530 
2531       UtilTrimBlanks(psz_attr);
2532       gAttrGroupInfo[group_index]->attr_name[index] = psz_attr;
2533 
2534       index++;
2535       if (psz_colon != NULL) {
2536          psz_attr = ++psz_colon;
2537       } else {
2538          break;
2539       }
2540    }
2541    gAttrGroupInfo[group_index]->num_attrs = count;
2542    SetAttrGroupInfoDisplayedNames(gAttrGroupInfo[group_index]);
2543 
2544    return TRUE;
2545 }
2546 
InitAttr()2547 int InitAttr()
2548 {
2549    char *c_ptr=NULL, buf[MAXSTRING];
2550    int i=0, attr_group_in_xdef=TRUE;
2551 
2552    maxAttrGroups = 0;
2553    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"MaxAttributeGroups")) !=
2554          NULL) {
2555       maxAttrGroups = atoi(c_ptr);
2556    }
2557    if (cmdLineTgrm2) {
2558 #ifdef DEFATTRGROUP
2559       if (maxAttrGroups == 0) {
2560          maxAttrGroups = 1;
2561          attr_group_in_xdef = FALSE;
2562       }
2563 #endif /* DEFATTRGROUP */
2564    }
2565    if (maxAttrGroups > 0) {
2566       gAttrGroupInfo = (AttrGroupInfo**)malloc(maxAttrGroups *
2567             sizeof(AttrGroupInfo*));
2568       if (gAttrGroupInfo == NULL) FailAllocMessage();
2569       memset(gAttrGroupInfo, 0, maxAttrGroups*sizeof(AttrGroupInfo*));
2570 
2571       for (i=0; i < maxAttrGroups; i++) {
2572          gAttrGroupInfo[i] = (AttrGroupInfo*)malloc(sizeof(AttrGroupInfo));
2573          if (gAttrGroupInfo[i] == NULL) FailAllocMessage();
2574          memset(gAttrGroupInfo[i], 0, sizeof(AttrGroupInfo));
2575 
2576          if (attr_group_in_xdef) {
2577             sprintf(buf, "AttributeGroup%1d", i);
2578             if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,buf)) != NULL) {
2579                ParseAttrGroupXDef(i, buf, c_ptr);
2580             } else {
2581                fprintf(stderr, TgLoadString(STID_CANNOT_GET_X_DEFAULT),
2582                      TOOL_NAME, buf);
2583                fprintf(stderr, "\n");
2584             }
2585          } else if (cmdLineTgrm2) {
2586 #ifdef DEFATTRGROUP
2587             char *tmp_buf=UtilStrDup(DEFATTRGROUP);
2588 
2589             if (tmp_buf == NULL) FailAllocMessage();
2590             if (!ParseAttrGroupXDef(i, NULL, tmp_buf)) {
2591                CleanUpAttrGroup();
2592             }
2593             UtilFree(tmp_buf);
2594 #endif /* DEFATTRGROUP */
2595          }
2596       }
2597    } else if (maxAttrGroups == 0) {
2598       /* nothing to do */
2599    } else {
2600       fprintf(stderr, TgLoadString(STID_INVALID_XDEF_USE_ALT_VALUE),
2601             TOOL_NAME, "MaxAttributeGroups", c_ptr, 0);
2602       fprintf(stderr, "\n");
2603    }
2604    return TRUE;
2605 }
2606