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/cutpaste.c,v 1.63 2011/06/25 22:36:42 cvsps Exp $
19  */
20 
21 #define _INCLUDE_FROM_CUTPASTE_C_
22 
23 #include "tgifdefs.h"
24 #include "cmdids.h"
25 
26 #include "arc.e"
27 #include "auxtext.e"
28 #include "attr.e"
29 #include "box.e"
30 #include "choice.e"
31 #include "choose.e"
32 #include "cmd.e"
33 #include "color.e"
34 #ifndef _NO_KINPUT
35 #include "convkinput.e"
36 #endif /* ~_NO_KINPUT */
37 #include "cutpaste.e"
38 #include "cursor.e"
39 #include "dialog.e"
40 #include "drawing.e"
41 #include "dup.e"
42 #include "file.e"
43 #include "font.e"
44 #include "grid.e"
45 #include "import.e"
46 #include "ini.e"
47 #include "mainloop.e"
48 #include "mark.e"
49 #include "menu.e"
50 #include "miniline.e"
51 #include "move.e"
52 #include "msg.e"
53 #include "names.e"
54 #ifndef _NO_NKF
55 #include "nkf.e"
56 #endif /* ~_NO_NKF */
57 #include "obj.e"
58 #include "oval.e"
59 #include "page.e"
60 #include "pattern.e"
61 #include "poly.e"
62 #include "polygon.e"
63 #include "raster.e"
64 #include "rcbox.e"
65 #include "remote.e"
66 #include "select.e"
67 #include "setup.e"
68 #include "special.e"
69 #include "stretch.e"
70 #include "strtbl.e"
71 #include "text.e"
72 #include "util.e"
73 #include "xbitmap.e"
74 #include "xpixmap.e"
75 
76 int	copyingToCutBuffer=FALSE;
77 int	pastingFile=FALSE;
78 
79 char	gszFilesIniFile[MAXPATHLENGTH];
80 /* do not translate -- program constants */
81 char	*gpszRecentFilesSec="Recent Files";
82 char	*gpszRecentFilesCountKey="Count";
83 
84 int	 cutBufferIsTgifObj=FALSE;
85 int	 cutBufferIsUTF8=FALSE;
86 struct DynStrRec dsCutBuffer;
87 
88 Time	startSelectionOwnershipTime=(Time)0;
89 Time	endSelectionOwnershipTime=(Time)0;
90 int	startSelectionOwnershipTimeValid=FALSE;
91 int	endSelectionOwnershipTimeValid=FALSE;
92 
93 SetCutBufferInfo gSetCutBufferInfo;
94 
95 static int pasteFromXSelectionOnly=TRUE;
96 static int pasteFromSelectionTimeout=10;
97 
98 static Atom gaCutBufferAtom[] = { XA_CUT_BUFFER0, XA_CUT_BUFFER1,
99    XA_CUT_BUFFER2, XA_CUT_BUFFER3, XA_CUT_BUFFER4, XA_CUT_BUFFER5,
100    XA_CUT_BUFFER6, XA_CUT_BUFFER7, (Atom)0 };
101 
102 static Atom gaAllSelectionAtom[] = {
103    XA_PRIMARY,
104    /*
105     * It does not appear that you need to try anything other than XA_PRIMARY.
106     *
107     * XA_CUT_BUFFER0, XA_CUT_BUFFER1, XA_CUT_BUFFER2, XA_CUT_BUFFER3,
108     * XA_CUT_BUFFER4, XA_CUT_BUFFER5, XA_CUT_BUFFER6, XA_CUT_BUFFER7,
109     */
110    (Atom)0 };
111 
112 static
ResetCutBufferInfo()113 void ResetCutBufferInfo()
114 {
115    UtilFree(gSetCutBufferInfo.tgif_dyn_str.s);
116    UtilFree(gSetCutBufferInfo.utf8_dyn_str.s);
117    UtilFree(gSetCutBufferInfo.text_dyn_str.s);
118    memset(&gSetCutBufferInfo, 0, sizeof(SetCutBufferInfo));
119 }
120 
121 static
SetCutBuffer(buf,bytes_to_write,buf_is_simple_string,buf_is_utf8_string)122 void SetCutBuffer(buf, bytes_to_write, buf_is_simple_string, buf_is_utf8_string)
123    char *buf;
124    int bytes_to_write, buf_is_simple_string, buf_is_utf8_string;
125    /*
126     * if buf_is_simple_string is TRUE, this is for text
127     * otherwise, it's an object
128     */
129 {
130    FreeDynStrBuf(&dsCutBuffer);
131    memset(&dsCutBuffer, 0, sizeof(dsCutBuffer));
132 
133    dsCutBuffer.s = (char*)malloc((bytes_to_write+1)*sizeof(char));
134    if (dsCutBuffer.s == NULL) FailAllocMessage();
135    memcpy(dsCutBuffer.s, buf, bytes_to_write);
136    dsCutBuffer.s[bytes_to_write] = '\0';
137    dsCutBuffer.sz = bytes_to_write+1;
138 
139    cutBufferIsTgifObj = (buf_is_simple_string ? FALSE : TRUE);
140    cutBufferIsUTF8 = buf_is_utf8_string;
141 
142    if (buf_is_simple_string) {
143       if (buf_is_utf8_string) {
144          DynStrSet(&gSetCutBufferInfo.utf8_dyn_str, dsCutBuffer.s);
145          gSetCutBufferInfo.utf8_valid = TRUE;
146       } else {
147          DynStrSet(&gSetCutBufferInfo.text_dyn_str, dsCutBuffer.s);
148          gSetCutBufferInfo.text_valid = TRUE;
149       }
150    } else {
151       DynStrSet(&gSetCutBufferInfo.tgif_dyn_str, dsCutBuffer.s);
152       gSetCutBufferInfo.tgif_valid = TRUE;
153    }
154 }
155 
156 #ifdef _CALL_XSTOREBYTES_ALSO
157 static
WriteOldStyleCutBuffer()158 int WriteOldStyleCutBuffer()
159 {
160    int xstorebytes_failed=FALSE;
161 
162    copyingToCutBuffer = TRUE;
163    XRotateBuffers(mainDisplay, 1);
164    XStoreBytes(mainDisplay, dsCutBuffer.s, dsCutBuffer.sz-1);
165 
166    XSync(mainDisplay, False);
167    if (copyingToCutBuffer == INVALID) {
168       xstorebytes_failed = TRUE;
169    }
170    copyingToCutBuffer = FALSE;
171    return (!xstorebytes_failed);
172 }
173 #endif /* _CALL_XSTOREBYTES_ALSO */
174 
WriteBufToCutBuffer(buf,bytes_to_write,buf_is_simple_string,buf_is_utf8_string,pscbi)175 int WriteBufToCutBuffer(buf, bytes_to_write, buf_is_simple_string,
176       buf_is_utf8_string, pscbi)
177    char *buf;
178    int bytes_to_write, buf_is_simple_string, buf_is_utf8_string;
179    SetCutBufferInfo *pscbi;
180    /*
181     * if buf_is_simple_string is TRUE, this is for text
182     * otherwise, it's an object
183     */
184 {
185    int copy_failed=FALSE, setselowner_failed=FALSE;
186 
187    ClearSelection();
188    SetCutBuffer(buf, bytes_to_write, buf_is_simple_string, buf_is_utf8_string);
189 #ifdef _CALL_XSTOREBYTES_ALSO
190    if (!WriteOldStyleCutBuffer()) {
191       ClearSelection();
192    }
193 #endif /* _CALL_XSTOREBYTES_ALSO */
194    /*
195     * If use gSelectionMainAtom here, on Fedora 11, pasting anything copied
196     *       from other tools ends up pasting from tgif!
197     * So, XA_PRIMARY is hard coded and gSelectionMainAtom is not used.
198     */
199    XSetSelectionOwner(mainDisplay, XA_PRIMARY, mainWindow,
200          lastKeyOrBtnEvInfo.time);
201    if (XGetSelectionOwner(mainDisplay, XA_PRIMARY) != mainWindow) {
202       setselowner_failed = TRUE;
203       sprintf(gszMsgBox, TgLoadString(STID_CANT_ACQUIRE_X_SELECTION));
204       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
205    } else {
206       startSelectionOwnershipTimeValid = TRUE;
207       endSelectionOwnershipTimeValid = FALSE;
208       startSelectionOwnershipTime = lastKeyOrBtnEvInfo.time;
209       endSelectionOwnershipTime = (Time)0;
210    }
211    return (!copy_failed);
212 }
213 
ClearSelection()214 void ClearSelection()
215 {
216    /*
217     * XStoreBytes(mainDisplay, NULL, 0);
218     * XSetSelectionOwner(mainDisplay, XA_PRIMARY, None,
219     *       lastKeyOrBtnEvInfo.time);
220     */
221 
222    /*
223     * Does not appear to need to do this:
224     *
225     * int i=0;
226     *
227     * for (i=0; gaAllSelectionAtom[i] != (Atom)0; i++) {
228     *    XSetSelectionOwner(mainDisplay, gaAllSelectionAtom[i], None,
229     *          lastKeyOrBtnEvInfo.time);
230     * }
231     */
232 }
233 
234 static
CopyObjectToCutBuffer(force)235 int CopyObjectToCutBuffer(force)
236    int force;
237    /* if force == TRUE, copy the TGIF object no matter what */
238    /* if force == FALSE, do not copy if the object is a simple text object, */
239    /*    i.e., all with same font, size, color, etc */
240    /* returns FALSE if copying in text mode -- this is */
241    /*    interpreted as an attempt to copy highlighted text */
242 {
243    FILE *fp=NULL;
244    char tmpfile[MAXSTRING], *cut_buffer=NULL;
245    struct stat stat;
246    unsigned char header=TGIF_HEADER;
247    struct ObjRec *partial_text_obj_ptr=NULL;
248 
249    if (curChoice == DRAWTEXT) {
250       if (!textHighlight) {
251          return FALSE;
252       } else {
253          /* create a partial text object with only the minilines */
254          partial_text_obj_ptr = CreateTextObjFromHighLightedText();
255          if (partial_text_obj_ptr == NULL) {
256             return FALSE;
257          }
258          if (!force) {
259             struct TextRec *text_ptr=partial_text_obj_ptr->detail.t;
260             MiniLinesInfo *minilines=(&text_ptr->minilines);
261             MiniLineInfo *pMiniLine=minilines->first;
262             StrBlockInfo *pStrBlock=pMiniLine->first_block;
263             StrSegInfo *pStrSeg=pStrBlock->seg;
264             int sz_unit=pStrSeg->sz_unit, double_byte=pStrSeg->double_byte, font=pStrSeg->font, style=pStrSeg->style;
265             int underline_on=pStrSeg->underline_on, overline_on=pStrSeg->overline_on;
266             int color_index=INVALID;
267 
268             if (SingleFontText(text_ptr, &sz_unit, &double_byte, &font, &style,
269                   &underline_on, &overline_on) &&
270                   SingleColorText(text_ptr, &color_index)) {
271                /* since it's a simple text object, don't copy it as a TGIF object */
272                return FALSE;
273             }
274          }
275       }
276    } else if (topSel == NULL) {
277       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
278       return TRUE;
279    }
280    if (MkTempFile(tmpfile, sizeof(tmpfile), tmpDir, TOOL_NAME) == NULL) {
281       return TRUE;
282    }
283    // Naehring: added b to the mode string.
284    if ((fp=fopen(tmpfile, "wb+")) == NULL) {
285       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
286             tmpfile);
287       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
288       return TRUE;
289    }
290    writeFileFailed = FALSE;
291    if (write(fileno(fp), (char*)(&header), 1) < 1) writeFileFailed = TRUE;
292 
293    if (curChoice == DRAWTEXT) {
294       /* create a partial text object with only the minilines */
295       copyInDrawTextMode = TRUE;
296       Save(fp, partial_text_obj_ptr, 0, 1);
297       copyInDrawTextMode = FALSE;
298 
299       FreeObj(partial_text_obj_ptr);
300    } else {
301       struct SelRec *sel_ptr=NULL;
302       struct ObjRec *top_obj=NULL, *bot_obj=NULL;
303 
304       for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
305          struct ObjRec *obj_ptr=DupObj(sel_ptr->obj);
306 
307          obj_ptr->prev = NULL;
308          obj_ptr->next = top_obj;
309 
310          if (top_obj == NULL) {
311             bot_obj = obj_ptr;
312          } else {
313             top_obj->prev = obj_ptr;
314          }
315          top_obj = obj_ptr;
316       }
317       Save(fp, bot_obj, 0, 1);
318       while (top_obj != NULL) {
319          struct ObjRec *obj_ptr=top_obj->next;
320 
321          FreeObj(top_obj);
322          top_obj = obj_ptr;
323       }
324    }
325    if (writeFileFailed) {
326       FailToWriteFileMessage(tmpfile);
327       fclose(fp);
328       unlink(tmpfile);
329       writeFileFailed = FALSE;
330       return TRUE;
331    }
332    fflush(fp);
333    if (fstat(fileno(fp), &stat) < 0) {
334       fclose(fp);
335       unlink(tmpfile);
336       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_FSTAT_ABORT_COPY), tmpfile);
337       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
338       return TRUE;
339    }
340 
341    if (cut_buffer != NULL) free(cut_buffer);
342    cut_buffer = (char*)malloc((stat.st_size+1)*sizeof(char));
343    if (cut_buffer == NULL) FailAllocMessage();
344 
345    rewind(fp);
346    if (read(fileno(fp), cut_buffer, stat.st_size) < stat.st_size) {
347       sprintf(gszMsgBox, TgLoadString(STID_ERR_READING_FILE_COPY_ABORT),
348             tmpfile);
349       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
350    } else {
351       if (!WriteBufToCutBuffer(cut_buffer, stat.st_size, FALSE, FALSE, NULL)) {
352          sprintf(gszMsgBox, TgLoadString(STID_COPY_FAILED_OBJ_MAYBE_TOO_BIG));
353          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
354       } else {
355          sprintf(gszMsgBox, TgLoadString(STID_COPY_BUFFER_UPDATED));
356          Msg(gszMsgBox);
357       }
358    }
359    fclose(fp);
360    unlink(tmpfile);
361    UtilFree(cut_buffer);
362 
363    return (curChoice != DRAWTEXT);
364 }
365 
CopyToCutBuffer()366 int CopyToCutBuffer()
367    /* returns FALSE if copying in text mode -- this is */
368    /*    interpreted as an attempt to copy highlighted text */
369 {
370    int rc=TRUE, handle_edit_text_size=FALSE;
371 
372    ResetCutBufferInfo();
373    if (curChoice == DRAWTEXT) {
374       if (escPressed) {
375          escPressed = FALSE;
376          Msg(TgLoadString(STID_ESC_KEY_PRESS_IGNORED));
377       }
378       if (!textHighlight) {
379          MsgBox(TgLoadString(STID_NO_TEXT_SELECTED_FOR_COPY), TOOL_NAME,
380                INFO_MB);
381          return FALSE;
382       } else {
383          if (editTextSize != 0) {
384             handle_edit_text_size = TRUE;
385             if (RestoreEditTextSize(curTextObj, TRUE)) {
386                UpdTextBBox(curTextObj);
387             }
388          }
389          if (CanCopyHighLightedTextAsUTF8Strings(NULL)) {
390             XEvent ev;
391 
392             if (handle_edit_text_size && editTextSize != 0) {
393                if (RestoreEditTextSize(curTextObj, FALSE)) {
394                   UpdTextBBox(curTextObj);
395                }
396             }
397             copyInDrawTextMode = TRUE;
398             copyDoubleByteStringInDrawTextMode = TRUE;
399             ev.type = KeyPress;
400             DrawText(&ev);
401             copyDoubleByteStringInDrawTextMode = FALSE;
402             copyInDrawTextMode = FALSE;
403          } else if (CanCopyHighLightedTextAsStrings()) {
404             XEvent ev;
405 
406             if (handle_edit_text_size && editTextSize != 0) {
407                if (RestoreEditTextSize(curTextObj, FALSE)) {
408                   UpdTextBBox(curTextObj);
409                }
410             }
411             copyInDrawTextMode = TRUE;
412             ev.type = KeyPress;
413             DrawText(&ev);
414             copyInDrawTextMode = FALSE;
415          } else {
416 #ifdef NOT_DEFINED
417             char *cut_buffer=NULL;
418             int cut_buffer_size=0;
419 
420             GatherHighLightedTextAsStrings(&cut_buffer, &cut_buffer_size);
421             if (cut_buffer == NULL) {
422                ClearSelection();
423             } else {
424                if (WriteBufToCutBuffer(cut_buffer, cut_buffer_size-1, TRUE,
425                      FALSE, NULL)) {
426                   sprintf(gszMsgBox, TgLoadString(STID_COPY_BUFFER_UPDATED));
427                } else {
428                   sprintf(gszMsgBox, TgLoadString(STID_COPY_FAIL_SEL_STR_MAY_TOO_LNG));
429                }
430                Msg(gszMsgBox);
431                free(cut_buffer);
432             }
433 #endif /* NOT_DEFINED */
434             /*
435              * create a partial text object with only the minilines, this
436              *       will be done in CopyObjectToCutBuffer() below
437              */
438          }
439       }
440    } else if (topSel == NULL) {
441       MsgBox(TgLoadString(STID_NO_OBJ_SELECTED_FOR_COPY), TOOL_NAME, INFO_MB);
442       return TRUE;
443    }
444    rc = CopyObjectToCutBuffer(FALSE);
445    if (handle_edit_text_size && editTextSize != 0) {
446       if (RestoreEditTextSize(curTextObj, FALSE)) {
447          UpdTextBBox(curTextObj);
448       }
449    }
450    return rc;
451 }
452 
CopyPlainTextAsObject()453 int CopyPlainTextAsObject()
454    /* returns FALSE if copying in text mode -- this is */
455    /*    interpreted as an attempt to copy highlighted text */
456 {
457    if (!(curChoice == DRAWTEXT && textHighlight)) {
458       return (curChoice != DRAWTEXT);
459    }
460    return CopyObjectToCutBuffer(TRUE);
461 }
462 
CutToCutBuffer()463 void CutToCutBuffer()
464 {
465    if (curChoice == DRAWTEXT && textCursorShown) {
466       CopyToCutBuffer();
467       DelSelText();
468    }
469    if (curChoice == NOTHING && topSel != NULL) {
470       CopyToCutBuffer();
471       DelAllSelObj();
472    }
473 }
474 
PasteString(CutBuffer,highlight,record_cmd)475 unsigned int PasteString(CutBuffer, highlight, record_cmd)
476    char *CutBuffer;
477    int highlight, record_cmd;
478 {
479    char *c_ptr=NULL, *dest_c_ptr=NULL;
480    int x, y, num_lines=0, char_count, root_x, root_y, grid_x, grid_y;
481    unsigned int status=0, button_pressed=0;
482    struct ObjRec *obj_ptr=NULL;
483    struct TextRec *text_ptr=NULL;
484    Window root_win=None, child_win=None;
485    MiniLineInfo *pFirstMiniLine=NULL, *pLastMiniLine=NULL;
486 
487    if (*CutBuffer == '\0') {
488       MsgBox(TgLoadString(STID_CUT_BUFFER_EMPTY), TOOL_NAME, INFO_MB);
489       return 0;
490    }
491    TieLooseEnds();
492    SetCurChoice(NOTHING);
493    if (topSel!=NULL) { HighLightReverse(); RemoveAllSel(); }
494 
495    XQueryPointer(mainDisplay, drawWindow, &root_win, &child_win,
496          &root_x, &root_y, &x, &y, &status);
497    GridXY(x, y, &grid_x, &grid_y);
498 
499    obj_ptr = (struct ObjRec *)malloc(sizeof(struct ObjRec));
500    if (obj_ptr == NULL) FailAllocMessage();
501    memset(obj_ptr, 0, sizeof(struct ObjRec));
502 
503    text_ptr = (struct TextRec *)malloc(sizeof(struct TextRec));
504    if (text_ptr == NULL) FailAllocMessage();
505    memset(text_ptr, 0, sizeof(struct TextRec));
506 
507    for (c_ptr=CutBuffer, num_lines = 0; *c_ptr != '\0'; num_lines++) {
508       MiniLineInfo *pMiniLine=NULL;
509       if (lengthLimit256InInsertChar) {
510          char s[MAXSTRING+1];
511 
512          char_count = 0;
513          dest_c_ptr = s;
514          while (*c_ptr != '\0' && *c_ptr != '\n' && *c_ptr != '\r') {
515             *dest_c_ptr++ = *c_ptr++;
516             if (++char_count == MAXSTRING) {
517                sprintf(gszMsgBox,
518                      TgLoadString(STID_STRING_LEN_EXCEEDS_AND_TRUNC),
519                      MAXSTRING);
520                Msg(gszMsgBox);
521                while (*c_ptr != '\0' && *c_ptr != '\n' && *c_ptr != '\r') {
522                   c_ptr++;
523                }
524                break;
525             }
526          }
527          *dest_c_ptr = '\0';
528          pMiniLine = CreateMiniLineFromString(s, &pFirstMiniLine,
529                &pLastMiniLine);
530       } else {
531          unsigned char *psz_CR=(unsigned char *)strchr(c_ptr, '\r');
532          unsigned char *psz_LF=(unsigned char *)strchr(c_ptr, '\n');
533          int use_CR=FALSE, use_LF=FALSE;
534 
535          if (psz_CR == NULL) {
536             if (psz_LF != NULL) {
537                use_LF = TRUE;
538             }
539          } else if (psz_CR == NULL) {
540             use_CR = TRUE;
541          } else if (psz_CR > psz_LF) {
542             use_LF = TRUE;
543          } else {
544             use_CR = TRUE;
545          }
546          if (use_CR) {
547             *psz_CR = '\0';
548             pMiniLine = CreateMiniLineFromString(c_ptr, &pFirstMiniLine,
549                   &pLastMiniLine);
550             *psz_CR = '\n';
551             c_ptr = (char*)psz_CR;
552          } else if (use_LF) {
553             *psz_LF = '\0';
554             pMiniLine = CreateMiniLineFromString(c_ptr, &pFirstMiniLine,
555                   &pLastMiniLine);
556             *psz_LF = '\r';
557             c_ptr = (char*)psz_LF;
558          } else {
559             pMiniLine = CreateMiniLineFromString(c_ptr, &pFirstMiniLine,
560                   &pLastMiniLine);
561             c_ptr += strlen(c_ptr);
562          }
563       }
564       if (*c_ptr == '\n') {
565          c_ptr++;
566          if (c_ptr[1] == '\r') c_ptr++;
567       } else if (*c_ptr == '\r') {
568          c_ptr++;
569          if (c_ptr[1] == '\n') c_ptr++;
570       }
571    }
572    text_ptr->lines = num_lines;
573    text_ptr->minilines.first = pFirstMiniLine;
574    text_ptr->minilines.last = pLastMiniLine;
575    text_ptr->baseline_y = grid_y+pFirstMiniLine->asc;
576 
577    CopyCurInfoIntoTextPtr(obj_ptr, text_ptr);
578 
579    obj_ptr->x = grid_x;
580    obj_ptr->y = grid_y;
581    obj_ptr->type = OBJ_TEXT;
582    obj_ptr->color = colorIndex;
583    if (mainDisplay != NULL) {
584       UtilStrCpyN(obj_ptr->color_str, sizeof(obj_ptr->color_str),
585             colorMenuItems[colorIndex]);
586    }
587    obj_ptr->id = objId++;;
588    obj_ptr->dirty = FALSE;
589    obj_ptr->rotation = 0;
590    obj_ptr->detail.t = text_ptr;
591    obj_ptr->fattr = obj_ptr->lattr = NULL;
592    obj_ptr->ctm = NULL;
593 
594    RecalcTextMetrics(text_ptr, grid_x, text_ptr->baseline_y);
595    UpdTextBBox(obj_ptr);
596    AddObj(NULL, topObj, obj_ptr);
597    button_pressed = PlaceTopObj(obj_ptr, NULL, NULL);
598 
599    if (highlight) SelectTopObj();
600    if (record_cmd) RecordNewObjCmd();
601    SetFileModified(TRUE);
602    justDupped = FALSE;
603 
604    return button_pressed;
605 }
606 
607 static
CreateTmpBoxObj(LtX,LtY,RbX,RbY)608 struct ObjRec *CreateTmpBoxObj(LtX, LtY, RbX, RbY)
609    int LtX, LtY, RbX, RbY;
610 {
611    register struct BoxRec *box_ptr;
612    register struct ObjRec *obj_ptr;
613 
614    box_ptr = (struct BoxRec *)malloc(sizeof(struct BoxRec));
615    if (box_ptr == NULL) FailAllocMessage();
616    memset(box_ptr, 0, sizeof(struct BoxRec));
617    box_ptr->fill = NONEPAT;
618    box_ptr->width = 0;
619    box_ptr->pen = NONEPAT;
620    box_ptr->dash = 0;
621 
622    obj_ptr = (struct ObjRec *)malloc(sizeof(struct ObjRec));
623    if (obj_ptr == NULL) FailAllocMessage();
624    memset(obj_ptr, 0, sizeof(struct ObjRec));
625 
626    obj_ptr->bbox.ltx = obj_ptr->obbox.ltx = obj_ptr->x = LtX;
627    obj_ptr->bbox.lty = obj_ptr->obbox.lty = obj_ptr->y = LtY;
628    obj_ptr->bbox.rbx = obj_ptr->obbox.rbx = RbX;
629    obj_ptr->bbox.rby = obj_ptr->obbox.rby = RbY;
630    obj_ptr->type = OBJ_BOX;
631    obj_ptr->color = colorIndex;
632    if (mainDisplay != NULL) {
633       UtilStrCpyN(obj_ptr->color_str, sizeof(obj_ptr->color_str),
634             colorMenuItems[colorIndex]);
635    }
636    obj_ptr->id = 0;
637    obj_ptr->dirty = FALSE;
638    obj_ptr->rotation = 0;
639    obj_ptr->detail.b = box_ptr;
640    obj_ptr->fattr = obj_ptr->lattr = NULL;
641    obj_ptr->ctm = NULL;
642    obj_ptr->invisible = FALSE;
643    obj_ptr->trans_pat = FALSE;
644 
645    return obj_ptr;
646 }
647 
AssignNewObjIds(ObjPtr)648 void AssignNewObjIds(ObjPtr)
649    struct ObjRec *ObjPtr;
650 {
651    register struct ObjRec *obj_ptr;
652    register struct AttrRec *attr_ptr;
653 
654    ObjPtr->id = objId++;
655    switch (ObjPtr->type) {
656    case OBJ_GROUP:
657    case OBJ_SYM:
658    case OBJ_ICON:
659    case OBJ_PIN:
660       for (obj_ptr=ObjPtr->detail.r->first; obj_ptr != NULL;
661             obj_ptr=obj_ptr->next) {
662          AssignNewObjIds(obj_ptr);
663       }
664       break;
665    default: break;
666    }
667    for (attr_ptr=ObjPtr->fattr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
668       AssignNewObjIds(attr_ptr->obj);
669    }
670 }
671 
672 #define CBF_NON_TGIF 0
673 #define CBF_TGIF_STRING 1
674 #define CBF_TGIF_OBJ 2
675 
676 static
CutBufferType(cut_buffer)677 int CutBufferType(cut_buffer)
678    char *cut_buffer;
679 {
680    unsigned char header=TGIF_HEADER;
681 
682    if (((unsigned char)(*cut_buffer)) == header) {
683       if (strncmp(&cut_buffer[1], "%TGIF", 5) == 0) {
684          return CBF_TGIF_OBJ;
685       } else if (strncmp(&cut_buffer[1], "state(", 6) == 0) {
686          /* very old tgif format (even before tgif-2.12) */
687          return CBF_TGIF_OBJ;
688       }
689       return CBF_TGIF_STRING;
690    }
691    return CBF_NON_TGIF;
692 }
693 
694 static
GetObjsFromCutBuffer(cut_buffer,len,pp_top_obj,pp_bot_obj)695 int GetObjsFromCutBuffer(cut_buffer, len, pp_top_obj, pp_bot_obj)
696    char *cut_buffer;
697    int len;
698    struct ObjRec **pp_top_obj, **pp_bot_obj;
699    /* the cut_buffer is the original cut buffer shifted one byte */
700 {
701    FILE *fp=NULL;
702    int read_status=0;
703    char tmpfile[MAXSTRING];
704    struct ObjRec *obj_ptr=NULL, *saved_top_obj=NULL, *saved_bot_obj=NULL;
705 
706    (*pp_top_obj) = (*pp_bot_obj) = NULL;
707 
708    if (MkTempFile(tmpfile, sizeof(tmpfile), tmpDir, TOOL_NAME) == NULL) {
709       return FALSE;
710    }
711    // Naehring: added b to the mode string.
712    if ((fp=fopen(tmpfile, "wb+")) == NULL) {
713       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_WRITING),
714             tmpfile);
715       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
716       return FALSE;
717    }
718    writeFileFailed = FALSE;
719    if (write(fileno(fp), cut_buffer, len) < len) {
720       fclose(fp);
721       unlink(tmpfile);
722       sprintf(gszMsgBox, TgLoadString(STID_ERR_WRITING_FILE_PASTE_ABORT),
723             tmpfile);
724       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
725       return FALSE;
726    }
727    fflush(fp);
728    rewind(fp);
729 
730    SetWatchCursor(drawWindow);
731    SetWatchCursor(mainWindow);
732 
733    saved_top_obj = topObj;
734    saved_bot_obj = botObj;
735    curPage->top = topObj = NULL;
736    curPage->bot = botObj = NULL;
737 
738    importingFile = TRUE;
739    pastingFile = TRUE;
740    readingPageNum = loadedCurPageNum = 0;
741    foundGoodStateObject = FALSE;
742    while ((read_status=ReadObj(fp, &obj_ptr)) == TRUE) {
743       if (obj_ptr != NULL) {
744          AdjForOldVersion(obj_ptr);
745          UnlockAnObj(obj_ptr);
746          AssignNewObjIds(obj_ptr);
747          AddObj(NULL, topObj, obj_ptr);
748       }
749    }
750    fclose(fp);
751    if (!PRTGIF && colorLayers && needToRedrawColorWindow) {
752       RedrawColorWindow();
753    }
754    importingFile = FALSE;
755    pastingFile = FALSE;
756    unlink(tmpfile);
757    SetDefaultCursor(mainWindow);
758    ShowCursor();
759 
760    if (read_status == INVALID) {
761       sprintf(gszMsgBox, TgLoadString(STID_FILEVER_TOO_LARGE_PASTE_ABORT),
762             fileVersion);
763       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
764       return FALSE;
765    }
766    (*pp_top_obj) = topObj;
767    (*pp_bot_obj) = botObj;
768 
769    curPage->top = topObj = saved_top_obj;
770    curPage->bot = botObj = saved_bot_obj;
771 
772    return TRUE;
773 }
774 
FreeSelectionOrCutBuffer(cut_buffer,from_selection)775 void FreeSelectionOrCutBuffer(cut_buffer, from_selection)
776    char *cut_buffer;
777    int from_selection;
778 {
779    if (from_selection) {
780       UtilFree(cut_buffer);
781    } else {
782       if (cut_buffer != NULL) XFree(cut_buffer);
783    }
784 }
785 
FetchSelectionOrCutBuffer(pnLen,pnFromSelection)786 char *FetchSelectionOrCutBuffer(pnLen, pnFromSelection)
787    int *pnLen, *pnFromSelection;
788 {
789    int len=0;
790    unsigned long uLen=0L;
791    char *cut_buffer=GetTextBytesFromSelection(FALSE, &uLen);
792 
793    *pnLen = 0;
794    *pnFromSelection = FALSE;
795    if (cut_buffer == NULL || uLen == 0L) {
796       if (pasteFromXSelectionOnly) return NULL;
797 
798       cut_buffer = (char*)XFetchBytes(mainDisplay, &len);
799       if (len == 0) {
800          return NULL;
801       }
802    } else {
803       *pnFromSelection = TRUE;
804       len = (int)uLen;
805    }
806    *pnLen = len;
807 
808    return cut_buffer;
809 }
810 
811 static int pastingUTF8String=FALSE;
812 
PasteFromCutBuffer()813 int PasteFromCutBuffer()
814    /* returns FALSE if pasting in text mode and non-tgif bytes are */
815    /*    in the cut buffer -- this is interpreted as an attempt to */
816    /*    paste into the current text */
817 {
818    int len=0, ltx=0, lty=0, rbx=0, rby=0, dx=0, dy=0, from_selection=FALSE;
819    char *cut_buffer=NULL, *orig_cut_buffer=NULL;
820    struct ObjRec *obj_ptr=NULL, *tmp_obj=NULL;
821    struct ObjRec *saved_top_obj=NULL, *saved_bot_obj=NULL;
822    struct ObjRec *pasted_top_obj=NULL, *pasted_bot_obj=NULL;
823    struct ObjRec *tmp_top_obj=NULL, *tmp_bot_obj=NULL;
824    StrSegInfo ssi;
825 
826    memset(&ssi, 0, sizeof(StrSegInfo));
827 
828    if (curChoice == DRAWTEXT && textCursorShown &&
829          CanPasteUTF8StringIntoText(&ssi)) {
830       pastingUTF8String = TRUE;
831    }
832    cut_buffer = FetchSelectionOrCutBuffer(&len, &from_selection);
833    if (cut_buffer == NULL) {
834       MsgBox(TgLoadString(STID_CUT_BUFFER_EMPTY), TOOL_NAME, INFO_MB);
835       pastingUTF8String = FALSE;
836       return TRUE;
837    }
838    orig_cut_buffer = cut_buffer;
839 
840    if (CutBufferType(cut_buffer) != CBF_TGIF_OBJ) {
841       if (curChoice == DRAWTEXT) {
842          XEvent ev;
843 
844          FreeSelectionOrCutBuffer(cut_buffer, from_selection);
845          pasteInDrawTextMode = TRUE;
846          ev.type = KeyPress;
847          DrawText(&ev);
848          pastingUTF8String = FALSE;
849          return FALSE;
850       }
851 #ifndef _NO_KINPUT
852       if (copyAndPasteJIS) {
853          CvtJisToEuc(cut_buffer, cut_buffer);
854       }
855 #endif /* ~_NO_KINPUT */
856       Msg(TgLoadString(STID_PASTE_FROM_NON_TGIF));
857       PasteString(cut_buffer, TRUE, TRUE);
858       FreeSelectionOrCutBuffer(cut_buffer, from_selection);
859       pastingUTF8String = FALSE;
860 
861       return TRUE;
862    }
863    if (curChoice == DRAWTEXT) {
864       SaveCursorPositionInCurText();
865    } else {
866       MakeQuiescent();
867    }
868    if (!GetObjsFromCutBuffer(&cut_buffer[1], len-1, &pasted_top_obj,
869          &pasted_bot_obj)) {
870       FreeSelectionOrCutBuffer(orig_cut_buffer, from_selection);
871       pastingUTF8String = FALSE;
872       return TRUE;
873    }
874    FreeSelectionOrCutBuffer(orig_cut_buffer, from_selection);
875 
876    saved_top_obj = topObj;
877    saved_bot_obj = botObj;
878    curPage->top = topObj = pasted_top_obj;
879    curPage->bot = botObj = pasted_bot_obj;
880 
881    if (curChoice == DRAWTEXT && textCursorShown && topObj != NULL &&
882          topObj == botObj && topObj->type == OBJ_TEXT) {
883       /* we are pasting minilines and not object */
884       struct ObjRec *partial_text_obj_ptr=topObj;
885 
886       RestoreCursorPositionInCurText();
887       curPage->top = topObj = saved_top_obj;
888       curPage->bot = botObj = saved_bot_obj;
889 
890       if (curStrBlock->type == SB_SUPSUB_CENTER) {
891          strcpy(gszMsgBox, TgLoadString(STID_CANNOT_PASTE_MIXED_TEXT));
892          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
893       } else {
894          Msg(TgLoadString(STID_STR_BLOCKS_PASTED_FROM_TGIF));
895          PasteMiniLinesAtCursor(partial_text_obj_ptr);
896       }
897       FreeObj(partial_text_obj_ptr);
898       pastingUTF8String = FALSE;
899 
900       return FALSE;
901    }
902    if (curChoice == DRAWTEXT) {
903       /* we've made a mistake earlier on, should have MakeQuiescent() */
904       struct ObjRec *pasted_top_obj=topObj, *pasted_bot_obj=botObj;
905 
906       curPage->top = topObj = saved_top_obj;
907       curPage->bot = botObj = saved_bot_obj;
908       RestoreCursorPositionInCurText();
909 
910       MakeQuiescent();
911 
912       saved_top_obj = topObj;
913       saved_bot_obj = botObj;
914       curPage->top = topObj = pasted_top_obj;
915       curPage->bot = botObj = pasted_bot_obj;
916    }
917    if (topObj != NULL) SetFileModified(TRUE);
918 
919    ltx = topObj->obbox.ltx;
920    lty = topObj->obbox.lty;
921    rbx = topObj->obbox.rbx;
922    rby = topObj->obbox.rby;
923    for (obj_ptr = topObj->next; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
924       if (obj_ptr->obbox.ltx < ltx) ltx = obj_ptr->obbox.ltx;
925       if (obj_ptr->obbox.lty < lty) lty = obj_ptr->obbox.lty;
926       if (obj_ptr->obbox.rbx > rbx) rbx = obj_ptr->obbox.rbx;
927       if (obj_ptr->obbox.rby > rby) rby = obj_ptr->obbox.rby;
928    }
929    tmp_obj = CreateTmpBoxObj(ltx, lty, rbx, rby);
930 
931    tmp_top_obj = topObj;
932    tmp_bot_obj = botObj;
933    curPage->top = topObj = NULL;
934    curPage->bot = botObj = NULL;
935    PlaceTopObj(tmp_obj, saved_top_obj, saved_bot_obj);
936    curPage->top = topObj = tmp_top_obj;
937    curPage->bot = botObj = tmp_bot_obj;
938 
939    dx = tmp_obj->obbox.ltx - ltx;
940    dy = tmp_obj->obbox.lty - lty;
941    FreeBoxObj(tmp_obj);
942 
943    for (obj_ptr=topObj; obj_ptr != NULL; obj_ptr=obj_ptr->next) {
944       MoveObj(obj_ptr, dx, dy);
945    }
946 
947    SelAllObj(FALSE, FALSE);
948 
949    if (botObj != NULL) {
950       botObj->next = saved_top_obj;
951    } else {
952       curPage->top = topObj = saved_top_obj;
953    }
954 
955    if (saved_top_obj != NULL) {
956       saved_top_obj->prev = botObj;
957       curPage->bot = botObj = saved_bot_obj;
958    }
959    RedrawDrawWindow(botObj);
960    PrepareToRecord(CMD_NEW, NULL, NULL, 0);
961    RecordCmd(CMD_NEW, NULL, topSel, botSel, numObjSelected);
962    HighLightForward();
963 
964    Msg(TgLoadString(STID_OBJS_PASTED_FROM_TGIF));
965    pastingUTF8String = FALSE;
966    return TRUE;
967 }
968 
PasteFromFile()969 int PasteFromFile()
970    /* returns FALSE if pasting in text mode and non-tgif bytes are */
971    /*    in the cut buffer -- this is interpreted as an attempt to */
972    /*    paste into the current text */
973 {
974    char file_name[MAXPATHLENGTH+1];
975    FILE *fp=NULL;
976    char inbuf[MAXSTRING+1], *cut_buffer=NULL;
977    int size=0, pos=0;
978 
979    if (SelectFileNameToPaste(TgLoadString(STID_SEL_A_FILE_TO_PASTE_DOTS),
980          file_name) == INVALID) {
981       return TRUE;
982    } else if (FileIsRemote(file_name)) {
983       MsgBox(TgLoadString(STID_PASTING_REMOTE_FILE_NOT_SUP), TOOL_NAME,
984             INFO_MB);
985       return TRUE;
986    }
987    if (curChoice == DRAWTEXT) {
988       XEvent ev;
989 
990       pasteInDrawTextMode = TRUE;
991       pasteFromFileInDrawTextMode = TRUE;
992       strcpy(pasteFromFileName, file_name);
993       ev.type = KeyPress;
994       DrawText(&ev);
995       return FALSE;
996    }
997    if ((fp=fopen(file_name, "r")) == NULL) {
998       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_READING),
999             file_name);
1000       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
1001       return TRUE;
1002    }
1003    while (fgets(inbuf, MAXSTRING, fp) != NULL) size += strlen(inbuf);
1004    fclose(fp);
1005    if (size == 0) {
1006       sprintf(gszMsgBox, TgLoadString(STID_NAMED_FILE_IS_EMPTY), file_name);
1007       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
1008       return TRUE;
1009    }
1010    cut_buffer = (char*)malloc((size+2)*sizeof(char));
1011    if (cut_buffer == NULL) {
1012       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_MALLOC_NUM_BYTES), size+2);
1013       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
1014       return TRUE;
1015    }
1016    if ((fp=fopen(file_name, "r")) == NULL) {
1017       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_OPEN_FILE_FOR_READING),
1018             file_name);
1019       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
1020       return TRUE;
1021    }
1022    while (fgets(&cut_buffer[pos], MAXSTRING, fp) != NULL) {
1023       pos += strlen(&cut_buffer[pos]);
1024    }
1025    fclose(fp);
1026 #ifndef _NO_NKF
1027    if (Tg_useNKF()) {
1028       char *out_buffer=(char*)malloc(size+2);
1029 
1030       if (out_buffer == NULL) FailAllocMessage();
1031       Tg_do_kconv(cut_buffer, out_buffer, size+2);
1032       PasteString(out_buffer, TRUE, TRUE);
1033       free(out_buffer);
1034    } else {
1035 #ifndef _NO_KINPUT
1036       if (copyAndPasteJIS) {
1037          CvtJisToEuc(cut_buffer, cut_buffer);
1038       }
1039 #endif /* ~_NO_KINPUT */
1040       PasteString(cut_buffer, TRUE, TRUE);
1041    }
1042 #else /* _NO_NKF */
1043    PasteString(cut_buffer, TRUE, TRUE);
1044 #endif /* ~_NO_NKF */
1045 
1046    return TRUE;
1047 }
1048 
1049 #ifdef NOT_DEFINED
1050 static
WindowPropertyExists(dpy,win,property)1051 int WindowPropertyExists(dpy, win, property)
1052    Display *dpy;
1053    Window win;
1054    Atom property;
1055 {
1056    Atom type_atom=(Atom)0;
1057    unsigned long ulBytesLeft=0L, ulLen=0L;
1058    char *psz=NULL;
1059    int nFormat=0, rc=FALSE;
1060    unsigned long ulCurSize=0L, ulTotalLeft=0x10000L;
1061    int nStatus=XGetWindowProperty(dpy, win, property,
1062          ulCurSize, ulTotalLeft, False, AnyPropertyType,
1063          &type_atom, &nFormat, &ulLen, &ulBytesLeft,
1064          (unsigned char **)(&psz));
1065 
1066    rc = (nStatus != None);
1067 
1068    if (psz != NULL) XFree(psz);
1069 
1070    return rc;
1071 }
1072 #endif /* NOT_DEFINED */
1073 
1074 static
GetTextBytesFromWindowProperty(win,property,compound_text,pul_len,delete_prop)1075 char *GetTextBytesFromWindowProperty(win, property, compound_text, pul_len,
1076       delete_prop)
1077    Window win;
1078    Atom property;
1079    int compound_text, delete_prop;
1080    unsigned long *pul_len;
1081 {
1082    int actual_format=0;
1083    unsigned long long_offset=0L, bytes_after=0L, nitems=0L, total=0L;
1084    char *cut_buffer=NULL, *psz=NULL;
1085    Atom actual_type=(Atom)0;
1086 
1087    /*
1088     * Accorind to the documentation for XGetWindowProperty(), if it is called
1089     *         with AnyPropertyType, the following is true:
1090     *     N = actual length of the stored property in bytes
1091     *         (even if the format is 16 or 32)
1092     *     I = 4 * long_offset
1093     *     T = N - I
1094     *     L = MINIMUM(T, 4 * long_length)
1095     *     A = N - (I + L)
1096     * We use a fixed long_legnth of 0x4000L
1097     * L is nitems
1098     * A is bytes_after
1099     */
1100    if (debugCopyPaste > 1) {
1101       char *atom_name=NULL;
1102 
1103       atom_name = XGetAtomName(mainDisplay, property);
1104       fprintf(stderr, "Property name is '%s'.\n", atom_name);
1105       XFree(atom_name);
1106    }
1107    while (XGetWindowProperty(mainDisplay, win, property,
1108             long_offset, 0x4000L, delete_prop, AnyPropertyType,
1109             &actual_type, &actual_format, &nitems, &bytes_after,
1110             (unsigned char **)(&psz)) == Success) {
1111       if (debugCopyPaste) {
1112          static int n=0;
1113          fprintf(stderr, "[%1d] actual_format = %1d, nitems = %1d\n", ++n,
1114                (int)actual_format, (int)nitems);
1115       }
1116       if (psz != NULL && nitems > 0) {
1117          unsigned int bytes_per_item=(actual_format>>3);
1118          unsigned int bytes_got=(bytes_per_item==1 ? nitems :
1119                (bytes_per_item*nitems));
1120 
1121          total += bytes_got;
1122          if (cut_buffer == NULL) {
1123             cut_buffer = (char*)malloc(total+1);
1124          } else {
1125             cut_buffer = (char*)realloc(cut_buffer, total+1);
1126          }
1127          if (cut_buffer == NULL) FailAllocMessage();
1128          memcpy(&cut_buffer[long_offset<<2], psz, bytes_got);
1129          cut_buffer[total] = '\0';
1130          long_offset += (bytes_got>>2);
1131       }
1132       if (psz != NULL) XFree(psz);
1133 
1134       if (nitems == 0 || bytes_after == 0L) {
1135          break;
1136       }
1137    }
1138    *pul_len = total;
1139 
1140    return cut_buffer;
1141 }
1142 
1143 static
GetTextBytesFromGivenSelection(compound_text,pulLen,which_selection,paste_utf8_string,pssi,pn_timeout_msg_displayed)1144 char *GetTextBytesFromGivenSelection(compound_text, pulLen, which_selection,
1145       paste_utf8_string, pssi, pn_timeout_msg_displayed)
1146    int compound_text, which_selection, paste_utf8_string;
1147    int *pn_timeout_msg_displayed;
1148    unsigned long *pulLen;
1149    StrSegInfo *pssi;
1150    /* the returned string should be freed by calling UtilFree() */
1151 {
1152    int done=FALSE, need_to_try_text=FALSE, need_to_try_utf8=FALSE;
1153    char *cut_buffer=NULL;
1154    time_t tloc, endtime;
1155    Window selection_owner_win=None;
1156    XEvent ev;
1157 
1158    selection_owner_win = XGetSelectionOwner(mainDisplay, which_selection);
1159    if (selection_owner_win == None) {
1160       return NULL;
1161    }
1162    if (compound_text) {
1163       XConvertSelection(mainDisplay, which_selection, compoundTextAtom,
1164             tmpSelectionAtom, mainWindow, lastKeyOrBtnEvInfo.time);
1165    } else {
1166       if (paste_utf8_string) {
1167          /* try tgif object first, then try utf8, then try text */
1168          need_to_try_utf8 = TRUE;
1169       } else {
1170          /* try tgif object then try text */
1171          need_to_try_text = TRUE;
1172       }
1173       XConvertSelection(mainDisplay, which_selection, tgifProtocolAtom,
1174             tmpSelectionAtom, mainWindow, lastKeyOrBtnEvInfo.time);
1175    }
1176    if (XCheckMaskEvent(mainDisplay, ExposureMask, &ev) ||
1177          XCheckMaskEvent(mainDisplay, VisibilityChangeMask, &ev)) {
1178       ExposeEventHandler(&ev, TRUE);
1179    }
1180    XFlush(mainDisplay);
1181 
1182    time(&tloc);
1183    endtime = tloc + pasteFromSelectionTimeout;
1184 
1185    SaveStatusStrings();
1186    sprintf(gszMsgBox, TgLoadString(STID_CONVERTING_GIVEN_DOTS),
1187          (compound_text ? "COMPOUND_TEXT" : "TEXT"));
1188    SetStringStatus(gszMsgBox);
1189    while (!done) {
1190       if (XPending(mainDisplay)) {
1191          XNextEvent(mainDisplay, &ev);
1192          if (ev.type == SelectionNotify) {
1193             XSelectionEvent *xselectionev=(&ev.xselection);
1194 
1195             if (xselectionev->property == None) {
1196                /* conversion has been refused */
1197                if (paste_utf8_string) {
1198                   if (need_to_try_utf8) {
1199                      need_to_try_utf8 = FALSE;
1200                      need_to_try_text = TRUE;
1201                      XConvertSelection(mainDisplay, which_selection,
1202                            utf8StringAtom, tmpSelectionAtom, mainWindow,
1203                            lastKeyOrBtnEvInfo.time);
1204                      continue;
1205                   } else if (need_to_try_text) {
1206                      need_to_try_text = FALSE;
1207                      XConvertSelection(mainDisplay, which_selection,
1208                            textAtom, tmpSelectionAtom, mainWindow,
1209                            lastKeyOrBtnEvInfo.time);
1210                      continue;
1211                   }
1212                } else {
1213                   if (need_to_try_text) {
1214                      need_to_try_text = FALSE;
1215                      need_to_try_utf8 = TRUE;
1216                      XConvertSelection(mainDisplay, which_selection,
1217                            textAtom, tmpSelectionAtom, mainWindow,
1218                            lastKeyOrBtnEvInfo.time);
1219                      continue;
1220                   } else if (need_to_try_utf8) {
1221                      need_to_try_utf8 = FALSE;
1222                      XConvertSelection(mainDisplay, which_selection,
1223                            utf8StringAtom, tmpSelectionAtom, mainWindow,
1224                            lastKeyOrBtnEvInfo.time);
1225                      continue;
1226                   }
1227                }
1228                if (pasteFromXSelectionOnly) {
1229                   Msg(TgLoadString(STID_SELECTION_CONV_REFUSED));
1230                } else {
1231                   TwoLineMsg(TgLoadString(STID_SELECTION_CONV_REFUSED),
1232                         TgLoadString(STID_TRY_PASTE_WITH_OLD_X_MECH));
1233                }
1234             } else {
1235                cut_buffer = GetTextBytesFromWindowProperty(
1236                      ev.xselection.requestor,
1237                      ev.xselection.property, compound_text, pulLen, True);
1238                if (cut_buffer == NULL || (pulLen != NULL && *pulLen == 0L)) {
1239                   if (paste_utf8_string) {
1240                      if (need_to_try_utf8) {
1241                         need_to_try_utf8 = FALSE;
1242                         need_to_try_text = TRUE;
1243                         XConvertSelection(mainDisplay, which_selection,
1244                               utf8StringAtom, tmpSelectionAtom, mainWindow,
1245                               lastKeyOrBtnEvInfo.time);
1246                         continue;
1247                      } else if (need_to_try_text) {
1248                         need_to_try_text = FALSE;
1249                         XConvertSelection(mainDisplay, which_selection,
1250                               textAtom, tmpSelectionAtom, mainWindow,
1251                               lastKeyOrBtnEvInfo.time);
1252                         continue;
1253                      }
1254                   } else {
1255                      if (need_to_try_text) {
1256                         need_to_try_text = FALSE;
1257                         need_to_try_utf8 = TRUE;
1258                         XConvertSelection(mainDisplay, which_selection,
1259                               textAtom, tmpSelectionAtom, mainWindow,
1260                               lastKeyOrBtnEvInfo.time);
1261                         continue;
1262                      } else if (need_to_try_utf8) {
1263                         need_to_try_utf8 = FALSE;
1264                         XConvertSelection(mainDisplay, which_selection,
1265                               utf8StringAtom, tmpSelectionAtom, mainWindow,
1266                               lastKeyOrBtnEvInfo.time);
1267                         continue;
1268                      }
1269                   }
1270                }
1271             }
1272             done = TRUE;
1273          } else if (ev.type == SelectionRequest) {
1274             HandleSelectionRequest(&ev);
1275          }
1276       } else {
1277          MillisecondSleep(100);
1278       }
1279       time(&tloc);
1280       if (tloc >= endtime) break;
1281    }
1282    RestoreStatusStrings();
1283 
1284    if (!done) {
1285       if (*pn_timeout_msg_displayed) {
1286          /* don't display the timeout message again */
1287       } else {
1288          *pn_timeout_msg_displayed = TRUE;
1289          sprintf(gszMsgBox, TgLoadString(STID_TIMEOUT_CONVERT_GIVEN),
1290                (compound_text ? "COMPOUND_TEXT" : "TEXT"));
1291          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
1292       }
1293       UtilFree(cut_buffer);
1294 
1295       return NULL;
1296    }
1297    if (cut_buffer != NULL) {
1298       if (paste_utf8_string && CutBufferType(cut_buffer) != CBF_TGIF_OBJ) {
1299          if (!ConvertToOrFromUTF8(CONVERT_FROM_UTF8,
1300                (canvasFontDoubleByte ? curFont : pssi->font), &cut_buffer)) {
1301             UtilFree(cut_buffer);
1302             return NULL;
1303          }
1304       } else {
1305 #ifndef _NO_KINPUT
1306          if (cut_buffer) CvtCompoundTextToEuc(cut_buffer, cut_buffer);
1307 #endif /* ~_NO_KINPUT */
1308       }
1309    }
1310    return cut_buffer;
1311 }
1312 
GetTextBytesFromSelection(compound_text,pulLen)1313 char *GetTextBytesFromSelection(compound_text, pulLen)
1314    int compound_text;
1315    unsigned long *pulLen;
1316    /* the returned string should be freed by calling UtilFree() */
1317 {
1318    int i=0, paste_utf8_string=FALSE, timeout_msg_displayed=FALSE;
1319    char *cut_buffer=NULL;
1320    StrSegInfo ssi;
1321 
1322    if (pulLen != NULL) *pulLen = 0L;
1323 
1324    memset(&ssi, 0, sizeof(StrSegInfo));
1325    if (curChoice == DRAWTEXT && textCursorShown && pastingUTF8String) {
1326       if (!textHighlight) {
1327          if (canvasFontDoubleByte && CanPasteUTF8StringIntoText(&ssi)) {
1328             if (ssi.double_byte) {
1329                SetCopyUTF8FontInfo(&ssi, TRUE);
1330             }
1331             paste_utf8_string = TRUE;
1332          } else if (CanFindDoubleByteFontAtCursor(&ssi)) {
1333             if (ssi.double_byte) {
1334                SetCopyUTF8FontInfo(&ssi, TRUE);
1335             }
1336             paste_utf8_string = TRUE;
1337          }
1338       } else if (CanPasteUTF8StringIntoText(&ssi)) {
1339          if (ssi.double_byte) {
1340             SetCopyUTF8FontInfo(&ssi, TRUE);
1341          }
1342          paste_utf8_string = TRUE;
1343       }
1344    }
1345    SaveStatusStrings();
1346    sprintf(gszMsgBox, TgLoadString(STID_CONVERTING_GIVEN_DOTS),
1347          (compound_text ? "COMPOUND_TEXT" : "TEXT"));
1348    SetStringStatus(gszMsgBox);
1349    for (i=0; gaAllSelectionAtom[i] != (Atom)0; i++) {
1350       cut_buffer = GetTextBytesFromGivenSelection(compound_text, pulLen,
1351             gaAllSelectionAtom[i], paste_utf8_string, &ssi,
1352             &timeout_msg_displayed);
1353       if (cut_buffer == NULL || (pulLen != NULL && *pulLen == 0L)) {
1354          continue;
1355       } else {
1356          return cut_buffer;
1357       }
1358    }
1359    RestoreStatusStrings();
1360    /*
1361     * If nothing else worked, go back to the the old way of getting data from
1362     *       the root window.
1363     */
1364    if (!timeout_msg_displayed) {
1365       cut_buffer = GetTextBytesFromWindowProperty(rootWindow, XA_CUT_BUFFER0,
1366             compound_text, pulLen, False);
1367       if (cut_buffer != NULL) {
1368          if (paste_utf8_string && CutBufferType(cut_buffer) != CBF_TGIF_OBJ) {
1369             if (!ConvertToOrFromUTF8(CONVERT_FROM_UTF8,
1370                   (canvasFontDoubleByte ? curFont: ssi.font), &cut_buffer)) {
1371                UtilFree(cut_buffer);
1372                return NULL;
1373             }
1374          } else {
1375 #ifndef _NO_KINPUT
1376             if (cut_buffer) CvtCompoundTextToEuc(cut_buffer, cut_buffer);
1377 #endif /* ~_NO_KINPUT */
1378          }
1379       }
1380    }
1381    return cut_buffer;
1382 }
1383 
PasteCompoundText()1384 int PasteCompoundText()
1385 {
1386    unsigned long uLen=0L;
1387    char *cut_buffer=NULL;
1388 
1389    if (curChoice == DRAWTEXT) {
1390       XEvent ev;
1391 
1392       pasteInDrawTextMode = TRUE;
1393       pasteCompoundTextInDrawTextMode = TRUE;
1394       ev.type = KeyPress;
1395       DrawText(&ev);
1396       return FALSE;
1397    }
1398    cut_buffer = GetTextBytesFromSelection(TRUE, &uLen);
1399    if (cut_buffer == NULL || uLen == 0L) {
1400       return TRUE;
1401    }
1402    Msg(TgLoadString(STID_PASTE_COMP_TEXT_FROM_NON_TGIF));
1403    PasteString(cut_buffer, TRUE, TRUE);
1404    UtilFree(cut_buffer);
1405 
1406    return TRUE;
1407 }
1408 
CopyDoubleByteString()1409 int CopyDoubleByteString()
1410    /* returns FALSE if copying in text mode -- this is */
1411    /*    interpreted as an attempt to copy highlighted text */
1412 {
1413    int handle_edit_text_size=FALSE;
1414 
1415    if (curChoice == DRAWTEXT) {
1416       if (escPressed) {
1417          escPressed = FALSE;
1418          Msg(TgLoadString(STID_ESC_KEY_PRESS_IGNORED));
1419       }
1420       if (!textHighlight) {
1421          MsgBox(TgLoadString(STID_NO_TEXT_SELECTED_FOR_COPY), TOOL_NAME,
1422                INFO_MB);
1423          return FALSE;
1424       } else {
1425          if (editTextSize != 0) {
1426             handle_edit_text_size = TRUE;
1427             if (RestoreEditTextSize(curTextObj, TRUE)) {
1428                UpdTextBBox(curTextObj);
1429             }
1430          }
1431          if (CanCopyHighLightedTextAsUTF8Strings(NULL)) {
1432             XEvent ev;
1433 
1434             if (handle_edit_text_size && editTextSize != 0) {
1435                if (RestoreEditTextSize(curTextObj, FALSE)) {
1436                   UpdTextBBox(curTextObj);
1437                }
1438             }
1439             copyInDrawTextMode = TRUE;
1440             copyDoubleByteStringInDrawTextMode = TRUE;
1441             ev.type = KeyPress;
1442             DrawText(&ev);
1443             copyDoubleByteStringInDrawTextMode = FALSE;
1444             copyInDrawTextMode = FALSE;
1445 
1446             return FALSE;
1447          }
1448       }
1449    }
1450    return TRUE;
1451 }
1452 
PasteDoubleByteString()1453 int PasteDoubleByteString()
1454 {
1455    int rc=0;
1456 
1457    pastingUTF8String = TRUE;
1458    rc = PasteFromCutBuffer();
1459    pastingUTF8String = FALSE;
1460 
1461    return rc;
1462 }
1463 
CleanUpCutBuffer()1464 void CleanUpCutBuffer()
1465 {
1466    copyingToCutBuffer = FALSE;
1467 }
1468 
1469 /* ----------------------- Properties ----------------------- */
1470 
1471 static
SkipIntProp(lWhich,plSkip)1472 void SkipIntProp(lWhich, plSkip)
1473    long lWhich, *plSkip;
1474 {
1475    (*plSkip) |= lWhich;
1476 }
1477 
1478 static
TestStringProp(n1,n2,psz1,psz2,lWhich,plSkip)1479 void TestStringProp(n1, n2, psz1, psz2, lWhich, plSkip)
1480    int n1, n2;
1481    char *psz1, *psz2; /* psz2 cannot be NULL */
1482    long lWhich, *plSkip;
1483 {
1484    if (n1 != n2 ||
1485          ((psz1 == NULL || *psz1 == '\0') && *psz2 != '\0') ||
1486          ((psz1 != NULL && *psz1 != '\0') && strcmp(psz1, psz2) != 0)) {
1487       SkipIntProp(lWhich, plSkip);
1488    }
1489 }
1490 
1491 static
SetStringProp(n1,pn2,psz1,psz2)1492 void SetStringProp(n1, pn2, psz1, psz2)
1493    int n1, *pn2;
1494    char *psz1, *psz2; /* psz2 cannot be NULL */
1495 {
1496    *pn2 = n1;
1497    if (psz1 == NULL) {
1498       *psz2 = '\0';
1499    } else {
1500       strcpy(psz2, psz1);
1501    }
1502 }
1503 
1504 static
TestIntProp(n1,n2,lWhich,plSkip)1505 void TestIntProp(n1, n2, lWhich, plSkip)
1506    int n1, n2;
1507    long lWhich, *plSkip;
1508 {
1509    if (n1 != n2) SkipIntProp(lWhich, plSkip);
1510 }
1511 
1512 static
TestFontProp(nDoubleByte1,nFont1,nStyle1,nDoubleByte2,nFont2,nStyle2,lWhich,plSkip)1513 void TestFontProp(nDoubleByte1, nFont1, nStyle1, nDoubleByte2, nFont2,
1514       nStyle2, lWhich, plSkip)
1515    int nDoubleByte1, nFont1, nStyle1;
1516    int nDoubleByte2, nFont2, nStyle2;
1517    long lWhich, *plSkip;
1518 {
1519    if (nStyle1 != nStyle2) {
1520       SkipIntProp(lWhich, plSkip);
1521    } else if (nDoubleByte1 != nDoubleByte2) {
1522       SkipIntProp(lWhich, plSkip);
1523    } else if (nDoubleByte1) {
1524       if (nFont1 != nFont2) {
1525          SkipIntProp(lWhich, plSkip);
1526       }
1527    } else {
1528       if (nFont1 != nFont2) {
1529          SkipIntProp(lWhich, plSkip);
1530       }
1531    }
1532 }
1533 
1534 static
TestCTMProp(ctm1,transformed,ctm2,lWhich,plSkip)1535 void TestCTMProp(ctm1, transformed, ctm2, lWhich, plSkip)
1536    struct XfrmMtrxRec *ctm1, *ctm2;
1537    int transformed;
1538    long lWhich, *plSkip;
1539 {
1540    if ((ctm1 == NULL && transformed) ||
1541          (ctm1 != NULL && (!transformed ||
1542          ctm1->m[0] != ctm2->m[0] ||
1543          ctm1->m[1] != ctm2->m[1] ||
1544          ctm1->m[2] != ctm2->m[2] ||
1545          ctm1->m[3] != ctm2->m[3] ||
1546          ctm1->t[0] != ctm2->t[0] ||
1547          ctm1->t[1] != ctm2->t[1]))) {
1548       SkipIntProp(lWhich, plSkip);
1549    }
1550 }
1551 
SetIntPropertyMask(lWhich,nValue,pszValue,plMask,plSkip,pProp)1552 void SetIntPropertyMask(lWhich, nValue, pszValue, plMask, plSkip, pProp)
1553    long lWhich, *plMask, *plSkip;
1554    int nValue;
1555    char *pszValue;
1556    struct PropertiesRec *pProp;
1557 {
1558    if (((*plSkip) & lWhich) == lWhich) {
1559       /*
1560        * this property is inconsistent, continue skipping it
1561        */
1562    } else if (((*plMask) & lWhich) == lWhich) {
1563       /*
1564        * this property was set before, if it's not the same, skip/invalidate it
1565        */
1566       switch (lWhich) {
1567       case PROP_MASK_COLOR:
1568          TestStringProp(nValue, pProp->color, pszValue, pProp->color_str,
1569                lWhich, plSkip);
1570          break;
1571       case PROP_MASK_WIDTH:
1572          TestStringProp(nValue, pProp->width, pszValue, pProp->width_spec,
1573                lWhich, plSkip);
1574          break;
1575       case PROP_MASK_AW:
1576          TestStringProp(nValue, pProp->aw, pszValue, pProp->aw_spec,
1577                lWhich, plSkip);
1578          break;
1579       case PROP_MASK_AH:
1580          TestStringProp(nValue, pProp->ah, pszValue, pProp->ah_spec,
1581                lWhich, plSkip);
1582          break;
1583       case PROP_MASK_TRANSPAT:
1584          TestIntProp(nValue, pProp->trans_pat, lWhich, plSkip);
1585          break;
1586       case PROP_MASK_FILL:
1587          TestIntProp(nValue, pProp->fill, lWhich, plSkip);
1588          break;
1589       case PROP_MASK_PEN:
1590          TestIntProp(nValue, pProp->pen, lWhich, plSkip);
1591          break;
1592       case PROP_MASK_DASH:
1593          TestIntProp(nValue, pProp->dash, lWhich, plSkip);
1594          break;
1595       case PROP_MASK_ARROW_STYLE:
1596          TestIntProp(nValue, pProp->arrow_style, lWhich, plSkip);
1597          break;
1598       case PROP_MASK_CURVED:
1599          TestIntProp(nValue, pProp->curved, lWhich, plSkip);
1600          break;
1601       case PROP_MASK_RCB_RADIUS:
1602          TestIntProp(nValue, pProp->rcb_radius, lWhich, plSkip);
1603          break;
1604       case PROP_MASK_TEXT_JUST:
1605          TestIntProp(nValue, pProp->text_just, lWhich, plSkip);
1606          break;
1607       case PROP_MASK_TEXT_SZ_UNIT:
1608          TestIntProp(nValue, pProp->text_sz_unit, lWhich, plSkip);
1609          break;
1610       case PROP_MASK_UNDERLINE_ON:
1611          TestIntProp(nValue, pProp->underline_on, lWhich, plSkip);
1612          break;
1613       case PROP_MASK_OVERLINE_ON:
1614          TestIntProp(nValue, pProp->overline_on, lWhich, plSkip);
1615          break;
1616       }
1617    } else {
1618       /* this property is never set, go set it */
1619       (*plMask) |= lWhich;
1620       switch (lWhich) {
1621       case PROP_MASK_COLOR:
1622          SetStringProp(nValue, &pProp->color, pszValue, pProp->color_str);
1623          break;
1624       case PROP_MASK_WIDTH:
1625          SetStringProp(nValue, &pProp->width, pszValue, pProp->width_spec);
1626          break;
1627       case PROP_MASK_AW:
1628          SetStringProp(nValue, &pProp->aw, pszValue, pProp->aw_spec);
1629          break;
1630       case PROP_MASK_AH:
1631          SetStringProp(nValue, &pProp->ah, pszValue, pProp->ah_spec);
1632          break;
1633       case PROP_MASK_TRANSPAT: pProp->trans_pat = nValue; break;
1634       case PROP_MASK_FILL: pProp->fill = nValue; break;
1635       case PROP_MASK_PEN: pProp->pen = nValue; break;
1636       case PROP_MASK_DASH: pProp->dash = nValue; break;
1637       case PROP_MASK_ARROW_STYLE: pProp->arrow_style = nValue; break;
1638       case PROP_MASK_CURVED: pProp->curved = nValue; break;
1639       case PROP_MASK_RCB_RADIUS: pProp->rcb_radius = nValue; break;
1640       case PROP_MASK_TEXT_JUST: pProp->text_just = nValue; break;
1641       case PROP_MASK_TEXT_SZ_UNIT: pProp->text_sz_unit = nValue; break;
1642       case PROP_MASK_VSPACE: pProp->v_space = nValue; break;
1643       case PROP_MASK_UNDERLINE_ON: pProp->underline_on = nValue; break;
1644       case PROP_MASK_OVERLINE_ON: pProp->overline_on = nValue; break;
1645       }
1646    }
1647 }
1648 
SetFontPropertyMask(nDoubleByte,nFont,nStyle,plMask,plSkip,pProp)1649 void SetFontPropertyMask(nDoubleByte, nFont, nStyle, plMask, plSkip, pProp)
1650    int nDoubleByte, nFont, nStyle;
1651    long *plMask, *plSkip;
1652    struct PropertiesRec *pProp;
1653 {
1654    if (((*plSkip) & PROP_MASK_TEXT_FONT) == PROP_MASK_TEXT_FONT) {
1655       /*
1656        * this property is inconsistent, continue skipping it
1657        */
1658    } else if (((*plMask) & PROP_MASK_TEXT_FONT) == PROP_MASK_TEXT_FONT) {
1659       /*
1660        * this property was set before, if it's not the same, skip/invalidate it
1661        */
1662       TestFontProp(nDoubleByte, nFont, nStyle, pProp->double_byte,
1663             pProp->text_font, pProp->text_style, PROP_MASK_TEXT_FONT, plSkip);
1664    } else {
1665       /* this property is never set, go set it */
1666       (*plMask) |= PROP_MASK_TEXT_FONT;
1667       pProp->double_byte = nDoubleByte;
1668       pProp->text_font = nFont;
1669       pProp->text_style = nStyle;
1670    }
1671 }
1672 
SetCTMPropertyMask(ctm,plMask,plSkip,pProp)1673 void SetCTMPropertyMask(ctm, plMask, plSkip, pProp)
1674    struct XfrmMtrxRec *ctm;
1675    long *plMask, *plSkip;
1676    struct PropertiesRec *pProp;
1677 {
1678    if (((*plSkip) & PROP_MASK_CTM) == PROP_MASK_CTM) {
1679       /*
1680        * this property is inconsistent, continue skipping it
1681        */
1682    } else if (((*plMask) & PROP_MASK_CTM) == PROP_MASK_CTM) {
1683       /*
1684        * this property was set before, if it's not the same, skip/invalidate it
1685        */
1686       TestCTMProp(ctm, pProp->transformed, &pProp->ctm, PROP_MASK_CTM, plSkip);
1687    } else {
1688       /* this property is never set, go set it */
1689       (*plMask) |= PROP_MASK_CTM;
1690       if (ctm == NULL) {
1691          pProp->transformed = FALSE;
1692       } else {
1693          pProp->transformed = TRUE;
1694          memcpy(&pProp->ctm, ctm, sizeof(struct XfrmMtrxRec));
1695       }
1696    }
1697 }
1698 
SetTextPropMask(ObjPtr,plMask,plSkip,pProp)1699 void SetTextPropMask(ObjPtr, plMask, plSkip, pProp)
1700    struct ObjRec *ObjPtr;
1701    long *plMask, *plSkip;
1702    struct PropertiesRec *pProp;
1703 {
1704    struct TextRec *text_ptr=ObjPtr->detail.t;
1705    int sz_unit=INVALID, double_byte=INVALID, font=INVALID, style=INVALID;
1706    int underline_on=FALSE, overline_on=FALSE;
1707 
1708    SetCTMPropertyMask(ObjPtr->ctm, plMask, plSkip, pProp);
1709 
1710    SetIntPropertyMask(PROP_MASK_COLOR, ObjPtr->color,
1711          colorMenuItems[ObjPtr->color], plMask, plSkip, pProp);
1712 
1713    SetIntPropertyMask(PROP_MASK_TRANSPAT, ObjPtr->trans_pat, NULL,
1714          plMask, plSkip, pProp);
1715    SetIntPropertyMask(PROP_MASK_FILL, text_ptr->fill, NULL,
1716          plMask, plSkip, pProp);
1717    SetIntPropertyMask(PROP_MASK_PEN, text_ptr->pen, NULL,
1718          plMask, plSkip, pProp);
1719    SetIntPropertyMask(PROP_MASK_TEXT_JUST, text_ptr->minilines.just, NULL,
1720          plMask, plSkip, pProp);
1721    SetIntPropertyMask(PROP_MASK_VSPACE, text_ptr->minilines.v_space, NULL,
1722          plMask, plSkip, pProp);
1723 
1724    if (SingleFontText(text_ptr, &sz_unit, &double_byte, &font, &style,
1725          &underline_on, &overline_on)) {
1726       SetIntPropertyMask(PROP_MASK_TEXT_SZ_UNIT, sz_unit, NULL,
1727             plMask, plSkip, pProp);
1728       SetIntPropertyMask(PROP_MASK_UNDERLINE_ON, underline_on, NULL,
1729             plMask, plSkip, pProp);
1730       SetIntPropertyMask(PROP_MASK_OVERLINE_ON, overline_on, NULL,
1731             plMask, plSkip, pProp);
1732       SetFontPropertyMask(double_byte, font, style, plMask, plSkip, pProp);
1733    }
1734 }
1735 
1736 static
SetPropMask(ObjPtr,plMask,plSkip,pProp)1737 void SetPropMask(ObjPtr, plMask, plSkip, pProp)
1738    struct ObjRec *ObjPtr;
1739    long *plMask, *plSkip;
1740    struct PropertiesRec *pProp;
1741 {
1742    if (curChoice == DRAWTEXT) {
1743       if (textCursorShown && textHighlight) {
1744          SetIntPropertyMask(PROP_MASK_TRANSPAT, transPat, NULL,
1745                plMask, plSkip, pProp);
1746          SetIntPropertyMask(PROP_MASK_FILL, objFill, NULL,
1747                plMask, plSkip, pProp);
1748          SetIntPropertyMask(PROP_MASK_PEN, penPat, NULL,
1749                plMask, plSkip, pProp);
1750 
1751          SetIntPropertyMask(PROP_MASK_TEXT_JUST, textJust, NULL,
1752                plMask, plSkip, pProp);
1753          SetIntPropertyMask(PROP_MASK_VSPACE, textVSpace, NULL,
1754                plMask, plSkip, pProp);
1755 
1756          if (HighlightedTextHasSameProperty(PROP_MASK_COLOR,
1757                curStrBlock->seg->color, TRUE)) {
1758             SetIntPropertyMask(PROP_MASK_COLOR, colorIndex,
1759                   colorMenuItems[colorIndex], plMask, plSkip, pProp);
1760          }
1761          if (HighlightedTextHasSameProperty(PROP_MASK_TEXT_SZ_UNIT,
1762                curStrBlock->seg->sz_unit, TRUE)) {
1763             SetIntPropertyMask(PROP_MASK_TEXT_SZ_UNIT, GetCurSzUnit(), NULL,
1764                   plMask, plSkip, pProp);
1765          }
1766          if (HighlightedTextHasSameProperty(PROP_MASK_UNDERLINE_ON,
1767                curStrBlock->seg->underline_on, TRUE)) {
1768             SetIntPropertyMask(PROP_MASK_UNDERLINE_ON, curUnderlineOn, NULL,
1769                   plMask, plSkip, pProp);
1770          }
1771          if (HighlightedTextHasSameProperty(PROP_MASK_OVERLINE_ON,
1772                curStrBlock->seg->overline_on, TRUE)) {
1773             SetIntPropertyMask(PROP_MASK_OVERLINE_ON, curOverlineOn, NULL,
1774                   plMask, plSkip, pProp);
1775          }
1776          if (HighlightedTextHasSameProperty(PROP_MASK_TEXT_FONT,
1777                curStrBlock->seg->font, TRUE) &&
1778                HighlightedTextHasSameProperty(PROP_MASK_TEXT_STYLE,
1779                curStrBlock->seg->style, TRUE)) {
1780             SetFontPropertyMask(canvasFontDoubleByte, curFont, curStyle, plMask,
1781                   plSkip, pProp);
1782          }
1783       } else {
1784          SetIntPropertyMask(PROP_MASK_TRANSPAT, transPat, NULL,
1785                plMask, plSkip, pProp);
1786          SetIntPropertyMask(PROP_MASK_FILL, objFill, NULL,
1787                plMask, plSkip, pProp);
1788          SetIntPropertyMask(PROP_MASK_PEN, penPat, NULL,
1789                plMask, plSkip, pProp);
1790 
1791          SetIntPropertyMask(PROP_MASK_TEXT_JUST, textJust, NULL,
1792                plMask, plSkip, pProp);
1793          SetIntPropertyMask(PROP_MASK_VSPACE, textVSpace, NULL,
1794                plMask, plSkip, pProp);
1795 
1796          SetIntPropertyMask(PROP_MASK_COLOR, colorIndex,
1797                colorMenuItems[colorIndex], plMask, plSkip, pProp);
1798          SetIntPropertyMask(PROP_MASK_TEXT_SZ_UNIT, GetCurSzUnit(), NULL,
1799                plMask, plSkip, pProp);
1800          SetIntPropertyMask(PROP_MASK_UNDERLINE_ON, curUnderlineOn, NULL,
1801                plMask, plSkip, pProp);
1802          SetIntPropertyMask(PROP_MASK_OVERLINE_ON, curOverlineOn, NULL,
1803                plMask, plSkip, pProp);
1804          SetFontPropertyMask(canvasFontDoubleByte, curFont, curStyle, plMask,
1805                plSkip, pProp);
1806       }
1807    } else if (ObjPtr == NULL) {
1808       double radian=((double)(textRotation))*M_PI/180.0/64.0;
1809       double sin_val=sin(radian);
1810       double cos_val=cos(radian);
1811       struct XfrmMtrxRec ctm;
1812 
1813       ctm.m[CTM_SX] = ctm.m[CTM_SY] = ((double)1000)*cos_val;
1814       ctm.m[CTM_SIN] = ((double)1000)*sin_val;
1815       ctm.m[CTM_MSIN] = (-ctm.m[CTM_SIN]);
1816       ctm.t[CTM_TX] = ctm.t[CTM_TY] = 0;
1817       SetCTMPropertyMask(&ctm, plMask, plSkip, pProp);
1818 
1819       SetIntPropertyMask(PROP_MASK_COLOR, colorIndex,
1820             colorMenuItems[colorIndex], plMask, plSkip, pProp);
1821       SetIntPropertyMask(PROP_MASK_WIDTH, curWidthOfLine[lineWidth],
1822             curWidthOfLineSpec[lineWidth],
1823             plMask, plSkip, pProp);
1824       SetIntPropertyMask(PROP_MASK_AW, curArrowHeadW[lineWidth],
1825             curArrowHeadWSpec[lineWidth],
1826             plMask, plSkip, pProp);
1827       SetIntPropertyMask(PROP_MASK_AH, curArrowHeadH[lineWidth],
1828             curArrowHeadHSpec[lineWidth],
1829             plMask, plSkip, pProp);
1830 
1831       SetIntPropertyMask(PROP_MASK_TRANSPAT, transPat, NULL,
1832             plMask, plSkip, pProp);
1833       SetIntPropertyMask(PROP_MASK_FILL, objFill, NULL,
1834             plMask, plSkip, pProp);
1835       SetIntPropertyMask(PROP_MASK_PEN, penPat, NULL,
1836             plMask, plSkip, pProp);
1837       SetIntPropertyMask(PROP_MASK_DASH, curDash, NULL,
1838             plMask, plSkip, pProp);
1839       SetIntPropertyMask(PROP_MASK_CURVED, curSpline, NULL,
1840             plMask, plSkip, pProp);
1841       SetIntPropertyMask(PROP_MASK_ARROW_STYLE, lineStyle, NULL,
1842             plMask, plSkip, pProp);
1843       SetIntPropertyMask(PROP_MASK_RCB_RADIUS, rcbRadius, NULL,
1844             plMask, plSkip, pProp);
1845 
1846       SetIntPropertyMask(PROP_MASK_TEXT_JUST, textJust, NULL,
1847             plMask, plSkip, pProp);
1848       SetIntPropertyMask(PROP_MASK_TEXT_SZ_UNIT, GetCurSzUnit(), NULL,
1849             plMask, plSkip, pProp);
1850       SetIntPropertyMask(PROP_MASK_VSPACE, textVSpace, NULL,
1851             plMask, plSkip, pProp);
1852       SetIntPropertyMask(PROP_MASK_UNDERLINE_ON, curUnderlineOn, NULL,
1853             plMask, plSkip, pProp);
1854       SetIntPropertyMask(PROP_MASK_OVERLINE_ON, curOverlineOn, NULL,
1855             plMask, plSkip, pProp);
1856 
1857       SetFontPropertyMask(canvasFontDoubleByte, curFont, curStyle, plMask,
1858             plSkip, pProp);
1859    } else {
1860       struct ObjRec *obj_ptr=NULL;
1861 
1862       switch (ObjPtr->type) {
1863       case OBJ_POLY: SetPolyPropMask(ObjPtr, plMask, plSkip, pProp); break;
1864       case OBJ_BOX: SetBoxPropMask(ObjPtr, plMask, plSkip, pProp); break;
1865       case OBJ_OVAL: SetOvalPropMask(ObjPtr, plMask, plSkip, pProp); break;
1866       case OBJ_TEXT: SetTextPropMask(ObjPtr, plMask, plSkip, pProp); break;
1867       case OBJ_POLYGON:
1868          SetPolygonPropMask(ObjPtr, plMask, plSkip, pProp);
1869          break;
1870       case OBJ_ARC: SetArcPropMask(ObjPtr, plMask, plSkip, pProp); break;
1871       case OBJ_RCBOX: SetRCBoxPropMask(ObjPtr, plMask, plSkip, pProp); break;
1872       case OBJ_XBM: SetXBmPropMask(ObjPtr, plMask, plSkip, pProp); break;
1873       case OBJ_XPM: SetXPmPropMask(ObjPtr, plMask, plSkip, pProp); break;
1874 
1875       case OBJ_GROUP:
1876       case OBJ_ICON:
1877       case OBJ_SYM:
1878       case OBJ_PIN:
1879          for (obj_ptr=ObjPtr->detail.r->first; obj_ptr != NULL;
1880                obj_ptr=obj_ptr->next) {
1881             SetPropMask(obj_ptr, plMask, plSkip, pProp);
1882          }
1883          break;
1884       }
1885    }
1886 }
1887 
1888 static
GetAngleFromCTM(ctm)1889 int GetAngleFromCTM(ctm)
1890    struct XfrmMtrxRec *ctm;
1891    /* returned angle is degree*64 */
1892 {
1893    double sin_val=(double)(ctm->m[CTM_SIN]/((double)1000));
1894    double cos_val=(double)(ctm->m[CTM_SX]/((double)1000));
1895    double radian=(double)0;
1896    int angle=0;
1897 
1898    if (fabs(sin_val) < EQ_TOL) {
1899       if (cos_val > 0) {
1900          radian = (double)0;
1901       } else {
1902          radian = (double)M_PI;
1903       }
1904    } else if (fabs(cos_val) < EQ_TOL) {
1905       if (sin_val> 0) {
1906          radian = (double)(M_PI/2.0);
1907       } else {
1908          radian = (double)(M_PI*3.0/2.0);
1909       }
1910    } else {
1911       radian = (double)atan(sin_val/cos_val);
1912       if (radian >= 0) {
1913          if (sin_val < 0) {
1914             radian += (double)M_PI;
1915          }
1916       } else {
1917          if (cos_val > 0) {
1918             radian += (double)(M_PI*2.0);
1919          } else {
1920             radian += (double)M_PI;
1921          }
1922       }
1923    }
1924    angle = (int)(radian*((double)180)*((double)64)/M_PI);
1925    return angle;
1926 }
1927 
1928 static
DoPasteAProperty(lWhich,pProp)1929 void DoPasteAProperty(lWhich, pProp)
1930    long lWhich;
1931    struct PropertiesRec *pProp;
1932 {
1933    char szBuf[MAXSTRING];
1934    int index=0, new_alloc=FALSE;
1935 
1936    switch (lWhich) {
1937    case PROP_MASK_COLOR:
1938       index = QuickFindColorIndex(NULL, pProp->color_str, &new_alloc, FALSE);
1939       if (index == INVALID) {
1940          sprintf(gszMsgBox, TgLoadString(STID_FAIL_ALLOC_NAMED_COLOR),
1941                pProp->color_str);
1942          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
1943          return;
1944       } else {
1945          ChangeAllSelColor(index, TRUE);
1946       }
1947       break;
1948    case PROP_MASK_WIDTH:
1949       ChangeAllSelRealLineWidth(CHANGE_WIDTH, pProp->width, (-1), (-1),
1950             pProp->width_spec, NULL, NULL, TRUE);
1951       break;
1952    case PROP_MASK_AW:
1953       ChangeAllSelRealLineWidth(CHANGE_AW, (-1), pProp->aw, (-1),
1954             NULL, pProp->aw_spec, NULL, TRUE);
1955       break;
1956    case PROP_MASK_AH:
1957       ChangeAllSelRealLineWidth(CHANGE_AH, (-1), (-1), pProp->ah,
1958             NULL, NULL, pProp->ah_spec, TRUE);
1959       break;
1960 
1961    case PROP_MASK_TRANSPAT: ChangeAllSelTransPat(pProp->trans_pat, TRUE); break;
1962    case PROP_MASK_FILL: ChangeAllSelFill(pProp->fill, TRUE); break;
1963    case PROP_MASK_PEN: ChangeAllSelPen(pProp->pen, TRUE); break;
1964    case PROP_MASK_DASH: ChangeAllSelLineDash(pProp->dash, TRUE); break;
1965    case PROP_MASK_ARROW_STYLE:
1966       ChangeAllSelLineStyle(pProp->arrow_style, TRUE);
1967       break;
1968    case PROP_MASK_CURVED: ChangeAllSelLineType(pProp->curved, TRUE); break;
1969    case PROP_MASK_RCB_RADIUS: ChangeAllSelRCBRadius(pProp->rcb_radius); break;
1970    case PROP_MASK_TEXT_JUST: ChangeFontJust(pProp->text_just); break;
1971    case PROP_MASK_TEXT_SZ_UNIT:
1972       sprintf(szBuf, "%1d", SzUnitToFontSize(pProp->text_sz_unit));
1973       SetSelFontSize(szBuf);
1974       break;
1975    case PROP_MASK_VSPACE: ChangeVSpace(pProp->v_space); break;
1976    case PROP_MASK_UNDERLINE_ON: ChangeFontUnderline(pProp->underline_on); break;
1977    case PROP_MASK_OVERLINE_ON: ChangeFontOverline(pProp->overline_on); break;
1978 
1979    case PROP_MASK_TEXT_FONT:
1980       ChangeFont(pProp->text_font, TRUE);
1981       ChangeFontStyle(pProp->text_style);
1982       break;
1983 
1984    case PROP_MASK_CTM:
1985       if (topSel == NULL) {
1986          if (pProp->transformed) {
1987             int angle=GetAngleFromCTM(&pProp->ctm);
1988 
1989             FormatAngle(angle, szBuf);
1990          } else {
1991             strcpy(szBuf, "0");
1992          }
1993          SetTextRotation(szBuf);
1994       } else {
1995          SetSelCTM(pProp->transformed, &pProp->ctm);
1996       }
1997       break;
1998    }
1999 }
2000 
2001 static
DoGetAProperty(lWhich,pProp)2002 void DoGetAProperty(lWhich, pProp)
2003    long lWhich;
2004    struct PropertiesRec *pProp;
2005 {
2006    char szBuf[MAXSTRING];
2007    int index=0, new_alloc=FALSE;
2008 
2009    switch (lWhich) {
2010    case PROP_MASK_COLOR:
2011       index = QuickFindColorIndex(NULL, pProp->color_str, &new_alloc, FALSE);
2012       if (index == INVALID) {
2013          sprintf(gszMsgBox, TgLoadString(STID_FAIL_ALLOC_NAMED_COLOR),
2014                pProp->color_str);
2015          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2016          return;
2017       } else {
2018          ChangeAllSelColor(index, TRUE);
2019       }
2020       break;
2021    case PROP_MASK_WIDTH_INDEX:
2022       ChangeAllSelLineWidth(pProp->width_index, TRUE);
2023       break;
2024    case PROP_MASK_WIDTH:
2025       ChangeAllSelRealLineWidth(CHANGE_WIDTH, pProp->width, (-1), (-1),
2026             pProp->width_spec, NULL, NULL, TRUE);
2027       break;
2028    case PROP_MASK_AW:
2029       ChangeAllSelRealLineWidth(CHANGE_AW, (-1), pProp->aw, (-1),
2030             NULL, pProp->aw_spec, NULL, TRUE);
2031       break;
2032    case PROP_MASK_AH:
2033       ChangeAllSelRealLineWidth(CHANGE_AH, (-1), (-1), pProp->ah,
2034             NULL, NULL, pProp->ah_spec, TRUE);
2035       break;
2036 
2037    case PROP_MASK_TRANSPAT: ChangeAllSelTransPat(pProp->trans_pat, TRUE); break;
2038    case PROP_MASK_FILL: ChangeAllSelFill(pProp->fill, TRUE); break;
2039    case PROP_MASK_PEN: ChangeAllSelPen(pProp->pen, TRUE); break;
2040    case PROP_MASK_DASH: ChangeAllSelLineDash(pProp->dash, TRUE); break;
2041    case PROP_MASK_ARROW_STYLE:
2042       ChangeAllSelLineStyle(pProp->arrow_style, TRUE);
2043       break;
2044    case PROP_MASK_CURVED: ChangeAllSelLineType(pProp->curved, TRUE); break;
2045    case PROP_MASK_RCB_RADIUS: ChangeAllSelRCBRadius(pProp->rcb_radius); break;
2046    case PROP_MASK_TEXT_JUST: ChangeFontJust(pProp->text_just); break;
2047    case PROP_MASK_TEXT_SZ_UNIT:
2048       sprintf(szBuf, "%1d", SzUnitToFontSize(pProp->text_sz_unit));
2049       SetSelFontSize(szBuf);
2050       break;
2051    case PROP_MASK_VSPACE: ChangeVSpace(pProp->v_space); break;
2052    case PROP_MASK_UNDERLINE_ON: ChangeFontUnderline(pProp->underline_on); break;
2053    case PROP_MASK_OVERLINE_ON: ChangeFontOverline(pProp->overline_on); break;
2054 
2055    case PROP_MASK_TEXT_FONT:
2056       ChangeFont(pProp->text_font, TRUE);
2057       ChangeFontStyle(pProp->text_style);
2058       break;
2059 
2060    case PROP_MASK_CTM:
2061       if (topSel == NULL) {
2062          if (pProp->transformed) {
2063             int angle=GetAngleFromCTM(&pProp->ctm);
2064 
2065             FormatAngle(angle, szBuf);
2066          } else {
2067             strcpy(szBuf, "0");
2068          }
2069          SetTextRotation(szBuf);
2070       } else {
2071          SetSelCTM(pProp->transformed, &pProp->ctm);
2072       }
2073       break;
2074    }
2075 }
2076 
2077 static char gszPropIniFile[MAXPATHLENGTH];
2078 
2079 static char gszCopyPasteSec[MAXSTRING];
2080 static char gszCopyPasteBackupSec[MAXSTRING];
2081 static char gszPropSetSec[MAXSTRING];
2082 static char gszPropProfilePrefix[MAXSTRING];
2083 
2084 struct PropInfoRec {
2085    long bit;
2086    int checked;
2087    char *key;
2088    char *desc;
2089 };
2090 
2091 static struct PropInfoRec gstPropInfo[] = {
2092    { PROP_MASK_AH,           FALSE, NULL, NULL },
2093    { PROP_MASK_AW,           FALSE, NULL, NULL },
2094    { PROP_MASK_ARROW_STYLE,  FALSE, NULL, NULL },
2095    { PROP_MASK_COLOR,        FALSE, NULL, NULL },
2096    { PROP_MASK_DASH,         FALSE, NULL, NULL },
2097    { PROP_MASK_WIDTH,        FALSE, NULL, NULL },
2098    { PROP_MASK_TRANSPAT,     FALSE, NULL, NULL },
2099    { PROP_MASK_FILL,         FALSE, NULL, NULL },
2100    { PROP_MASK_PEN,          FALSE, NULL, NULL },
2101    { PROP_MASK_RCB_RADIUS,   FALSE, NULL, NULL },
2102    { PROP_MASK_CURVED,       FALSE, NULL, NULL },
2103    { PROP_MASK_TEXT_FONT,    FALSE, NULL, NULL },
2104    { PROP_MASK_TEXT_STYLE,   FALSE, NULL, NULL },
2105    { PROP_MASK_TEXT_JUST,    FALSE, NULL, NULL },
2106    { PROP_MASK_TEXT_SZ_UNIT, FALSE, NULL, NULL },
2107    { PROP_MASK_VSPACE,       FALSE, NULL, NULL },
2108    { PROP_MASK_UNDERLINE_ON, FALSE, NULL, NULL },
2109    { PROP_MASK_OVERLINE_ON,  FALSE, NULL, NULL },
2110    { PROP_MASK_CTM,          FALSE, NULL, NULL },
2111    { PROP_MASK_WIDTH_INDEX,  FALSE, NULL, NULL },
2112    { 0L, FALSE, NULL, NULL }
2113 };
2114 
2115 static struct PropInfoRec gstCompatPropInfo[] = {
2116    /*
2117     * do not translate -- program constants
2118     *
2119     * These are used for compatibility reasons.  In an earlier version,
2120     *       the keys in "cutpaste.ini" are these strings.  In the current
2121     *       version, hex numbers are used.  gstCompatPropInfo are only used
2122     *       by UpdateSavedPropKeys() to update the old keys to new ones.
2123     */
2124    { PROP_MASK_AH,           FALSE, NULL, "arrow height" },
2125    { PROP_MASK_AW,           FALSE, NULL, "arrow width" },
2126    { PROP_MASK_ARROW_STYLE,  FALSE, NULL, "arrow style" },
2127    { PROP_MASK_COLOR,        FALSE, NULL, "color" },
2128    { PROP_MASK_DASH,         FALSE, NULL, "dash" },
2129    { PROP_MASK_WIDTH,        FALSE, NULL, "line width" },
2130    { PROP_MASK_TRANSPAT,     FALSE, NULL, "pattern transparency" },
2131    { PROP_MASK_FILL,         FALSE, NULL, "fill" },
2132    { PROP_MASK_PEN,          FALSE, NULL, "pen" },
2133    { PROP_MASK_RCB_RADIUS,   FALSE, NULL, "rcbox radius" },
2134    { PROP_MASK_CURVED,       FALSE, NULL, "spline" },
2135    { PROP_MASK_TEXT_FONT,    FALSE, NULL, "text font" },
2136    { PROP_MASK_TEXT_STYLE,   FALSE, NULL, "text style (no use, part of text font)" },
2137    { PROP_MASK_TEXT_JUST,    FALSE, NULL, "text justification" },
2138    { PROP_MASK_TEXT_SZ_UNIT, FALSE, NULL, "text size" },
2139    { PROP_MASK_VSPACE,       FALSE, NULL, "text vertical spacing" },
2140    { PROP_MASK_UNDERLINE_ON, FALSE, NULL, "text underline" },
2141    { PROP_MASK_CTM,          FALSE, NULL, "transformation matrix" },
2142    { 0L, FALSE, NULL, NULL }
2143 };
2144 
2145 static
WriteIntProp(pszSec,pszKey,nValue)2146 void WriteIntProp(pszSec, pszKey, nValue)
2147    char *pszSec, *pszKey;
2148    int nValue;
2149 {
2150    sprintf(gszMsgBox, "%1d", nValue);
2151    tgWriteProfileString(pszSec, pszKey, gszMsgBox, gszPropIniFile);
2152 }
2153 
2154 static
WriteStringProp(pszSec,pszKey,nValue,pszValue)2155 void WriteStringProp(pszSec, pszKey, nValue, pszValue)
2156    char *pszSec, *pszKey;
2157    int nValue;
2158    char *pszValue;
2159 {
2160    sprintf(gszMsgBox, "%1d,%s", nValue, (pszValue==NULL ? "" : pszValue));
2161    tgWriteProfileString(pszSec, pszKey, gszMsgBox, gszPropIniFile);
2162 }
2163 
2164 static
WriteFontProp(pszSec,pszKey,nFont,nStyle)2165 void WriteFontProp(pszSec, pszKey, nFont, nStyle)
2166    char *pszSec, *pszKey;
2167    int nFont, nStyle;
2168 {
2169    char font_str[MAXSTRING];
2170 
2171    *font_str = '\0';
2172    GetPSFontStr(nFont, nStyle, font_str);
2173    /* font_str starts with the '/' character */
2174    sprintf(gszMsgBox, "%1d,%s", nStyle, &font_str[1]);
2175    tgWriteProfileString(pszSec, pszKey, gszMsgBox, gszPropIniFile);
2176 }
2177 
2178 static
WriteCTMProp(pszSec,pszKey,nTransformed,ctm)2179 void WriteCTMProp(pszSec, pszKey, nTransformed, ctm)
2180    char *pszSec, *pszKey;
2181    int nTransformed;
2182    struct XfrmMtrxRec *ctm;
2183 {
2184    if (nTransformed) {
2185       sprintf(gszMsgBox, "%1d,%g,%g,%g,%g,%1d,%1d", nTransformed,
2186             ctm->m[CTM_SX], ctm->m[CTM_SIN], ctm->m[CTM_MSIN], ctm->m[CTM_SY],
2187             ctm->t[CTM_TX], ctm->t[CTM_TY]);
2188    } else {
2189       sprintf(gszMsgBox, "%1d,%g,%g,%g,%g,%1d,%1d", nTransformed,
2190             (double)1000, (double)0, (double)0, (double)1000, 0, 0);
2191    }
2192    tgWriteProfileString(pszSec, pszKey, gszMsgBox, gszPropIniFile);
2193 }
2194 
2195 static
ParseIntProp(pszBuf,pnValue)2196 int ParseIntProp(pszBuf, pnValue)
2197    char *pszBuf;
2198    int *pnValue;
2199 {
2200    return (sscanf(pszBuf, "%d", pnValue) == 1);
2201 }
2202 
2203 static
ParseStringProp(pszBuf,pnValue,pszValue)2204 int ParseStringProp(pszBuf, pnValue, pszValue)
2205    char *pszBuf;
2206    int *pnValue;
2207    char *pszValue;
2208 {
2209    char *psz1=strtok(pszBuf, ","), *psz2=NULL;
2210 
2211    if (psz1 == NULL) return FALSE;
2212    psz2 = strtok(NULL, ",");
2213    if (psz2 == NULL) return FALSE;
2214 
2215    if (sscanf(psz1, "%d", pnValue) != 1) return FALSE;
2216    strcpy(pszValue, psz2);
2217 
2218    return TRUE;
2219 }
2220 
2221 static
ParseFontSzUnitProp(pszBuf,pnSzUnit)2222 int ParseFontSzUnitProp(pszBuf, pnSzUnit)
2223    char *pszBuf;
2224    int *pnSzUnit;
2225 {
2226    int i, sz_unit=0;
2227 
2228    if (sscanf(pszBuf, "%d", &sz_unit) != 1) return FALSE;
2229    for (i=0; i < numFontSizes; i++) {
2230       if (sz_unit == fontSzUnits[i]) {
2231          *pnSzUnit = sz_unit;
2232          return TRUE;
2233       }
2234    }
2235    if (topSel == NULL) {
2236       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_FIND_SIZE_USE_ALT),
2237             SzUnitToFontSize(sz_unit), defaultFontSize);
2238       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2239       *pnSzUnit = FontSizeToSzUnit(defaultFontSize);
2240    } else {
2241       *pnSzUnit = sz_unit;
2242    }
2243    return TRUE;
2244 }
2245 
2246 static
ParseFontProp(pszBuf,pnDoubleByte,pnFont,pnStyle)2247 int ParseFontProp(pszBuf, pnDoubleByte, pnFont, pnStyle)
2248    char *pszBuf;
2249    int *pnDoubleByte, *pnFont, *pnStyle;
2250 {
2251    char font_str[MAXSTRING], *psz=NULL;
2252 
2253    *font_str = '\0';
2254    psz = strchr(pszBuf, ',');
2255    if (psz == NULL) return FALSE;
2256 
2257    *psz = '\0';
2258    if (sscanf(pszBuf, "%d", pnStyle) != 1) {
2259       *psz = ',';
2260       return FALSE;
2261    }
2262    *psz++ = ',';
2263    strcpy(font_str, psz);
2264    *pnFont = GetFontIndex(font_str, *pnStyle, TRUE);
2265    *pnDoubleByte = FALSE;
2266    if (*pnFont == INVALID) {
2267       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_FIND_FONT_USE_ALT), font_str,
2268             "Times");
2269       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2270       *pnDoubleByte = FALSE;
2271       *pnFont = FONT_TIM;
2272    }
2273    return TRUE;
2274 }
2275 
2276 static
ParseCTMProp(pszBuf,pnTransformed,ctm)2277 int ParseCTMProp(pszBuf, pnTransformed, ctm)
2278    char *pszBuf;
2279    int *pnTransformed;
2280    struct XfrmMtrxRec *ctm;
2281 {
2282    struct PropInfoRec *ppir=NULL;
2283    char *psz=strchr(pszBuf, ',');
2284 
2285    if (psz == NULL) return FALSE;
2286    *psz = '\0';
2287    if (sscanf(pszBuf, "%d", pnTransformed) != 1) {
2288       *psz = ',';
2289       return FALSE;
2290    }
2291    *psz++ = ',';
2292    if (sscanf(psz, "%lg , %lg , %lg, %lg , %d , %d",
2293          &ctm->m[CTM_SX], &ctm->m[CTM_SIN], &ctm->m[CTM_MSIN], &ctm->m[CTM_SY],
2294          &ctm->t[CTM_TX], &ctm->t[CTM_TY]) != 6) {
2295       return FALSE;
2296    }
2297    if (topSel != NULL) {
2298       struct SelRec *sel_ptr=NULL;
2299 
2300       for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
2301          switch (sel_ptr->obj->type) {
2302          case OBJ_GROUP:
2303          case OBJ_ICON:
2304          case OBJ_SYM:
2305          case OBJ_PIN:
2306             for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2307                if (ppir->bit == PROP_MASK_CTM) {
2308                   sprintf(gszMsgBox,
2309                         TgLoadString(STID_CANNOT_PASTE_COMPOUND_OBJ),
2310                         ppir->desc, ppir->desc);
2311                   MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2312                   break;
2313                }
2314             }
2315             return FALSE;
2316          }
2317       }
2318    } else if (topSel == NULL && (*pnTransformed)) {
2319       if ((fabs(ctm->m[CTM_SX]-ctm->m[CTM_SY]) < EQ_TOL) &&
2320             (fabs(ctm->m[CTM_SIN]+ctm->m[CTM_MSIN]) < EQ_TOL)) {
2321          return TRUE;
2322       }
2323       for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2324          if (ppir->bit == PROP_MASK_CTM) {
2325             sprintf(gszMsgBox, TgLoadString(STID_CUT_BUF_HAS_NON_ROT_COMP),
2326                   ppir->desc);
2327             MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2328             break;
2329          }
2330       }
2331       *pnTransformed = FALSE;
2332    }
2333    return TRUE;
2334 }
2335 
2336 static
WritePropToIni(lWhich,pszSec,pszKey,pProp)2337 void WritePropToIni(lWhich, pszSec, pszKey, pProp)
2338    long lWhich;
2339    char *pszSec, *pszKey;
2340    struct PropertiesRec *pProp;
2341 {
2342    switch (lWhich) {
2343    case PROP_MASK_AH:
2344       WriteStringProp(pszSec, pszKey, pProp->ah, pProp->ah_spec);
2345       break;
2346    case PROP_MASK_AW:
2347       WriteStringProp(pszSec, pszKey, pProp->aw, pProp->aw_spec);
2348       break;
2349    case PROP_MASK_ARROW_STYLE:
2350       WriteIntProp(pszSec, pszKey, pProp->arrow_style);
2351       break;
2352    case PROP_MASK_COLOR:
2353       WriteStringProp(pszSec, pszKey, pProp->color, pProp->color_str);
2354       break;
2355 
2356    case PROP_MASK_DASH: WriteIntProp(pszSec, pszKey, pProp->dash); break;
2357    case PROP_MASK_WIDTH:
2358       WriteStringProp(pszSec, pszKey, pProp->width, pProp->width_spec);
2359       break;
2360    case PROP_MASK_TRANSPAT:
2361       WriteIntProp(pszSec, pszKey, pProp->trans_pat);
2362       break;
2363    case PROP_MASK_FILL: WriteIntProp(pszSec, pszKey, pProp->fill); break;
2364    case PROP_MASK_PEN: WriteIntProp(pszSec, pszKey, pProp->pen); break;
2365    case PROP_MASK_RCB_RADIUS:
2366       WriteIntProp(pszSec, pszKey, pProp->rcb_radius);
2367       break;
2368    case PROP_MASK_CURVED: WriteIntProp(pszSec, pszKey, pProp->curved); break;
2369    case PROP_MASK_TEXT_FONT:
2370       WriteFontProp(pszSec, pszKey, pProp->text_font, pProp->text_style);
2371       break;
2372    case PROP_MASK_TEXT_JUST:
2373       WriteIntProp(pszSec, pszKey, pProp->text_just);
2374       break;
2375    case PROP_MASK_TEXT_SZ_UNIT:
2376       WriteIntProp(pszSec, pszKey, pProp->text_sz_unit);
2377       break;
2378    case PROP_MASK_UNDERLINE_ON:
2379       WriteIntProp(pszSec, pszKey, pProp->underline_on);
2380       break;
2381    case PROP_MASK_OVERLINE_ON:
2382       WriteIntProp(pszSec, pszKey, pProp->overline_on);
2383       break;
2384    case PROP_MASK_VSPACE: WriteIntProp(pszSec, pszKey, pProp->v_space); break;
2385    case PROP_MASK_CTM:
2386       WriteCTMProp(pszSec, pszKey, pProp->transformed, &pProp->ctm);
2387       break;
2388    }
2389 }
2390 
2391 static
ParseAProp(lWhich,pszBuf,pProp)2392 int ParseAProp(lWhich, pszBuf, pProp)
2393    long lWhich;
2394    char *pszBuf;
2395    struct PropertiesRec *pProp;
2396 {
2397    switch (lWhich) {
2398    case PROP_MASK_AH:
2399       return ParseStringProp(pszBuf, &pProp->ah, pProp->ah_spec);
2400    case PROP_MASK_AW:
2401       return ParseStringProp(pszBuf, &pProp->aw, pProp->aw_spec);
2402    case PROP_MASK_ARROW_STYLE: return ParseIntProp(pszBuf, &pProp->arrow_style);
2403    case PROP_MASK_COLOR:
2404       return ParseStringProp(pszBuf, &pProp->color, pProp->color_str);
2405    case PROP_MASK_DASH: return ParseIntProp(pszBuf, &pProp->dash);
2406    case PROP_MASK_WIDTH:
2407       return ParseStringProp(pszBuf, &pProp->width, pProp->width_spec);
2408    case PROP_MASK_TRANSPAT: return ParseIntProp(pszBuf, &pProp->trans_pat);
2409    case PROP_MASK_FILL: return ParseIntProp(pszBuf, &pProp->fill);
2410    case PROP_MASK_PEN: return ParseIntProp(pszBuf, &pProp->pen);
2411    case PROP_MASK_RCB_RADIUS: return ParseIntProp(pszBuf, &pProp->rcb_radius);
2412    case PROP_MASK_CURVED: return ParseIntProp(pszBuf, &pProp->curved);
2413    case PROP_MASK_TEXT_FONT:
2414       return ParseFontProp(pszBuf, &pProp->double_byte, &pProp->text_font,
2415             &pProp->text_style);
2416    case PROP_MASK_TEXT_JUST: return ParseIntProp(pszBuf, &pProp->text_just);
2417    case PROP_MASK_TEXT_SZ_UNIT:
2418       return ParseFontSzUnitProp(pszBuf, &pProp->text_sz_unit);
2419    case PROP_MASK_UNDERLINE_ON:
2420       return ParseIntProp(pszBuf, &pProp->underline_on);
2421    case PROP_MASK_OVERLINE_ON: return ParseIntProp(pszBuf, &pProp->overline_on);
2422    case PROP_MASK_VSPACE: return ParseIntProp(pszBuf, &pProp->v_space);
2423    case PROP_MASK_CTM:
2424       return ParseCTMProp(pszBuf, &pProp->transformed, &pProp->ctm);
2425    }
2426    return FALSE;
2427 }
2428 
2429 static
ReadPropFromIni(pszSec,plMask,pCheckArray,pProp)2430 int ReadPropFromIni(pszSec, plMask, pCheckArray, pProp)
2431    /*
2432     * This routine sets *plMask according to what's in the ini file.
2433     *     All associated fields in pProp are filled in.
2434     * This routine sets pCheckArray->num_cols and pCheckArray->num_rows;
2435     *     pCheckArray->value is set to NULL.
2436     */
2437    char *pszSec;
2438    long *plMask;
2439    struct CheckArrayRec *pCheckArray;
2440    struct PropertiesRec *pProp;
2441 {
2442    struct PropInfoRec *ppir=NULL;
2443    char *pszKey=NULL;
2444    char *pszKeys=tgGetProfileString(pszSec, NULL, gszPropIniFile);
2445 
2446    *plMask = 0L;
2447    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) ppir->checked = FALSE;
2448    pCheckArray->num_cols = 1;
2449 
2450    if (pszKeys == NULL) {
2451       strcpy(gszMsgBox, TgLoadString(STID_NO_PROPERTY_TO_PASTE));
2452       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2453       return FALSE;
2454    }
2455    for (pszKey=pszKeys; *pszKey != '\0'; pszKey++) {
2456       for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2457          if (strcmp(pszKey, ppir->key) == 0) {
2458             break;
2459          }
2460       }
2461       if (ppir != NULL) {
2462          char *pszValue=tgGetProfileString(pszSec, pszKey, gszPropIniFile);
2463 
2464          if (topSel == NULL && (ppir->bit == PROP_MASK_WIDTH ||
2465                ppir->bit == PROP_MASK_AW || ppir->bit == PROP_MASK_AH)) {
2466          } else {
2467             if (ParseAProp(ppir->bit, ((pszValue==NULL)?"":pszValue), pProp)) {
2468                ppir->checked = TRUE;
2469                pCheckArray->num_rows++;
2470                *plMask |= ppir->bit;
2471             }
2472          }
2473          if (pszValue != NULL) tgFreeProfileString(pszValue);
2474       } else {
2475          sprintf(gszMsgBox, TgLoadString(STID_INVALID_KEY_IN_SEC_OF_INIFILE),
2476                pszKey, pszSec, gszPropIniFile);
2477          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2478       }
2479       pszKey += strlen(pszKey);
2480    }
2481    tgFreeProfileString(pszKeys);
2482    return TRUE;
2483 }
2484 
2485 #define COPY_BUTTON    101
2486 #define PASTE_BUTTON   102
2487 #define SAVE_BUTTON    103
2488 #define RESTORE_BUTTON 104
2489 
CleanUpProperties()2490 void CleanUpProperties()
2491 {
2492    struct PropInfoRec *ppir=NULL;
2493 
2494    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2495       UtilFree(ppir->desc);
2496       UtilFree(ppir->key);
2497       ppir->desc = ppir->key = NULL;
2498    }
2499 }
2500 
2501 static
UpdateSavedPropKeys()2502 void UpdateSavedPropKeys()
2503 {
2504    char *pszName=NULL;
2505    char *pszNames=tgGetProfileString(gszPropSetSec, NULL, gszPropIniFile);
2506    int already_updated=FALSE;
2507 
2508    if (pszNames == NULL) return;
2509 
2510    for (pszName=pszNames; !already_updated && *pszName != '\0'; pszName++) {
2511       char szPropSetSec[MAXSTRING], *pszKeys=NULL;
2512 
2513       sprintf(szPropSetSec, "%s: %s", gszPropProfilePrefix, pszName);
2514 
2515       pszKeys = tgGetProfileString(szPropSetSec, NULL, gszPropIniFile);
2516       if (pszKeys != NULL) {
2517          char *pszKey=NULL;
2518 
2519          for (pszKey=pszKeys; *pszKey != '\0'; pszKey++) {
2520             if (pszKey[0] == '0' && pszKey[1] == 'x') {
2521                /* already updated */
2522                already_updated = TRUE;
2523                break;
2524             } else {
2525                char *pszValue=tgGetProfileString(szPropSetSec, pszKey,
2526                      gszPropIniFile);
2527 
2528                if (pszValue != NULL) {
2529                   struct PropInfoRec *ppir_compat=NULL, *ppir=NULL;
2530 
2531                   for (ppir_compat=gstCompatPropInfo, ppir=gstPropInfo;
2532                         ppir_compat->bit != 0L;
2533                         ppir_compat++, ppir++) {
2534                      if (strcmp(ppir_compat->desc, pszKey) == 0) {
2535                         tgWriteProfileString(szPropSetSec, ppir->key, pszValue,
2536                               gszPropIniFile);
2537                         tgWriteProfileString(szPropSetSec, pszKey, NULL,
2538                               gszPropIniFile);
2539                         break;
2540                      }
2541                   }
2542                   tgFreeProfileString(pszValue);
2543                }
2544             }
2545             pszKey += strlen(pszKey);
2546          }
2547          tgFreeProfileString(pszKeys);
2548       }
2549       pszName += strlen(pszName);
2550    }
2551    tgFreeProfileString(pszNames);
2552 
2553    if (!already_updated) {
2554       tgWriteProfileString(NULL, NULL, NULL, gszPropIniFile);
2555    }
2556 }
2557 
InitProperties()2558 void InitProperties()
2559 {
2560    struct PropInfoRec *ppir=NULL;
2561 
2562    /* do not translate -- program constants */
2563    sprintf(gszPropIniFile, "%s%ccutpaste.ini", tgifDir, DIR_SEP);
2564 
2565    strcpy(gszCopyPasteSec, "Copy/Paste Properties");
2566    strcpy(gszCopyPasteBackupSec, "Copy/Paste Properties - Backup");
2567    strcpy(gszPropSetSec, "Property Set Names");
2568    strcpy(gszPropProfilePrefix, "Property Profile");
2569 
2570    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2571       if (ppir->key != NULL) return;
2572 
2573       ppir->desc = UtilStrDup(PropLoadString(ppir->bit));
2574       if (ppir->desc == NULL) FailAllocMessage();
2575 
2576       sprintf(gszMsgBox, "0x%08lx", ppir->bit);
2577       ppir->key = UtilStrDup(gszMsgBox);
2578       if (ppir->key == NULL) FailAllocMessage();
2579    }
2580    UpdateSavedPropKeys();
2581 }
2582 
2583 static
FormatPropForDisplay(lWhich,pProp,ppir,pszBuf)2584 void FormatPropForDisplay(lWhich, pProp, ppir, pszBuf)
2585    long lWhich;
2586    struct PropertiesRec *pProp;
2587    struct PropInfoRec *ppir;
2588    char *pszBuf;
2589 {
2590    char font_str[MAXSTRING];
2591    int nLen=0;
2592 
2593    sprintf(pszBuf, "%s: ", ppir->desc);
2594    nLen = strlen(pszBuf);
2595 
2596    switch (lWhich) {
2597    case PROP_MASK_CTM: strcat(pszBuf, "..."); break;
2598 
2599    case PROP_MASK_COLOR: strcat(pszBuf, pProp->color_str); break;
2600    case PROP_MASK_WIDTH_INDEX:
2601       sprintf(&pszBuf[nLen], "%1d", pProp->width_index);
2602       break;
2603    case PROP_MASK_WIDTH: strcat(pszBuf, pProp->width_spec); break;
2604    case PROP_MASK_AW: strcat(pszBuf, pProp->aw_spec); break;
2605    case PROP_MASK_AH: strcat(pszBuf, pProp->ah_spec); break;
2606 
2607    case PROP_MASK_TRANSPAT:
2608       sprintf(&pszBuf[nLen], "%1d", pProp->trans_pat);
2609       break;
2610    case PROP_MASK_FILL: sprintf(&pszBuf[nLen], "%1d", pProp->fill); break;
2611    case PROP_MASK_PEN: sprintf(&pszBuf[nLen], "%1d", pProp->pen); break;
2612    case PROP_MASK_DASH: sprintf(&pszBuf[nLen], "%1d", pProp->dash); break;
2613    case PROP_MASK_ARROW_STYLE:
2614       sprintf(&pszBuf[nLen], "%1d", pProp->arrow_style);
2615       break;
2616    case PROP_MASK_CURVED: sprintf(&pszBuf[nLen], "%1d", pProp->curved); break;
2617    case PROP_MASK_RCB_RADIUS:
2618       sprintf(&pszBuf[nLen], "%1d", pProp->rcb_radius);
2619       break;
2620    case PROP_MASK_TEXT_JUST:
2621       sprintf(&pszBuf[nLen], "%1d", pProp->text_just);
2622       break;
2623    case PROP_MASK_TEXT_SZ_UNIT:
2624       if (showFontSizeInPoints) {
2625          sprintf(&pszBuf[nLen], "%1dpt",
2626                SzUnitToPointSize(pProp->text_sz_unit));
2627       } else {
2628          sprintf(&pszBuf[nLen], "%1d", SzUnitToFontSize(pProp->text_sz_unit));
2629       }
2630       break;
2631    case PROP_MASK_VSPACE: sprintf(&pszBuf[nLen], "%1d", pProp->v_space); break;
2632    case PROP_MASK_UNDERLINE_ON:
2633       sprintf(&pszBuf[nLen], "%1d", pProp->underline_on);
2634       break;
2635    case PROP_MASK_OVERLINE_ON:
2636       sprintf(&pszBuf[nLen], "%1d", pProp->overline_on);
2637       break;
2638 
2639    case PROP_MASK_TEXT_FONT:
2640       *font_str = '\0';
2641       GetPSFontStr(pProp->text_font, pProp->text_style, font_str);
2642       /* font_str starts with the '/' character */
2643       strcat(pszBuf, &font_str[1]);
2644       break;
2645    }
2646 }
2647 
2648 static
GetPropSelection(pp_dsp_ptr,ppsz_entries,pn_num_entries,pn_marked_index,pp_check_array,cur_buf,p_void)2649 int GetPropSelection(pp_dsp_ptr, ppsz_entries, pn_num_entries, pn_marked_index,
2650       pp_check_array, cur_buf, p_void)
2651    DspList **pp_dsp_ptr;
2652    char ***ppsz_entries, *cur_buf;
2653    int *pn_num_entries, *pn_marked_index;
2654    struct CheckArrayRec **pp_check_array;
2655    void *p_void;
2656 {
2657    struct PropertiesRec *pProp=(struct PropertiesRec *)p_void;
2658    struct PropInfoRec *ppir=NULL;
2659    int num_rows=0;
2660    char **ppsz=NULL;
2661 
2662    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2663       if (ppir->checked) {
2664          num_rows++;
2665       }
2666    }
2667    *ppsz_entries = (char**)malloc(num_rows*sizeof(char*));
2668    if (*ppsz_entries == NULL) FailAllocMessage();
2669    memset(*ppsz_entries, 0, num_rows*sizeof(char*));
2670 
2671    for (ppir=gstPropInfo, ppsz=(*ppsz_entries); ppir->bit != 0L; ppir++) {
2672       if (ppir->checked) {
2673          char szBuf[MAXSTRING];
2674 
2675          *szBuf = '\0';
2676          FormatPropForDisplay(ppir->bit, pProp, ppir, szBuf);
2677          *ppsz = UtilStrDup(szBuf);
2678          if ((*ppsz) == NULL) FailAllocMessage();
2679          ppsz++;
2680       }
2681    }
2682    *pn_num_entries = num_rows;
2683    return TRUE;
2684 }
2685 
2686 static
DoCopyProperties(pCheckArray,pProp)2687 int DoCopyProperties(pCheckArray, pProp)
2688    /*
2689     * Returns FALSE if nothing copied.
2690     */
2691    struct CheckArrayRec *pCheckArray;
2692    struct PropertiesRec *pProp;
2693 {
2694    struct PropInfoRec *ppir=NULL;
2695    int index=0, count=0;
2696    char *pszKeys=NULL;
2697 
2698    tgWriteProfileString(gszCopyPasteSec, NULL, NULL, gszPropIniFile);
2699    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2700       if (ppir->checked) {
2701          if (pCheckArray->value[0][index]) {
2702             WritePropToIni(ppir->bit, gszCopyPasteSec, ppir->key, pProp);
2703             count++;
2704          }
2705          index++;
2706       }
2707    }
2708    tgWriteProfileString(NULL, NULL, NULL, gszPropIniFile);
2709    if (count == 0) {
2710       strcpy(gszMsgBox, TgLoadString(STID_NO_PROPERTY_TO_COPY));
2711       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2712       return FALSE;
2713    }
2714    pszKeys = tgGetProfileString(gszCopyPasteSec, NULL, gszPropIniFile);
2715    if (pszKeys != NULL) {
2716       int total=(strlen(gszCopyPasteSec)+4), offset=0;
2717       char *pszKey=NULL;
2718       char *buf=(char*)malloc(total+1); /* sizeof buf is always total+1 */
2719 
2720       if (buf == NULL) FailAllocMessage();
2721       memset(buf, 0, total+1);
2722       *buf = TGIF_HEADER;
2723       sprintf(&buf[1], "[%s]", gszCopyPasteSec);
2724       offset = total;
2725 
2726       for (pszKey=pszKeys; *pszKey != '\0'; pszKey++) {
2727          int key_len=strlen(pszKey);
2728          char *pszValue=tgGetProfileString(gszCopyPasteSec, pszKey,
2729                gszPropIniFile);
2730 
2731          if (pszValue != NULL) {
2732             int value_len=strlen(pszValue);
2733 
2734             buf = (char*)realloc(buf, total+key_len+value_len+3);
2735             if (buf == NULL) FailAllocMessage();
2736             sprintf(&buf[offset], "%s=%s", pszKey, pszValue);
2737 
2738             offset += key_len+value_len+2;
2739             total += key_len+value_len+2;
2740 
2741             tgFreeProfileString(pszValue);
2742          } else {
2743             buf = (char*)realloc(buf, total+key_len+3);
2744             if (buf == NULL) FailAllocMessage();
2745             sprintf(&buf[offset], "%s=", pszKey);
2746 
2747             offset += key_len+2;
2748             total += key_len+2;
2749          }
2750          pszKey += key_len;
2751       }
2752       buf[total] = '\0';
2753       tgFreeProfileString(pszKeys);
2754 
2755       if (!WriteBufToCutBuffer(buf, total+1, TRUE, FALSE, NULL)) {
2756          strcpy(gszMsgBox, TgLoadString(STID_COPY_FAILED_OBJ_MAYBE_TOO_BIG));
2757          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2758       } else {
2759          strcpy(gszMsgBox, TgLoadString(STID_COPY_BUFFER_UPDATED));
2760          Msg(gszMsgBox);
2761       }
2762       free(buf);
2763    }
2764    return TRUE;
2765 }
2766 
2767 static
PropSetExists(pszPropSet)2768 int PropSetExists(pszPropSet)
2769    char *pszPropSet;
2770 {
2771    int nReturn=FALSE;
2772    char *pszKeys=tgGetProfileString(gszPropSetSec, NULL, gszPropIniFile);
2773 
2774    if (pszKeys != NULL) {
2775       char *pszKey=NULL;
2776 
2777       for (pszKey=pszKeys; *pszKey != '\0'; pszKey++) {
2778          if (UtilStrICmp(pszPropSet, pszKey) == 0) {
2779             nReturn = TRUE;
2780             break;
2781          }
2782          pszKey += strlen(pszKey);
2783       }
2784       tgFreeProfileString(pszKeys);
2785    }
2786    return nReturn;
2787 }
2788 
2789 static
DoSaveProperties(pCheckArray,pProp)2790 int DoSaveProperties(pCheckArray, pProp)
2791    /*
2792     * Returns FALSE if nothing saved.
2793     */
2794    struct CheckArrayRec *pCheckArray;
2795    struct PropertiesRec *pProp;
2796 {
2797    char szPropSet[MAXSTRING], szPropSetSec[MAXSTRING];
2798    struct PropInfoRec *ppir=NULL;
2799    int index=0, count=0;
2800 
2801    *szPropSet = *szPropSetSec = '\0';
2802    strcpy(gszMsgBox, TgLoadString(STID_ENTER_NAME_FOR_PROP_SET));
2803    if (Dialog(gszMsgBox, NULL, szPropSet) == INVALID) return FALSE;
2804    UtilTrimBlanks(szPropSet);
2805    if (*szPropSet == '\0') return FALSE;
2806    if (PropSetExists(szPropSet)) {
2807       sprintf(gszMsgBox, TgLoadString(STID_PROP_SET_EXISTS_OVERWRITE_YNC),
2808             szPropSet);
2809       if (MsgBox(gszMsgBox, TOOL_NAME, YNC_MB) != MB_ID_YES) {
2810          return FALSE;
2811       }
2812    }
2813    sprintf(szPropSetSec, "%s: %s", gszPropProfilePrefix, szPropSet);
2814    tgWriteProfileString(gszPropSetSec, szPropSet, "", gszPropIniFile);
2815    tgWriteProfileString(szPropSetSec, NULL, NULL, gszPropIniFile);
2816    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2817       if (ppir->checked) {
2818          if (pCheckArray->value[0][index]) {
2819             WritePropToIni(ppir->bit, szPropSetSec, ppir->key, pProp);
2820             count++;
2821          }
2822          index++;
2823       }
2824    }
2825    tgWriteProfileString(NULL, NULL, NULL, gszPropIniFile);
2826    if (count == 0) {
2827       strcpy(gszMsgBox, TgLoadString(STID_NO_PROP_TO_SAVE));
2828       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2829       return FALSE;
2830    }
2831    return TRUE;
2832 }
2833 
2834 static
DoPasteProperties(pCheckArray,pProp)2835 int DoPasteProperties(pCheckArray, pProp)
2836    /*
2837     * Returns FALSE if nothing pasted.
2838     */
2839    struct CheckArrayRec *pCheckArray;
2840    struct PropertiesRec *pProp;
2841 {
2842    struct PropInfoRec *ppir=NULL;
2843    int index=0, count=0;
2844    int paste_sel=FALSE;
2845 
2846    if ((curChoice == NOTHING || curChoice == VERTEXMODE ||
2847          curChoice == ROTATEMODE)) {
2848       paste_sel = (topSel != NULL);
2849    }
2850    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2851       if (ppir->checked) {
2852          if (pCheckArray->value[0][index]) {
2853             count++;
2854          }
2855          index++;
2856       }
2857    }
2858    if (count == 0) {
2859       strcpy(gszMsgBox, TgLoadString(STID_NO_PROPERTY_TO_PASTE));
2860       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2861       return FALSE;
2862    }
2863    if (paste_sel) {
2864       HighLightReverse();
2865       StartCompositeCmd();
2866    }
2867    for (ppir=gstPropInfo, index=0; ppir->bit != 0L; ppir++) {
2868       if (ppir->checked) {
2869          if (pCheckArray->value[0][index]) {
2870             DoPasteAProperty(ppir->bit, pProp);
2871          }
2872          index++;
2873       }
2874    }
2875    if (paste_sel) {
2876       EndCompositeCmd();
2877       HighLightForward();
2878    }
2879    return TRUE;
2880 }
2881 
2882 #define COPY_PROP    0
2883 #define PASTE_PROP   1
2884 #define SAVE_PROP    2
2885 #define RESTORE_PROP 3
2886 
2887 static
FreePropSelection(pp_dsp_ptr,ppsz_entries,pn_num_entries,pn_marked_index,pp_check_array,cur_buf,btn_id,selected_index,p_void)2888 int FreePropSelection(pp_dsp_ptr, ppsz_entries, pn_num_entries, pn_marked_index,
2889       pp_check_array, cur_buf, btn_id, selected_index, p_void)
2890    DspList **pp_dsp_ptr;
2891    char ***ppsz_entries, *cur_buf;
2892    int *pn_num_entries, *pn_marked_index, btn_id, selected_index;
2893    struct CheckArrayRec **pp_check_array;
2894    void *p_void;
2895 {
2896    struct PropertiesRec *pProp=(struct PropertiesRec *)p_void;
2897    struct PropInfoRec *ppir=NULL;
2898    int nReturn=TRUE;
2899 
2900    switch ((int)(long)(pProp->userdata)) {
2901    case COPY_PROP:
2902       if (btn_id == COPY_BUTTON) {
2903          nReturn = !DoCopyProperties(*pp_check_array, pProp);
2904       }
2905       break;
2906    case SAVE_PROP:
2907       if (btn_id == SAVE_BUTTON) {
2908          nReturn = !DoSaveProperties(*pp_check_array, pProp);
2909       }
2910       break;
2911    case PASTE_PROP:
2912       if (btn_id == PASTE_BUTTON) {
2913          nReturn = !DoPasteProperties(*pp_check_array, pProp);
2914       }
2915       break;
2916    case RESTORE_PROP:
2917       if (btn_id == RESTORE_BUTTON) {
2918          nReturn = !DoPasteProperties(*pp_check_array, pProp);
2919       }
2920       break;
2921    }
2922    if (*ppsz_entries != NULL) {
2923       char **ppsz=(*ppsz_entries);
2924 
2925       for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
2926          if (ppir->checked) {
2927             UtilFree(*ppsz);
2928             ppsz++;
2929          }
2930       }
2931       free(*ppsz_entries);
2932       *ppsz_entries = NULL;
2933    }
2934    return nReturn;
2935 }
2936 
2937 static
PropSelectionCheckUpdate(pp_dsp_ptr,ppsz_entries,pn_num_entries,pn_marked_index,pp_check_array,cur_buf,col,row,p_void)2938 int PropSelectionCheckUpdate(pp_dsp_ptr, ppsz_entries, pn_num_entries,
2939       pn_marked_index, pp_check_array, cur_buf, col, row, p_void)
2940    DspList **pp_dsp_ptr;
2941    char ***ppsz_entries, *cur_buf;
2942    int *pn_num_entries, *pn_marked_index, col, row;
2943    struct CheckArrayRec **pp_check_array;
2944    void *p_void;
2945 {
2946    struct PropertiesRec *pProp=(struct PropertiesRec *)p_void;
2947    char *psz=NULL, saved_ch='\0', *buf=NULL;
2948    int checked=FALSE;
2949 
2950    if (ppsz_entries == NULL || *ppsz_entries == NULL ||
2951          (*ppsz_entries)[row] == NULL || pp_check_array == NULL ||
2952          (*pp_check_array) == NULL || (*pp_check_array)->value == NULL) {
2953       return FALSE;
2954    }
2955    buf = (*ppsz_entries)[row];
2956    checked = (*pp_check_array)->value[0][row];
2957 
2958    psz = strchr(buf, ':');
2959    if (psz != NULL) {
2960       saved_ch = *psz;
2961       *psz = '\0';
2962       switch ((int)(long)(pProp->userdata)) {
2963       case COPY_PROP:
2964          sprintf(gszMsgBox, TgLoadString(checked ? STID_WILL_COPY_NAMED_PROP :
2965                STID_WILL_NOT_COPY_NAMED_PROP), buf);
2966          break;
2967       case SAVE_PROP:
2968          sprintf(gszMsgBox, TgLoadString(checked ? STID_WILL_SAVE_NAMED_PROP :
2969                STID_WILL_NOT_SAVE_NAMED_PROP), buf);
2970          break;
2971       case PASTE_PROP:
2972          sprintf(gszMsgBox, TgLoadString(checked ? STID_WILL_PASTE_NAMED_PROP :
2973                STID_WILL_NOT_PASTE_NAMED_PROP), buf);
2974          break;
2975       case RESTORE_PROP:
2976          sprintf(gszMsgBox, TgLoadString(checked ?
2977                STID_WILL_RESTORE_NAMED_PROP : STID_WILL_NOT_RESTORE_NAMED_PROP),
2978                buf);
2979          break;
2980       }
2981       *psz = saved_ch;
2982    }
2983    SetStringStatus(gszMsgBox);
2984 
2985    return TRUE;
2986 }
2987 
2988 static
SelectProperties(pszTitle,nWhich,pCheckArray,pProp)2989 void SelectProperties(pszTitle, nWhich, pCheckArray, pProp)
2990    char *pszTitle;
2991    int nWhich;
2992    struct CheckArrayRec *pCheckArray;
2993    struct PropertiesRec *pProp;
2994 {
2995    char win_name[128];
2996 
2997    ResetNamesInfo();
2998 
2999    NamesSetTitle(pszTitle);
3000    switch (nWhich) {
3001    case COPY_PROP:
3002       NamesAddButton(TgLoadCachedString(CSTID_COPY), COPY_BUTTON);
3003       NamesAddButton(TgLoadCachedString(CSTID_CANCEL), BUTTON_CANCEL);
3004       /* ignore double-click and <CR> */
3005       NamesSetDefaultBtnId(COPY_BUTTON, INVALID);
3006       NamesSetEntries(NULL, NULL, 0, pCheckArray, TRUE, INVALID, 0);
3007       NamesSetStyle(NAMES_SIMPLE_SELECT_NAME, NAMES_LOOP_MANY);
3008       NamesSetCallback((GetEntriesFunc*)GetPropSelection,
3009             (AfterLoopFunc*)FreePropSelection,
3010             (CheckUpdateFunc*)PropSelectionCheckUpdate);
3011       sprintf(win_name, TgLoadString(STID_TOOL_COPY_PROP), TOOL_NAME);
3012       break;
3013    case SAVE_PROP:
3014       NamesAddButton(TgLoadCachedString(CSTID_SAVE), SAVE_BUTTON);
3015       NamesAddButton(TgLoadCachedString(CSTID_CANCEL), BUTTON_CANCEL);
3016       /* ignore double-click and <CR> */
3017       NamesSetDefaultBtnId(SAVE_BUTTON, INVALID);
3018       NamesSetEntries(NULL, NULL, 0, pCheckArray, TRUE, INVALID, 0);
3019       NamesSetStyle(NAMES_SIMPLE_SELECT_NAME, NAMES_LOOP_MANY);
3020       NamesSetCallback((GetEntriesFunc*)GetPropSelection,
3021             (AfterLoopFunc*)FreePropSelection,
3022             (CheckUpdateFunc*)PropSelectionCheckUpdate);
3023       sprintf(win_name, TgLoadString(STID_TOOL_SAVE_PROP), TOOL_NAME);
3024       break;
3025    case PASTE_PROP:
3026       NamesAddButton(TgLoadCachedString(CSTID_PASTE), PASTE_BUTTON);
3027       NamesAddButton(TgLoadCachedString(CSTID_CANCEL), BUTTON_CANCEL);
3028       /* ignore double-click and <CR> */
3029       NamesSetDefaultBtnId(PASTE_BUTTON, INVALID);
3030       NamesSetEntries(NULL, NULL, 0, pCheckArray, TRUE, INVALID, 0);
3031       NamesSetStyle(NAMES_SIMPLE_SELECT_NAME, NAMES_LOOP_MANY);
3032       NamesSetCallback((GetEntriesFunc*)GetPropSelection,
3033             (AfterLoopFunc*)FreePropSelection,
3034             (CheckUpdateFunc*)PropSelectionCheckUpdate);
3035       sprintf(win_name, TgLoadString(STID_TOOL_PASTE_PROP), TOOL_NAME);
3036       break;
3037    case RESTORE_PROP:
3038       NamesAddButton(TgLoadCachedString(CSTID_RESTORE), RESTORE_BUTTON);
3039       NamesAddButton(TgLoadCachedString(CSTID_CANCEL), BUTTON_CANCEL);
3040       /* ignore double-click and <CR> */
3041       NamesSetDefaultBtnId(RESTORE_BUTTON, INVALID);
3042       NamesSetEntries(NULL, NULL, 0, pCheckArray, TRUE, INVALID, 0);
3043       NamesSetStyle(NAMES_SIMPLE_SELECT_NAME, NAMES_LOOP_MANY);
3044       NamesSetCallback((GetEntriesFunc*)GetPropSelection,
3045             (AfterLoopFunc*)FreePropSelection,
3046             (CheckUpdateFunc*)PropSelectionCheckUpdate);
3047       sprintf(win_name, TgLoadString(STID_TOOL_RESTORE_PROP), TOOL_NAME);
3048       break;
3049    }
3050    Names(win_name, NULL, NULL, 0, pProp);
3051 }
3052 
3053 static
PrepareToCopyProperties(pProp,lMask,lSkip,pCheckArray)3054 int PrepareToCopyProperties(pProp, lMask, lSkip, pCheckArray)
3055    struct PropertiesRec *pProp;
3056    long lMask, lSkip;
3057    struct CheckArrayRec *pCheckArray;
3058 {
3059    struct PropInfoRec *ppir=NULL;
3060    int index=0, num_rows=0;
3061 
3062    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
3063       ppir->checked = FALSE;
3064       if ((lMask & ppir->bit) != 0L && (lSkip & ppir->bit) == 0) {
3065          num_rows++;
3066       }
3067    }
3068    pCheckArray->num_cols = 1;
3069    pCheckArray->num_rows = num_rows;
3070 
3071    pCheckArray->value = (int**)malloc(sizeof(int*));
3072    if (pCheckArray->value == NULL) FailAllocMessage();
3073    memset(pCheckArray->value, 0, sizeof(int*));
3074 
3075    pCheckArray->value[0] = (int*)malloc(num_rows*sizeof(int));
3076    if (pCheckArray->value[0] == NULL) FailAllocMessage();
3077    memset(pCheckArray->value[0], 0, num_rows*sizeof(int));
3078 
3079    for (ppir=gstPropInfo, index=0; ppir->bit != 0L; ppir++) {
3080       long bit=ppir->bit;
3081 
3082       if ((lMask & bit) != 0L && (lSkip & bit) == 0) {
3083          ppir->checked = TRUE;
3084          pCheckArray->value[0][index++] = TRUE;
3085       }
3086    }
3087    return TRUE;
3088 }
3089 
3090 static
PrepareToPasteProperties(pszSec,plMask,pCheckArray,pProp)3091 int PrepareToPasteProperties(pszSec, plMask, pCheckArray, pProp)
3092    char *pszSec;
3093    struct PropertiesRec *pProp;
3094    long *plMask;
3095    struct CheckArrayRec *pCheckArray;
3096 {
3097    struct PropInfoRec *ppir=NULL;
3098    int index=0, num_rows=0;
3099 
3100    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
3101       ppir->checked = FALSE;
3102    }
3103    if (!ReadPropFromIni(pszSec, plMask, pCheckArray, pProp)) {
3104       return FALSE;
3105    }
3106    num_rows = pCheckArray->num_rows;
3107 
3108    pCheckArray->value = (int**)malloc(sizeof(int*));
3109    if (pCheckArray->value == NULL) FailAllocMessage();
3110    memset(pCheckArray->value, 0, sizeof(int*));
3111 
3112    pCheckArray->value[0] = (int*)malloc(num_rows*sizeof(int));
3113    if (pCheckArray->value[0] == NULL) FailAllocMessage();
3114    memset(pCheckArray->value[0], 0, num_rows*sizeof(int));
3115 
3116    for (ppir=gstPropInfo, index=0; ppir->bit != 0L; ppir++) {
3117       long bit=ppir->bit;
3118 
3119       if (((*plMask) & bit) != 0L) {
3120          ppir->checked = TRUE;
3121          pCheckArray->value[0][index++] = TRUE;
3122       }
3123    }
3124    return TRUE;
3125 }
3126 
3127 static
SetupProperties(pProp,plMask,plSkip,pCheckArray,nWhich)3128 int SetupProperties(pProp, plMask, plSkip, pCheckArray, nWhich)
3129    struct PropertiesRec *pProp;
3130    long *plMask, *plSkip;
3131    struct CheckArrayRec *pCheckArray;
3132    int nWhich;
3133 {
3134    int copy_sel=FALSE;
3135 
3136    pProp->userdata = (void*)(long)nWhich;
3137    if ((curChoice == NOTHING || curChoice == VERTEXMODE ||
3138          curChoice == ROTATEMODE)) {
3139       if (topSel == NULL) {
3140          copy_sel = FALSE;
3141       } else if (topSel == botSel) {
3142          copy_sel = TRUE;
3143       } else {
3144          switch (nWhich) {
3145          case COPY_PROP:
3146             strcpy(gszMsgBox, TgLoadString(STID_SEL_ONLY_ONE_FOR_COPY_PROP));
3147             break;
3148          case SAVE_PROP:
3149             strcpy(gszMsgBox, TgLoadString(STID_SEL_ONLY_ONE_FOR_SAVE_PROP));
3150             break;
3151          }
3152          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3153          return FALSE;
3154       }
3155    }
3156    if (copy_sel) {
3157       SetPropMask(topSel->obj, plMask, plSkip, pProp);
3158    } else {
3159       SetPropMask(NULL, plMask, plSkip, pProp);
3160    }
3161    /* shouldn't do this if we support multiple objects */
3162    *plSkip = ~((*plMask) | PROP_MASK_WIDTH_INDEX);
3163 
3164    if (pCheckArray != NULL) {
3165       return PrepareToCopyProperties(pProp, *plMask, *plSkip, pCheckArray);
3166    }
3167    return TRUE;
3168 }
3169 
3170 static
FixMasksForGetProperty(pProp,plMask,plSkip)3171 void FixMasksForGetProperty(pProp, plMask, plSkip)
3172    struct PropertiesRec *pProp;
3173    long *plMask, *plSkip;
3174 {
3175    long lMask=(*plMask);
3176 
3177    *plSkip |= PROP_MASK_CTM;
3178 
3179    if ((lMask & PROP_MASK_WIDTH) == PROP_MASK_WIDTH) {
3180       int i=0;
3181       char *width_spec=pProp->width_spec;
3182 
3183       if (((lMask & PROP_MASK_AW) == PROP_MASK_AW) &&
3184             ((lMask & PROP_MASK_AH) == PROP_MASK_AH)) {
3185          char *aw_spec=pProp->aw_spec;
3186          char *ah_spec=pProp->ah_spec;
3187 
3188          *plSkip |= (PROP_MASK_WIDTH | PROP_MASK_AW | PROP_MASK_AH);
3189          for (i=0; i < maxLineWidths; i++) {
3190             if (strcmp(width_spec, curWidthOfLineSpec[i]) == 0 &&
3191                   strcmp(aw_spec, curArrowHeadWSpec[i]) == 0 &&
3192                   strcmp(ah_spec, curArrowHeadHSpec[i]) == 0) {
3193                pProp->width_index = i;
3194                *plMask |= PROP_MASK_WIDTH_INDEX;
3195                break;
3196             }
3197          }
3198       } else {
3199          *plSkip |= (PROP_MASK_WIDTH);
3200          for (i=0; i < maxLineWidths; i++) {
3201             if (strcmp(width_spec, curWidthOfLineSpec[i]) == 0) {
3202                pProp->width_index = i;
3203                *plMask |= PROP_MASK_WIDTH_INDEX;
3204                break;
3205             }
3206          }
3207       }
3208    }
3209 }
3210 
DoGetProperty(target_index)3211 void DoGetProperty(target_index)
3212    int target_index;
3213 {
3214    struct PropertiesRec properties;
3215    struct PropInfoRec *ppir=NULL;
3216    long lMask=0L, lSkip=0L;
3217    int index=0;
3218    struct SelRec *saved_top_sel=NULL, *saved_bot_sel=NULL;
3219 
3220    memset(&properties, 0, sizeof(struct PropertiesRec));
3221    if (!SetupProperties(&properties, &lMask, &lSkip, NULL, COPY_PROP)) {
3222       return;
3223    }
3224    FixMasksForGetProperty(&properties, &lMask, &lSkip);
3225 
3226    HighLightReverse();
3227    saved_top_sel = topSel;
3228    saved_bot_sel = botSel;
3229    topSel = botSel = NULL;
3230    UpdSelBBox();
3231    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
3232       if ((lMask & ppir->bit) != 0L && (lSkip & ppir->bit) == 0) {
3233          if (index == target_index) {
3234             /* do something */
3235             DoGetAProperty(ppir->bit, &properties);
3236             break;
3237          }
3238          index++;
3239       }
3240    }
3241    topSel = saved_top_sel;
3242    botSel = saved_bot_sel;
3243    UpdSelBBox();
3244    HighLightReverse();
3245 }
3246 
CreateGetPropertyInfo()3247 EditAttrInfo *CreateGetPropertyInfo()
3248 {
3249    struct PropertiesRec properties;
3250    struct PropInfoRec *ppir=NULL;
3251    EditAttrInfo *pEditAttrInfo=NULL;
3252    long lMask=0L, lSkip=0L;
3253    int index=0, num_attrs=0;
3254    char **attr_strings=NULL, **status_strings=NULL;
3255 
3256    pEditAttrInfo = (EditAttrInfo*)malloc(sizeof(EditAttrInfo));
3257    if (pEditAttrInfo == NULL) FailAllocMessage();
3258    memset(pEditAttrInfo, 0, sizeof(EditAttrInfo));
3259 
3260    memset(&properties, 0, sizeof(struct PropertiesRec));
3261    if (!SetupProperties(&properties, &lMask, &lSkip, NULL, COPY_PROP)) {
3262       return NULL;
3263    }
3264    FixMasksForGetProperty(&properties, &lMask, &lSkip);
3265 
3266    for (ppir=gstPropInfo; ppir->bit != 0L; ppir++) {
3267       ppir->checked = FALSE;
3268       if ((lMask & ppir->bit) != 0L && (lSkip & ppir->bit) == 0) {
3269          num_attrs++;
3270       }
3271    }
3272    attr_strings = (char**)malloc(num_attrs*sizeof(char*));
3273    status_strings = (char**)malloc(num_attrs*sizeof(char*));
3274    if (attr_strings == NULL || status_strings == NULL) FailAllocMessage();
3275    memset(attr_strings, 0, num_attrs*sizeof(char*));
3276    memset(status_strings, 0, num_attrs*sizeof(char*));
3277 
3278    for (ppir=gstPropInfo, index=0; ppir->bit != 0L; ppir++) {
3279       long bit=ppir->bit;
3280 
3281       if ((lMask & bit) != 0L && (lSkip & bit) == 0) {
3282          char *psz=NULL, szBuf[MAXSTRING];
3283 
3284          *szBuf = '\0';
3285          FormatPropForDisplay(ppir->bit, &properties, ppir, szBuf);
3286          attr_strings[index] = UtilStrDup(szBuf);
3287          if (attr_strings[index] == NULL) FailAllocMessage();
3288          if ((psz=strchr(szBuf, ':')) != NULL) {
3289             *psz++ = '\0';
3290             UtilTrimBlanks(psz);
3291             sprintf(gszMsgBox,
3292                   TgLoadCachedString(CSTID_GET_NAMED_PROP_FROM_SEL_OBJ), szBuf);
3293             status_strings[index] = UtilStrDup(gszMsgBox);
3294             if (status_strings[index] == NULL) FailAllocMessage();
3295          }
3296          ppir->checked = TRUE;
3297          index++;
3298       }
3299    }
3300    pEditAttrInfo->num_attrs = num_attrs;
3301    pEditAttrInfo->fore_colors = NULL;
3302    pEditAttrInfo->attr_indices = NULL;
3303    pEditAttrInfo->attr_names = NULL;
3304    pEditAttrInfo->attr_values = NULL;
3305    pEditAttrInfo->attr_strings = attr_strings;
3306    pEditAttrInfo->status_strings = status_strings;
3307 
3308    return pEditAttrInfo;
3309 }
3310 
CopyProperties(nPrompt)3311 void CopyProperties(nPrompt)
3312    int nPrompt;
3313 {
3314    struct PropertiesRec properties;
3315    long lMask=0L, lSkip=0L;
3316    struct CheckArrayRec check_array;
3317 
3318    memset(&properties, 0, sizeof(struct PropertiesRec));
3319    memset(&check_array, 0, sizeof(struct CheckArrayRec));
3320    if (!SetupProperties(&properties, &lMask, &lSkip, &check_array, COPY_PROP)) {
3321       return;
3322    }
3323    tgWriteProfileString(gszCopyPasteSec, NULL, NULL, gszPropIniFile);
3324    tgWriteProfileString(NULL, NULL, NULL, gszPropIniFile);
3325    if (nPrompt) {
3326       SelectProperties(TgLoadString(STID_UNCHECK_PROP_FOR_COPY_DOTS),
3327             COPY_PROP, &check_array, &properties);
3328    } else {
3329       DoCopyProperties(&check_array, &properties);
3330    }
3331    CleanUpCheckArray(&check_array);
3332 }
3333 
SaveProperties()3334 void SaveProperties()
3335 {
3336    struct PropertiesRec properties;
3337    long lMask=0L, lSkip=0L;
3338    struct CheckArrayRec check_array;
3339 
3340    memset(&properties, 0, sizeof(struct PropertiesRec));
3341    memset(&check_array, 0, sizeof(struct CheckArrayRec));
3342    if (!SetupProperties(&properties, &lMask, &lSkip, &check_array, SAVE_PROP)) {
3343       return;
3344    }
3345    SelectProperties(TgLoadString(STID_UNCHECK_PROP_FOR_SAVE_DOTS),
3346          SAVE_PROP, &check_array, &properties);
3347    CleanUpCheckArray(&check_array);
3348 }
3349 
3350 static
WriteBufToIni(buf,ini_fname)3351 int WriteBufToIni(buf, ini_fname)
3352    char *buf, *ini_fname;
3353 {
3354    char *pszSec=buf, *pszKey=NULL, *pszKeyStart=NULL;
3355    int len=strlen(pszSec);
3356 
3357    pszKeyStart = (&buf[len+1]);
3358    if (*pszSec == '[' && pszSec[len-1] == ']') {
3359       pszSec[len-1] = '\0';
3360       pszSec++;
3361    }
3362    tgWriteProfileString(pszSec, NULL, NULL, ini_fname);
3363 
3364    for (pszKey=pszKeyStart; *pszKey != '\0'; pszKey++) {
3365       char *psz=strchr(pszKey, '=');
3366 
3367       if (psz == NULL) {
3368          tgWriteProfileString(pszSec, NULL, NULL, ini_fname);
3369          tgWriteProfileString(NULL, NULL, NULL, ini_fname);
3370          return FALSE;
3371       }
3372       *psz = '\0';
3373       tgWriteProfileString(pszSec, pszKey, &psz[1], ini_fname);
3374       *psz = '=';
3375 
3376       pszKey += strlen(pszKey);
3377    }
3378    tgWriteProfileString(NULL, NULL, NULL, ini_fname);
3379 
3380    return TRUE;
3381 }
3382 
PasteProperties(nPrompt)3383 void PasteProperties(nPrompt)
3384    int nPrompt;
3385 {
3386    struct PropertiesRec properties;
3387    long lMask=0L, lSkip=0L;
3388    struct CheckArrayRec check_array;
3389    int len=0, from_selection=FALSE;
3390    char *cut_buffer=NULL;
3391 
3392    cut_buffer = FetchSelectionOrCutBuffer(&len, &from_selection);
3393    if (cut_buffer == NULL) {
3394       MsgBox(TgLoadString(STID_CUT_BUFFER_EMPTY), TOOL_NAME, INFO_MB);
3395       return;
3396    }
3397    sprintf(gszMsgBox, "[%s]", gszCopyPasteSec);
3398    if (((unsigned char)(*cut_buffer)) != TGIF_HEADER ||
3399          strcmp(&cut_buffer[1], gszMsgBox) != 0) {
3400       FreeSelectionOrCutBuffer(cut_buffer, from_selection);
3401       MsgBox(TgLoadString(STID_COPY_PROP_BEFORE_PASTE), TOOL_NAME, INFO_MB);
3402       return;
3403    }
3404    if (!WriteBufToIni(&cut_buffer[1], gszPropIniFile)) {
3405       strcpy(gszMsgBox, TgLoadString(STID_MALFORMED_CUT_BUF_WHILE_PASTE));
3406       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3407 
3408       FreeSelectionOrCutBuffer(cut_buffer, from_selection);
3409       return;
3410    }
3411    FreeSelectionOrCutBuffer(cut_buffer, from_selection);
3412 
3413    memset(&check_array, 0, sizeof(struct CheckArrayRec));
3414    memset(&properties, 0, sizeof(struct PropertiesRec));
3415    properties.userdata = (void*)PASTE_PROP;
3416    if (!PrepareToPasteProperties(gszCopyPasteSec, &lMask, &check_array,
3417          &properties)) {
3418       return;
3419    }
3420    lSkip = (~lMask);
3421 
3422    if (nPrompt) {
3423       SelectProperties(TgLoadString(STID_UNCHECK_PROP_FOR_PASTE_DOTS),
3424             PASTE_PROP, &check_array, &properties);
3425    } else {
3426       DoPasteProperties(&check_array, &properties);
3427    }
3428    CleanUpCheckArray(&check_array);
3429 }
3430 
3431 static
GetPropSetNames(pp_dsp_ptr,ppsz_entries,pn_num_entries,pn_marked_index,pp_check_array,cur_buf,p_void)3432 int GetPropSetNames(pp_dsp_ptr, ppsz_entries, pn_num_entries, pn_marked_index,
3433       pp_check_array, cur_buf, p_void)
3434    DspList **pp_dsp_ptr;
3435    char ***ppsz_entries, *cur_buf;
3436    int *pn_num_entries, *pn_marked_index;
3437    struct CheckArrayRec **pp_check_array;
3438    void *p_void;
3439 {
3440    int num_rows=0;
3441    char **ppsz=NULL, *pszName=NULL;
3442    char *pszNames=tgGetProfileString(gszPropSetSec, NULL, gszPropIniFile);
3443 
3444    if (pszNames == NULL) {
3445       strcpy(gszMsgBox, TgLoadString(STID_NO_PROP_SETS_TO_RESTORE));
3446       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3447       return FALSE;
3448    }
3449    for (pszName=pszNames; *pszName != '\0'; pszName++, num_rows++) {
3450       pszName += strlen(pszName);
3451    }
3452    if (num_rows == 0) {
3453       tgWriteProfileString(gszPropSetSec, NULL, NULL, gszPropIniFile);
3454       tgWriteProfileString(NULL, NULL, NULL, gszPropIniFile);
3455       strcpy(gszMsgBox, TgLoadString(STID_NO_PROP_SETS_TO_RESTORE));
3456       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3457       return FALSE;
3458    }
3459    *ppsz_entries = (char**)malloc(num_rows*sizeof(char*));
3460    if (*ppsz_entries == NULL) FailAllocMessage();
3461    memset(*ppsz_entries, 0, num_rows*sizeof(char*));
3462 
3463    for (pszName=pszNames, ppsz=(*ppsz_entries); *pszName != '\0'; pszName++) {
3464       *ppsz = UtilStrDup(pszName);
3465       if ((*ppsz) == NULL) FailAllocMessage();
3466       ppsz++;
3467 
3468       pszName += strlen(pszName);
3469    }
3470    tgFreeProfileString(pszNames);
3471    *pn_num_entries = num_rows;
3472    return TRUE;
3473 }
3474 
3475 static
FreePropSetNames(pp_dsp_ptr,ppsz_entries,pn_num_entries,pn_marked_index,pp_check_array,cur_buf,btn_id,selected_index,p_void)3476 int FreePropSetNames(pp_dsp_ptr, ppsz_entries, pn_num_entries, pn_marked_index,
3477       pp_check_array, cur_buf, btn_id, selected_index, p_void)
3478    DspList **pp_dsp_ptr;
3479    char ***ppsz_entries, *cur_buf;
3480    int *pn_num_entries, *pn_marked_index, btn_id, selected_index;
3481    struct CheckArrayRec **pp_check_array;
3482    void *p_void;
3483 {
3484    int nReturn=TRUE;
3485 
3486    if (btn_id == BUTTON_OK) {
3487       nReturn = FALSE;
3488    }
3489    if (*ppsz_entries != NULL) {
3490       char **ppsz=(*ppsz_entries);
3491       int i=0;
3492 
3493       for (i=0; i < *pn_num_entries; i++) {
3494          UtilFree(*ppsz);
3495          ppsz++;
3496       }
3497       free(*ppsz_entries);
3498       *ppsz_entries = NULL;
3499    }
3500    return nReturn;
3501 }
3502 
3503 static
SelectPropSetForRestore(pszTitle,pszPropSet,nPropSetSize)3504 int SelectPropSetForRestore(pszTitle, pszPropSet, nPropSetSize)
3505    char *pszTitle, *pszPropSet;
3506    int nPropSetSize;
3507 {
3508    char win_name[128];
3509 
3510    ResetNamesInfo();
3511 
3512    NamesSetTitle(pszTitle);
3513    NamesAddButton(TgLoadCachedString(CSTID_OK), BUTTON_OK);
3514    NamesAddButton(TgLoadCachedString(CSTID_CANCEL), BUTTON_CANCEL);
3515    NamesSetDefaultBtnId(BUTTON_OK, BUTTON_OK);
3516    NamesSetEntries(NULL, NULL, 0, NULL, TRUE, INVALID, 0);
3517    NamesSetStyle(NAMES_SIMPLE_SELECT_NAME, NAMES_LOOP_MANY);
3518    NamesSetCallback((GetEntriesFunc*)GetPropSetNames,
3519          (AfterLoopFunc*)FreePropSetNames, NULL);
3520    sprintf(win_name, TgLoadString(STID_TOOL_RESTORE_PROP), TOOL_NAME);
3521 
3522    Names(win_name, NULL, pszPropSet, nPropSetSize, pszPropSet);
3523    return (*pszPropSet != '\0');
3524 }
3525 
RestoreProperties()3526 void RestoreProperties()
3527 {
3528    struct PropertiesRec properties;
3529    long lMask=0L, lSkip=0L;
3530    struct CheckArrayRec check_array;
3531    char szPropSet[MAXSTRING], szPropSetSec[MAXSTRING];
3532 
3533    memset(&check_array, 0, sizeof(struct CheckArrayRec));
3534    memset(&properties, 0, sizeof(struct PropertiesRec));
3535    properties.userdata = (void*)RESTORE_PROP;
3536    *szPropSet = '\0';
3537    if (!SelectPropSetForRestore(TgLoadString(STID_SEL_A_PROP_SET_TO_RESTORE),
3538          szPropSet, sizeof(szPropSet))) {
3539       return;
3540    }
3541    sprintf(szPropSetSec, "%s: %s", gszPropProfilePrefix, szPropSet);
3542    if (!PrepareToPasteProperties(szPropSetSec, &lMask, &check_array,
3543          &properties)) {
3544       return;
3545    }
3546    lSkip = (~lMask);
3547 
3548    SelectProperties(TgLoadString(STID_UNCHECK_PROP_FOR_RESTORE_DOTS),
3549          RESTORE_PROP, &check_array, &properties);
3550    CleanUpCheckArray(&check_array);
3551 }
3552 
BackupCopiedProperties()3553 void BackupCopiedProperties()
3554 {
3555    char *pszKeys=tgGetProfileString(gszCopyPasteSec, NULL, gszPropIniFile);
3556 
3557    tgWriteProfileString(gszCopyPasteBackupSec, NULL, NULL, gszPropIniFile);
3558    if (pszKeys != NULL) {
3559       char *pszKey=NULL;
3560 
3561       for (pszKey=pszKeys; *pszKey != '\0'; pszKey++) {
3562          char *pszValue=tgGetProfileString(gszCopyPasteSec, pszKey,
3563                gszPropIniFile);
3564 
3565          if (pszValue != NULL) {
3566             tgWriteProfileString(gszCopyPasteBackupSec, pszKey, pszValue,
3567                   gszPropIniFile);
3568             tgFreeProfileString(pszValue);
3569          }
3570          pszKey += strlen(pszKey);
3571       }
3572       tgFreeProfileString(pszKeys);
3573    }
3574    tgWriteProfileString(NULL, NULL, NULL, gszPropIniFile);
3575 }
3576 
RestoreCopiedProperties()3577 void RestoreCopiedProperties()
3578 {
3579    char *pszKeys=tgGetProfileString(gszCopyPasteBackupSec, NULL,
3580          gszPropIniFile);
3581 
3582    tgWriteProfileString(gszCopyPasteSec, NULL, NULL, gszPropIniFile);
3583    if (pszKeys != NULL) {
3584       char *pszKey=NULL;
3585 
3586       for (pszKey=pszKeys; *pszKey != '\0'; pszKey++) {
3587          char *pszValue=tgGetProfileString(gszCopyPasteBackupSec, pszKey,
3588                gszPropIniFile);
3589 
3590          if (pszValue != NULL) {
3591             tgWriteProfileString(gszCopyPasteSec, pszKey, pszValue,
3592                   gszPropIniFile);
3593             tgFreeProfileString(pszValue);
3594          }
3595          pszKey += strlen(pszKey);
3596       }
3597       tgFreeProfileString(pszKeys);
3598    }
3599    tgWriteProfileString(NULL, NULL, NULL, gszPropIniFile);
3600 }
3601 
3602 static
IniSectionListing(pszSection,pszIniFile,pnEntries)3603 DspList *IniSectionListing(pszSection, pszIniFile, pnEntries)
3604    char *pszSection, *pszIniFile;
3605    int *pnEntries;
3606 {
3607    DspList *dsp_ptr=NULL;
3608    char *pszKeys=NULL;
3609    int num_entries=0;
3610 
3611    *pnEntries = 0;
3612    if ((pszKeys=tgGetProfileString(pszSection, NULL, pszIniFile)) != NULL) {
3613       DspList *pdl=NULL;
3614       char *pszKey=NULL;
3615 
3616       for (pszKey=pszKeys; *pszKey != '\0'; pszKey++) {
3617          num_entries++;
3618          pszKey += strlen(pszKey);
3619       }
3620       dsp_ptr = (DspList*)malloc(num_entries*sizeof(DspList));
3621       if (dsp_ptr == NULL) FailAllocMessage();
3622       memset(dsp_ptr, 0, num_entries*sizeof(DspList));
3623       for (pszKey=pszKeys, pdl=dsp_ptr; *pszKey != '\0'; pszKey++, pdl++) {
3624          char *pszValue=tgGetProfileString(pszSection, pszKey, pszIniFile);
3625 
3626          sprintf(gszMsgBox, "%s=%s", pszKey, (pszValue==NULL ? "" : pszValue));
3627          UtilStrCpyN(pdl->itemstr, sizeof(pdl->itemstr), gszMsgBox);
3628          /* use the directory field for inherited */
3629          pdl->directory = TRUE;
3630          pdl->next = (&pdl[1]);
3631 
3632          if (pszValue != NULL) tgFreeProfileString(pszValue);
3633          pszKey += strlen(pszKey);
3634       }
3635       tgFreeProfileString(pszKeys);
3636    }
3637    if (num_entries == 0) return NULL;
3638 
3639    dsp_ptr[num_entries-1].next = NULL;
3640    *pnEntries = num_entries;
3641 
3642    return dsp_ptr;
3643 }
3644 
3645 static
EditIniSectionStrings(psz_title,dsp_ptr,entries,num_entries,pf_after_loop)3646 int EditIniSectionStrings(psz_title, dsp_ptr, entries, num_entries,
3647       pf_after_loop)
3648    char *psz_title, **entries;
3649    DspList *dsp_ptr;
3650    int num_entries;
3651    AfterLoopFunc *pf_after_loop;
3652 {
3653    char win_name[128];
3654 
3655    sprintf(win_name, TgLoadString(STID_TOOL_EDIT_INI_SECTION), TOOL_NAME);
3656    ResetNamesInfo();
3657    NamesSetTitle(psz_title);
3658    NamesAddButton(TgLoadCachedString(CSTID_OK), BUTTON_OK);
3659    NamesAddButton(TgLoadCachedString(CSTID_CANCEL), BUTTON_CANCEL);
3660    /* ignore double-click and <CR> */
3661    NamesSetDefaultBtnId(BUTTON_OK, INVALID);
3662    NamesSetStyle(NAMES_EDIT_ATTR, NAMES_LOOP_MANY);
3663    NamesSetCallback(NULL, pf_after_loop, NULL);
3664    NamesSetEntries(dsp_ptr, entries, num_entries, NULL, TRUE, INVALID, 0);
3665    return (Names(win_name, NULL, NULL, 0, NULL)==BUTTON_OK);
3666 }
3667 
3668 static
DefEditIniSectionCheck(psz_key,psz_value)3669 int DefEditIniSectionCheck(psz_key, psz_value)
3670    char *psz_key, *psz_value;
3671 {
3672    return TRUE;
3673 }
3674 
3675 static
EditIniSectionDefAfterLoop(pp_dsp_ptr,ppsz_entries,pn_num_entries,pn_marked_index,pp_check_array,cur_buf,btn_id,selected_index,p_void)3676 int EditIniSectionDefAfterLoop(pp_dsp_ptr, ppsz_entries, pn_num_entries,
3677       pn_marked_index, pp_check_array, cur_buf, btn_id, selected_index, p_void)
3678    DspList **pp_dsp_ptr;
3679    char ***ppsz_entries, *cur_buf;
3680    int *pn_num_entries, *pn_marked_index, btn_id, selected_index;
3681    struct CheckArrayRec **pp_check_array;
3682    void *p_void;
3683    /*
3684     * Returns FALSE if the content of the dialogbox is acceptable and
3685     *       the dialogbox will be closed.
3686     * Returns TRUE to tell the dialogbox to continue to loop.  In this case,
3687     *       this function should call MsgBox() to let the user know why
3688     *       the dialogbox is not closed.
3689     */
3690 {
3691    int i=0, num_entries=(*pn_num_entries);
3692 
3693    if (btn_id == BUTTON_CANCEL) {
3694       return FALSE;
3695    }
3696    for (i=0; i < num_entries; i++) {
3697       char *psz_key=(*ppsz_entries)[i], *psz_value=NULL;
3698       char *psz=strchr(psz_key, '=');
3699 
3700       *psz = '\0';
3701       psz_value = (&psz[1]);
3702       if (!DefEditIniSectionCheck(psz_key, psz_value)) {
3703          /* psz_value is not an acceptable value for psz_key */
3704          sprintf(gszMsgBox, TgLoadString(STID_BAD_VALUE_FOR_KEY), psz_key,
3705                psz_value);
3706          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3707          *psz = '=';
3708          return TRUE;
3709       }
3710       *psz = '=';
3711    }
3712    /* everything is fine */
3713    return FALSE;
3714 }
3715 
EditIniSection(pszTitle,pszSection,pszIniFile,pf_after_loop)3716 int EditIniSection(pszTitle, pszSection, pszIniFile, pf_after_loop)
3717    char *pszTitle, *pszSection, *pszIniFile;
3718    AfterLoopFunc *pf_after_loop;
3719 {
3720    int num_entries=0, ok_pressed=FALSE;
3721    DspList *dsp_ptr=NULL;
3722    char sz_title[MAXSTRING], **entries=NULL;
3723 
3724    dsp_ptr = IniSectionListing(pszSection, pszIniFile, &num_entries);
3725    if (dsp_ptr == NULL) {
3726       sprintf(gszMsgBox, TgLoadString(STID_NOTHING_TO_EDIT_SEC_EMPTY),
3727             pszSection, pszIniFile);
3728       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3729       return FALSE;
3730    }
3731    MakeQuiescent();
3732 
3733    ignoreDirectoryFlag = TRUE;
3734    entries = MakeNameDspItemArray(num_entries, dsp_ptr);
3735    ignoreDirectoryFlag = FALSE;
3736    if (pszTitle == NULL) {
3737       sprintf(sz_title, TgLoadString(STID_EDIT_VALUES_FOR_DOTS), pszSection);
3738    } else {
3739       UtilStrCpyN(sz_title, sizeof(sz_title), pszTitle);
3740    }
3741    if (pf_after_loop == NULL) {
3742       pf_after_loop = (AfterLoopFunc*)EditIniSectionDefAfterLoop;
3743    }
3744    if (EditIniSectionStrings(sz_title, dsp_ptr, entries, num_entries,
3745          pf_after_loop)) {
3746       int i=0;
3747 
3748       tgWriteProfileString(pszSection, NULL, NULL, pszIniFile);
3749       for (i=0; i < num_entries; i++) {
3750          char *psz=strchr(entries[i], '=');
3751 
3752          *psz++ = '\0';
3753          tgWriteProfileString(pszSection, entries[i], psz, pszIniFile);
3754       }
3755       tgWriteProfileString(NULL, NULL, NULL, pszIniFile);
3756       ok_pressed = TRUE;
3757    }
3758    free(dsp_ptr);
3759    free(*entries);
3760    free(entries);
3761 
3762    SetCurChoice(curChoiceBeforeMakeQuiescent);
3763 
3764    return ok_pressed;
3765 }
3766 
3767 static
IniSectionKeysListing(pszSection,pszIniFile,pnEntries)3768 DspList *IniSectionKeysListing(pszSection, pszIniFile, pnEntries)
3769    char *pszSection, *pszIniFile;
3770    int *pnEntries;
3771 {
3772    DspList *dsp_ptr=NULL;
3773    char *pszKeys=NULL;
3774    int num_entries=0;
3775 
3776    *pnEntries = 0;
3777    if ((pszKeys=tgGetProfileString(pszSection, NULL, pszIniFile)) != NULL) {
3778       DspList *pdl=NULL;
3779       char *pszKey=NULL;
3780 
3781       for (pszKey=pszKeys; *pszKey != '\0'; pszKey++) {
3782          num_entries++;
3783          pszKey += strlen(pszKey);
3784       }
3785       dsp_ptr = (DspList*)malloc(num_entries*sizeof(DspList));
3786       if (dsp_ptr == NULL) FailAllocMessage();
3787       memset(dsp_ptr, 0, num_entries*sizeof(DspList));
3788       for (pszKey=pszKeys, pdl=dsp_ptr; *pszKey != '\0'; pszKey++, pdl++) {
3789          UtilStrCpyN(pdl->itemstr, sizeof(pdl->itemstr), pszKey);
3790          /* use the directory field for inherited */
3791          pdl->next = (&pdl[1]);
3792 
3793          pszKey += strlen(pszKey);
3794       }
3795       tgFreeProfileString(pszKeys);
3796    }
3797    if (num_entries == 0) return NULL;
3798 
3799    dsp_ptr[num_entries-1].next = NULL;
3800    *pnEntries = num_entries;
3801 
3802    return dsp_ptr;
3803 }
3804 
3805 static
SelectFromIniSectionStrings(psz_title,dsp_ptr,entries,num_entries,selected_str,selected_str_sz)3806 int SelectFromIniSectionStrings(psz_title, dsp_ptr, entries, num_entries,
3807       selected_str, selected_str_sz)
3808    char *psz_title, **entries, *selected_str;
3809    DspList *dsp_ptr;
3810    int num_entries, selected_str_sz;
3811 {
3812    char win_name[128];
3813 
3814    sprintf(win_name, TgLoadString(STID_TOOL_SEL_FROM_INI_SECTION), TOOL_NAME);
3815    ResetNamesInfo();
3816    NamesSetTitle(psz_title);
3817    NamesAddButton(TgLoadCachedString(CSTID_OK), BUTTON_OK);
3818    NamesAddButton(TgLoadCachedString(CSTID_CANCEL), BUTTON_CANCEL);
3819    /* ignore double-click and <CR> */
3820    NamesSetDefaultBtnId(BUTTON_OK, BUTTON_OK);
3821    NamesSetStyle(NAMES_SIMPLE_SELECT_NAME, NAMES_LOOP_ONCE);
3822    NamesSetEntries(dsp_ptr, entries, num_entries, NULL, TRUE, INVALID, 0);
3823    return (Names(win_name, NULL, selected_str, selected_str_sz,
3824          NULL)==BUTTON_OK);
3825 }
3826 
SelectFromIniSection(pszTitle,pszSection,pszIniFile)3827 char *SelectFromIniSection(pszTitle, pszSection, pszIniFile)
3828    char *pszTitle, *pszSection, *pszIniFile;
3829 {
3830    int num_entries=0;
3831    DspList *dsp_ptr=NULL;
3832    char sz_title[MAXSTRING], **entries=NULL, selected_str[MAXSTRING];
3833 
3834    MakeQuiescent();
3835 
3836    dsp_ptr = IniSectionKeysListing(pszSection, pszIniFile, &num_entries);
3837    if (dsp_ptr == NULL) {
3838       sprintf(gszMsgBox, TgLoadString(STID_NOTHING_TO_SEL_SEC_EMPTY),
3839             pszSection, pszIniFile);
3840       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3841       return FALSE;
3842    }
3843    ignoreDirectoryFlag = TRUE;
3844    entries = MakeNameDspItemArray(num_entries, dsp_ptr);
3845    ignoreDirectoryFlag = FALSE;
3846    if (pszTitle == NULL) {
3847       sprintf(sz_title, TgLoadString(STID_SELECT_A_VALUE_FROM_SEC_DOTS),
3848             pszSection);
3849    } else {
3850       UtilStrCpyN(sz_title, sizeof(sz_title), pszTitle);
3851    }
3852    *selected_str = '\0';
3853    if (!SelectFromIniSectionStrings(sz_title, dsp_ptr, entries, num_entries,
3854          selected_str, sizeof(selected_str))) {
3855       *selected_str = '\0';
3856    }
3857    free(dsp_ptr);
3858    free(*entries);
3859    free(entries);
3860 
3861    return (*selected_str=='\0' ? NULL : UtilStrDup(selected_str));
3862 }
3863 
3864 /* ----------------------- Recently Used Files ----------------------- */
3865 
3866 static int maxRecentFiles=10;
3867 
FreeRecentFilesListing(pkvi,max_count)3868 void FreeRecentFilesListing(pkvi, max_count)
3869    KeyValInfo *pkvi;
3870    int max_count;
3871 {
3872    if (pkvi != NULL) {
3873       int i=0;
3874 
3875       for (i=0; i < max_count; i++) {
3876          UtilFree(pkvi[i].key);
3877          UtilFree(pkvi[i].value);
3878       }
3879       free(pkvi);
3880    }
3881 }
3882 
RecentFilesListing(pnEntries)3883 KeyValInfo *RecentFilesListing(pnEntries)
3884    int *pnEntries;
3885    /*
3886     * key will store the file name
3887     * value will store the full path
3888     */
3889 {
3890    int i=0, count=0, real_count=0, fix_up=FALSE;
3891    char *buf=NULL;
3892    KeyValInfo *pkvi=NULL;
3893 
3894    *pnEntries = 0;
3895    if ((buf=tgGetProfileString(gpszRecentFilesSec, gpszRecentFilesCountKey,
3896          gszFilesIniFile)) == NULL) {
3897       return NULL;
3898    }
3899    count = atoi(buf);
3900    tgFreeProfileString(buf);
3901    pkvi = (KeyValInfo*)malloc(count*sizeof(KeyValInfo));
3902    if (pkvi == NULL) FailAllocMessage();
3903    memset(pkvi, 0, count*sizeof(KeyValInfo));
3904    for (i=0; i < count; i++) {
3905       char sz_key[40], *psz=NULL;
3906 
3907       sprintf(sz_key, "%1d", i);
3908       buf = tgGetProfileString(gpszRecentFilesSec, sz_key, gszFilesIniFile);
3909       /* just in case the ini file is corrupted */
3910       if (buf == NULL) {
3911          fix_up = TRUE;
3912          continue;
3913       }
3914       psz = UtilStrRChr(buf, DIR_SEP);
3915       if (psz != NULL) {
3916          *psz = '\0';
3917          pkvi[real_count].key = UtilStrDup(&psz[1]);
3918          if (pkvi[real_count].key == NULL) FailAllocMessage();
3919          *psz = DIR_SEP;
3920       } else {
3921          pkvi[real_count].key = UtilStrDup(buf);
3922          if (pkvi[real_count].key == NULL) FailAllocMessage();
3923       }
3924       pkvi[real_count].value = UtilStrDup(buf);
3925       if (pkvi[real_count].value == NULL) FailAllocMessage();
3926       if (fix_up) {
3927          tgWriteProfileString(gpszRecentFilesSec, sz_key, buf, gszFilesIniFile);
3928       }
3929       tgFreeProfileString(buf);
3930       real_count++;
3931    }
3932    if (fix_up) {
3933       sprintf(gszMsgBox, "%1d", real_count);
3934       tgWriteProfileString(gpszRecentFilesSec, gpszRecentFilesCountKey,
3935             gszMsgBox, gszFilesIniFile);
3936       tgWriteProfileString(NULL, NULL, NULL, gszFilesIniFile);
3937    }
3938    if (real_count == 0) {
3939       FreeRecentFilesListing(pkvi, count);
3940       pkvi = NULL;
3941    }
3942    *pnEntries = real_count;
3943 
3944    return pkvi;
3945 }
3946 
OpenARecentlyUsedFile(file_index)3947 void OpenARecentlyUsedFile(file_index)
3948    int file_index;
3949 {
3950    int count=0, do_not_save=FALSE, need_to_check_auto_exec=FALSE;
3951    KeyValInfo *pkvi=RecentFilesListing(&count);
3952    char *psz_url=NULL;
3953 
3954    if (pkvi == NULL || count == 0 || file_index >= count) return;
3955 
3956    psz_url = pkvi[file_index].value;
3957 
3958    if (!BeforeOpenURL(&do_not_save)) {
3959       return;
3960    }
3961    OpenURL(psz_url, do_not_save, &need_to_check_auto_exec);
3962    FreeRecentFilesListing(pkvi, count);
3963    AfterOpenURL(need_to_check_auto_exec);
3964 }
3965 
AddARecentlyUsedFile(path)3966 void AddARecentlyUsedFile(path)
3967    char *path;
3968 {
3969    int i=0, count=0, move_index=INVALID;
3970    KeyValInfo *pkvi=RecentFilesListing(&count);
3971 
3972    if (count == 0) {
3973       /* do not translate -- program constants */
3974       tgWriteProfileString(gpszRecentFilesSec, NULL, NULL, gszFilesIniFile);
3975       tgWriteProfileString(gpszRecentFilesSec, "0", path, gszFilesIniFile);
3976    } else {
3977       char sz_key[40];
3978 
3979       for (i=0; i < count; i++) {
3980          if (strcmp(path, pkvi[i].value) == 0) {
3981             if (i == 0) {
3982                FreeRecentFilesListing(pkvi, count);
3983                return;
3984             }
3985             move_index = i;
3986             break;
3987          }
3988       }
3989       tgWriteProfileString(gpszRecentFilesSec, NULL, NULL, gszFilesIniFile);
3990       if (move_index == INVALID) {
3991          if (count+1 > maxRecentFiles) {
3992             count = maxRecentFiles-1;
3993          }
3994          for (i=0; i < count+1; i++) {
3995             sprintf(sz_key, "%1d", i);
3996             if (i == 0) {
3997                tgWriteProfileString(gpszRecentFilesSec, sz_key, path,
3998                      gszFilesIniFile);
3999             } else {
4000                tgWriteProfileString(gpszRecentFilesSec, sz_key, pkvi[i-1].value,
4001                      gszFilesIniFile);
4002             }
4003          }
4004       } else {
4005          if (count > maxRecentFiles) {
4006             count = maxRecentFiles;
4007          }
4008          for (i=0; i < count; i++) {
4009             sprintf(sz_key, "%1d", i);
4010             if (i == 0) {
4011                tgWriteProfileString(gpszRecentFilesSec, sz_key,
4012                      pkvi[move_index].value, gszFilesIniFile);
4013             } else if (i <= move_index) {
4014                tgWriteProfileString(gpszRecentFilesSec, sz_key, pkvi[i-1].value,
4015                      gszFilesIniFile);
4016             } else {
4017                tgWriteProfileString(gpszRecentFilesSec, sz_key, pkvi[i].value,
4018                      gszFilesIniFile);
4019             }
4020          }
4021       }
4022    }
4023    sprintf(gszMsgBox, "%1d", (move_index==INVALID ? count+1 : count));
4024    tgWriteProfileString(gpszRecentFilesSec, gpszRecentFilesCountKey,
4025          gszMsgBox, gszFilesIniFile);
4026    tgWriteProfileString(NULL, NULL, NULL, gszFilesIniFile);
4027    FreeRecentFilesListing(pkvi, count);
4028 }
4029 
InitRecentFiles()4030 int InitRecentFiles()
4031 {
4032    char *c_ptr=NULL;
4033 
4034    if (PRTGIF) return TRUE;
4035 
4036    /* do not translate -- program constants */
4037    sprintf(gszFilesIniFile, "%s%cfiles.ini", tgifDir, DIR_SEP);
4038 
4039    maxRecentFiles = 10;
4040    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"MaxRecentFiles")) != NULL) {
4041       maxRecentFiles = atoi(c_ptr);
4042       if (maxRecentFiles <= 0 || maxRecentFiles > 99) {
4043          sprintf(gszMsgBox, TgLoadString(STID_INVALID_XDEF_RNG_USE_ALT_VAL),
4044                TOOL_NAME, "MaxRecentFiles", c_ptr, 1, 99, 10);
4045          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4046          maxRecentFiles = 10;
4047       }
4048    }
4049    if ((c_ptr=tgGetProfileString(gpszRecentFilesSec, gpszRecentFilesCountKey,
4050          gszFilesIniFile)) != NULL) {
4051       int count = atoi(c_ptr);
4052 
4053       if (count > maxRecentFiles) {
4054          sprintf(gszMsgBox, "%1d", maxRecentFiles);
4055          tgWriteProfileString(gpszRecentFilesSec, gpszRecentFilesCountKey,
4056                gszMsgBox, gszFilesIniFile);
4057          tgWriteProfileString(NULL, NULL, NULL, gszFilesIniFile);
4058       }
4059       tgFreeProfileString(c_ptr);
4060    }
4061    return TRUE;
4062 }
4063 
CleanUpRecentFiles()4064 void CleanUpRecentFiles()
4065 {
4066 }
4067 
4068 /* ----------------------- SimpleString Object ----------------------- */
4069 
CreateSimpleStringObj(buf)4070 int CreateSimpleStringObj(buf)
4071    char *buf;
4072 {
4073    SimpleString *ss_ptr=NULL;
4074    struct ObjRec *obj_ptr=NULL;
4075 
4076    ss_ptr = (SimpleString *)malloc(sizeof(SimpleString));
4077    if (ss_ptr == NULL) FailAllocMessage();
4078    memset(ss_ptr, 0, sizeof(SimpleString));
4079 
4080    DynStrSet(&ss_ptr->dyn_str, buf);
4081 
4082    obj_ptr = (struct ObjRec *)malloc(sizeof(struct ObjRec));
4083    if (obj_ptr == NULL) FailAllocMessage();
4084    memset(obj_ptr, 0, sizeof(struct ObjRec));
4085 
4086    obj_ptr->type = OBJ_SS;
4087    obj_ptr->color = colorIndex;
4088    if (mainDisplay != NULL) {
4089       UtilStrCpyN(obj_ptr->color_str, sizeof(obj_ptr->color_str),
4090             colorMenuItems[colorIndex]);
4091    }
4092    obj_ptr->id = objId++;
4093    obj_ptr->detail.ss = ss_ptr;
4094    /*
4095     * the double-byte stuff is left alone, always single byte for now
4096     */
4097 
4098    AddObj(NULL, topObj, obj_ptr);
4099 
4100    return TRUE;
4101 }
4102 
SaveSimpleStringObj(FP,ObjPtr)4103 void SaveSimpleStringObj(FP, ObjPtr)
4104    FILE *FP;
4105    struct ObjRec *ObjPtr;
4106 {
4107    SimpleString *ss_ptr=ObjPtr->detail.ss;
4108 
4109    if (fprintf(FP, "simple_string('%s',%1d,%1d,%1d,%1d,\n\t\"",
4110          colorMenuItems[ObjPtr->color], ObjPtr->id, ss_ptr->double_byte,
4111          ss_ptr->double_byte_mod_bytes, ss_ptr->double_byte_vertical) == EOF) {
4112       writeFileFailed = TRUE;
4113    }
4114    if (ss_ptr->double_byte) {
4115       SaveDoubleByteString(FP, ss_ptr->dyn_str.s);
4116    } else {
4117       SaveString(FP, ss_ptr->dyn_str.s);
4118    }
4119    if (fprintf(FP, "\")") == EOF) writeFileFailed = TRUE;
4120 }
4121 
ReadSimpleStringObj(FP,Inbuf,ObjPtr)4122 void ReadSimpleStringObj(FP, Inbuf, ObjPtr)
4123    FILE *FP;
4124    char *Inbuf;
4125    struct ObjRec **ObjPtr;
4126 {
4127    SimpleString *ss_ptr=NULL;
4128    char *psz=NULL, *s=NULL, color_str[40], *line=NULL;
4129    int id=0, double_byte=FALSE, db_mod_bytes=FALSE, db_vertical=FALSE;
4130    int new_alloc=FALSE;
4131 
4132    *ObjPtr = NULL;
4133 
4134    s = FindChar((int)'(', Inbuf);
4135    s = ParseStr(s, (int)',', color_str, sizeof(color_str));
4136    InitScan(s, "\t\n, ");
4137 
4138    if (GETINT("simple_string", id, "id") == INVALID ||
4139        GETINT("simple_string", double_byte, "double_byte") == INVALID ||
4140        GETINT("simple_string", db_mod_bytes, "db_mod_bytes") == INVALID ||
4141        GETINT("simple_string", db_vertical, "db_vertical") == INVALID) {
4142       return;
4143    }
4144    if (id >= objId) objId = id+1;
4145 
4146    if ((line=UtilGetALine(FP)) == NULL) {
4147       sprintf(gszMsgBox, TgLoadString(STID_UNEXPECTED_EOF_IN_ABORT_READ),
4148             scanFileName, scanLineNum, "ReadSimpleStringObj()");
4149       if (PRTGIF) {
4150          fprintf(stderr, "%s\n", gszMsgBox);
4151       } else {
4152          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4153       }
4154       return;
4155    }
4156    scanLineNum++;
4157 
4158    psz = FindChar((int)'"', line);
4159    s = ReadString(psz);
4160    *(--s) = '\0';
4161 
4162    *ObjPtr = (struct ObjRec *)malloc(sizeof(struct ObjRec));
4163    if (*ObjPtr == NULL) FailAllocMessage();
4164    memset(*ObjPtr, 0, sizeof(struct ObjRec));
4165    ss_ptr = (SimpleString *)malloc(sizeof(SimpleString));
4166    if (ss_ptr == NULL) FailAllocMessage();
4167    memset(ss_ptr, 0, sizeof(SimpleString));
4168 
4169    DynStrSet(&ss_ptr->dyn_str, psz);
4170    free(line);
4171 
4172    ss_ptr->double_byte = double_byte;
4173    ss_ptr->double_byte_mod_bytes = db_mod_bytes;
4174    ss_ptr->double_byte_vertical = db_vertical;
4175 
4176    (*ObjPtr)->color = QuickFindColorIndex(*ObjPtr, color_str, &new_alloc, TRUE);
4177    UtilStrCpyN((*ObjPtr)->color_str, sizeof((*ObjPtr)->color_str), color_str);
4178    (*ObjPtr)->id = id;
4179 }
4180 
FreeSimpleStringObj(ObjPtr)4181 void FreeSimpleStringObj(ObjPtr)
4182    struct ObjRec *ObjPtr;
4183 {
4184    UtilFree(ObjPtr->detail.ss->dyn_str.s);
4185    free(ObjPtr->detail.ss);
4186    free(ObjPtr);
4187 }
4188 
4189 /* ----------------------- ReplaceGraphic() ----------------------- */
4190 
4191 static
UnInheritAllAttrs(obj_ptr)4192 void UnInheritAllAttrs(obj_ptr)
4193    struct ObjRec *obj_ptr;
4194 {
4195    struct AttrRec *attr_ptr;
4196 
4197    for (attr_ptr=obj_ptr->fattr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
4198       if (attr_ptr->inherited) {
4199          attr_ptr->inherited = FALSE;
4200       }
4201    }
4202 }
4203 
ReplaceGraphic()4204 void ReplaceGraphic()
4205 {
4206    struct SelRec *saved_top_sel=NULL, *saved_bot_sel=NULL, *sel_ptr=NULL;
4207    struct SelRec *prev_sel=NULL;
4208    struct ObjRec *pasted_top_obj=NULL, *pasted_bot_obj=NULL;
4209    struct ObjRec *icon_obj_to_replace=NULL;
4210    char *orig_cut_buffer=NULL;
4211    int sel_ltx=selLtX, sel_lty=selLtY, sel_rbx=selRbX, sel_rby=selRbY;
4212    int len=0, changed=FALSE, icons_converted_to_groups=FALSE, icon_count=0;
4213    int from_selection=FALSE;
4214 
4215    if (topSel == NULL) {
4216       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
4217       return;
4218    }
4219    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
4220       if (sel_ptr->obj->type == OBJ_ICON) {
4221          icon_obj_to_replace = sel_ptr->obj;
4222          icon_count++;
4223       }
4224    }
4225    if (icon_count > 1) {
4226       MsgBox(TgLoadString(STID_TOO_MANY_ICON_REPLACE_GRAPHIC), TOOL_NAME,
4227             INFO_MB);
4228       return;
4229    }
4230    orig_cut_buffer = FetchSelectionOrCutBuffer(&len, &from_selection);
4231    if (orig_cut_buffer == NULL) {
4232       MsgBox(TgLoadString(STID_CUT_BUFFER_EMPTY), TOOL_NAME, INFO_MB);
4233       return;
4234    }
4235    if (CutBufferType(orig_cut_buffer) != CBF_TGIF_OBJ) {
4236       sprintf(gszMsgBox, TgLoadString(STID_CANT_REPLACE_GRAPHIC_NOT_TGIF),
4237             TOOL_NAME);
4238       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4239       return;
4240    }
4241    HighLightReverse();
4242    saved_top_sel = topSel;
4243    saved_bot_sel = botSel;
4244    topSel = botSel = NULL;
4245    UpdSelBBox();
4246 
4247    if (!GetObjsFromCutBuffer(&orig_cut_buffer[1], len-1, &pasted_top_obj,
4248          &pasted_bot_obj)) {
4249       FreeSelectionOrCutBuffer(orig_cut_buffer, from_selection);
4250       topSel = saved_top_sel;
4251       botSel = saved_bot_sel;
4252       UpdSelBBox();
4253       HighLightForward();
4254       return;
4255    }
4256    FreeSelectionOrCutBuffer(orig_cut_buffer, from_selection);
4257 
4258    if (pasted_top_obj == NULL || pasted_top_obj != pasted_bot_obj) {
4259       MsgBox(TgLoadString(STID_CANT_REPLACE_GRAPHIC_TOO_MANY), TOOL_NAME,
4260             INFO_MB);
4261       topSel = saved_top_sel;
4262       botSel = saved_bot_sel;
4263       UpdSelBBox();
4264       HighLightForward();
4265       return;
4266    }
4267    if (pasted_top_obj->type == OBJ_SYM || pasted_top_obj->type == OBJ_ICON ||
4268          pasted_top_obj->type == OBJ_PIN) {
4269       pasted_top_obj->type = OBJ_GROUP;
4270    }
4271    DelAllAttrs(pasted_top_obj->fattr);
4272 
4273    topSel = saved_top_sel;
4274    botSel = saved_bot_sel;
4275    UpdSelBBox();
4276 
4277    StartCompositeCmd();
4278    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=prev_sel) {
4279       struct ObjRec *obj_ptr=sel_ptr->obj, *new_obj_ptr=NULL;
4280       int convert_to_group=TRUE;
4281 
4282       prev_sel = sel_ptr->prev;
4283       if (obj_ptr->type == OBJ_TEXT) continue;
4284 
4285       if (obj_ptr->type == OBJ_ICON && obj_ptr == icon_obj_to_replace) {
4286          switch (MsgBox(TgLoadString(STID_Q_CVT_ICON_TO_GRP_IN_REPLACE),
4287                TOOL_NAME, YNC_MB)) {
4288          case MB_ID_YES: break;
4289          case MB_ID_NO: convert_to_group = FALSE; break;
4290          case MB_ID_CANCEL: continue;
4291          }
4292       }
4293       changed = TRUE;
4294 
4295       PrepareToReplaceAnObj(obj_ptr);
4296 
4297       new_obj_ptr = DupObj(pasted_top_obj);
4298       MoveObj(new_obj_ptr, obj_ptr->obbox.ltx-new_obj_ptr->obbox.ltx,
4299             obj_ptr->obbox.lty-new_obj_ptr->obbox.lty);
4300 
4301       new_obj_ptr->prev = obj_ptr->prev;
4302       new_obj_ptr->next = obj_ptr->next;
4303       new_obj_ptr->fattr = obj_ptr->fattr;
4304       new_obj_ptr->lattr = obj_ptr->lattr;
4305 
4306       obj_ptr->fattr = obj_ptr->lattr = NULL;
4307       UnlinkObj(obj_ptr);
4308 
4309       if (obj_ptr->type == OBJ_SYM) new_obj_ptr->type = OBJ_SYM;
4310 
4311       UpdAttrOwner(new_obj_ptr->fattr, new_obj_ptr);
4312       if (new_obj_ptr->prev == NULL) {
4313          curPage->top = topObj = new_obj_ptr;
4314       } else {
4315          new_obj_ptr->prev->next = new_obj_ptr;
4316       }
4317       if (new_obj_ptr->next == NULL) {
4318          curPage->bot = botObj = new_obj_ptr;
4319       } else {
4320          new_obj_ptr->next->prev = new_obj_ptr;
4321       }
4322       AdjObjBBox(new_obj_ptr);
4323       ExpandCurSelBBoxes(new_obj_ptr);
4324 
4325       sel_ptr->obj = new_obj_ptr;
4326       if (convert_to_group) {
4327          icons_converted_to_groups = TRUE;
4328          UnInheritAllAttrs(new_obj_ptr);
4329       } else {
4330          struct SelRec *tmp_sel=NULL;
4331 
4332          saved_top_sel = topSel;
4333          saved_bot_sel = botSel;
4334          topSel = botSel = tmp_sel = SelectThisObject(new_obj_ptr);
4335          UpdSelBBox();
4336          MakeIconic(NULL, FALSE);
4337          topSel = saved_top_sel;
4338          botSel = saved_bot_sel;
4339          UpdSelBBox();
4340          free(tmp_sel);
4341       }
4342       RecordReplaceAnObj(new_obj_ptr);
4343       FreeObj(obj_ptr);
4344    }
4345    EndCompositeCmd();
4346 
4347    if (changed) {
4348       UpdSelBBox();
4349       RedrawAreas(botObj, sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
4350             sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
4351             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
4352             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
4353       SetFileModified(TRUE);
4354       justDupped = FALSE;
4355       if (icons_converted_to_groups) {
4356          MsgBox(TgLoadString(STID_REPLACE_GRAPHIC_ICON_TO_GROUP), TOOL_NAME,
4357                INFO_MB);
4358       } else {
4359          Msg(TgLoadString(STID_REPLACE_GRAPHIC_DONE));
4360       }
4361    }
4362    HighLightForward();
4363 }
4364 
4365 /* ----------------------- ReplaceGraphic() ----------------------- */
4366 
4367 static int wordWrapDuringImportTextFile=FALSE;
4368 
4369 #ifndef A4PAPER
4370 static int topMarginForImportTextFile=HALF_INCH;
4371 static int leftMarginForImportTextFile=HALF_INCH;
4372 static int rightMarginForImportTextFile=HALF_INCH;
4373 static int bottomMarginForImportTextFile=HALF_INCH;
4374 #else /* A4PAPER */
4375 static int topMarginForImportTextFile=ONE_CM;
4376 static int leftMarginForImportTextFile=ONE_CM;
4377 static int rightMarginForImportTextFile=ONE_CM;
4378 static int bottomMarginForImportTextFile=ONE_CM;
4379 #endif /* A4PAPER */
4380 
4381 static
DoSetMarginsForImportMultipageTextFile(spec)4382 int DoSetMarginsForImportMultipageTextFile(spec)
4383    char *spec;
4384 {
4385    int top=0, bottom=0, left=0, right=0;
4386    char *top_str=NULL, *bottom_str=NULL, *left_str=NULL, *right_str=NULL;
4387 
4388    UtilTrimBlanks(spec);
4389    top_str = strtok(spec, ",\t\n\r");
4390    if (top_str == NULL) return FALSE;
4391    bottom_str = strtok(NULL, ",\t\n\r");
4392    if (bottom_str == NULL) return FALSE;
4393    left_str = strtok(NULL, ",\t\n\r");
4394    if (left_str == NULL) return FALSE;
4395    right_str = strtok(NULL, ",\t\n\r");
4396    if (right_str == NULL) return FALSE;
4397 
4398    if (GetDimension(top_str, FALSE, &top) &&
4399          GetDimension(bottom_str, FALSE, &bottom) &&
4400          GetDimension(left_str, FALSE, &left) &&
4401          GetDimension(right_str, FALSE, &right)) {
4402       topMarginForImportTextFile = top;
4403       bottomMarginForImportTextFile = bottom;
4404       leftMarginForImportTextFile = left;
4405       rightMarginForImportTextFile = right;
4406 
4407       return TRUE;
4408    }
4409    return FALSE;
4410 }
4411 
4412 static
GetCurMarginsForImportTextFile(buf)4413 void GetCurMarginsForImportTextFile(buf)
4414    char *buf;
4415 {
4416    char n_str[MAXSTRING], s_str[MAXSTRING], w_str[MAXSTRING], e_str[MAXSTRING];
4417    float n=((float)topMarginForImportTextFile)*printMag/((float)100.0);
4418    float s=((float)bottomMarginForImportTextFile)*printMag/((float)100.0);
4419    float w=((float)leftMarginForImportTextFile)*printMag/((float)100.0);
4420    float e=((float)rightMarginForImportTextFile)*printMag/((float)100.0);
4421    float n_val=(float)0.0, s_val=(float)0.0, w_val=(float)0.0, e_val=(float)0.0;
4422    float unit=(float)0.0;
4423 
4424    switch (gridSystem) {
4425    case ENGLISH_GRID:
4426       unit = (float)PIX_PER_INCH;
4427 
4428       n_val = n / unit;
4429       s_val = s / unit;
4430       w_val = w / unit;
4431       e_val = e / unit;
4432       FormatFloat(&n_val, n_str);
4433       FormatFloat(&s_val, s_str);
4434       FormatFloat(&w_val, w_str);
4435       FormatFloat(&e_val, e_str);
4436       /* do not translate -- program constants */
4437       sprintf(buf, "[ %s in, %s in, %s in, %s in ]",
4438             n_str, s_str, w_str, e_str);
4439       break;
4440    case METRIC_GRID:
4441       unit = (float)ONE_CM;
4442 
4443       n_val = n / unit;
4444       s_val = s / unit;
4445       w_val = w / unit;
4446       e_val = e / unit;
4447       FormatFloat(&n_val, n_str);
4448       FormatFloat(&s_val, s_str);
4449       FormatFloat(&w_val, w_str);
4450       FormatFloat(&e_val, e_str);
4451       /* do not translate -- program constants */
4452       sprintf(buf, "[ %s cm, %s cm, %s cm, %s cm ]",
4453             n_str, s_str, w_str, e_str);
4454       break;
4455    }
4456 }
4457 
ImportMultipageTextFile()4458 void ImportMultipageTextFile()
4459 {
4460 }
4461 
SetMarginsForImportMultipageTextFile()4462 void SetMarginsForImportMultipageTextFile()
4463 {
4464    char buf[MAXSTRING<<1], spec[MAXSTRING+1], spec_copy[MAXSTRING+1];
4465 
4466    GetCurMarginsForImportTextFile(gszMsgBox);
4467    sprintf(buf, TgLoadString(STID_CUR_MARGINS_ARE_GIVEN), gszMsgBox);
4468    *spec = '\0';
4469    switch (gridSystem) {
4470    case ENGLISH_GRID:
4471       sprintf(gszMsgBox, TgLoadString(STID_ENTER_MARGINS),
4472             "0.5in,0.5in,0.5in,0.5in");
4473       break;
4474    case METRIC_GRID:
4475       sprintf(gszMsgBox, TgLoadString(STID_ENTER_MARGINS),
4476             "1cm,1cm,1cm,1cm");
4477       break;
4478    }
4479    if (Dialog(gszMsgBox, buf, spec) == INVALID) {
4480       return;
4481    }
4482    UtilStrCpyN(spec_copy, sizeof(spec_copy), spec);
4483 
4484    if (!DoSetMarginsForImportMultipageTextFile(spec_copy)) {
4485       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_4_VAL), spec);
4486       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4487    } else {
4488       char buf[MAXSTRING<<1];
4489 
4490       strcpy(buf, "    ");
4491       GetCurMarginsForImportTextFile(&buf[4]);
4492       TwoLineMsg(TgLoadString(STID_NEW_MARGINS_ARE_GIVEN), buf);
4493    }
4494 }
4495 
ToggleWordWrapDuringImportMultipageTextFile()4496 void ToggleWordWrapDuringImportMultipageTextFile()
4497 {
4498    wordWrapDuringImportTextFile = !wordWrapDuringImportTextFile;
4499    sprintf(gszMsgBox, TgLoadString(wordWrapDuringImportTextFile ?
4500          STID_WILL_USE_WORDWRAP_IMPORT_TEXT : STID_NO_WORDWRAP_IMPORT_TEXT),
4501          TOOL_NAME);
4502    Msg(gszMsgBox);
4503 }
4504 
RefreshImportMutipageTextFileMenu(menu)4505 int RefreshImportMutipageTextFileMenu(menu)
4506    TgMenu *menu;
4507 {
4508    int ok=TRUE;
4509 
4510    /* Word Wrap */
4511    ok &= TgSetMenuItemCheckById(menu, CMDID_TOGGLEWORDWRAPONIMPORTTEXT,
4512          wordWrapDuringImportTextFile);
4513 
4514    /*
4515     * Well, don't know how to do all these yet.  Just disable them for now.
4516     */
4517    ok &= TgEnableMenuItemById(menu, CMDID_IMPORTMULTIPAGETEXTFILE, FALSE);
4518    ok &= TgEnableMenuItemById(menu, CMDID_SETMARGINSONIMPORTTEXT, FALSE);
4519    ok &= TgEnableMenuItemById(menu, CMDID_TOGGLEWORDWRAPONIMPORTTEXT, FALSE);
4520 
4521    return ok;
4522 }
4523 
CreateImportMutipageTextFileMenu(parent_menu,x,y,menu_info,status_str_xlated)4524 TgMenu *CreateImportMutipageTextFileMenu(parent_menu, x, y, menu_info,
4525       status_str_xlated)
4526    TgMenu *parent_menu;
4527    int x, y;
4528    TgMenuInfo *menu_info;
4529    int status_str_xlated; /* ignored, always 0 */
4530 {
4531    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
4532 
4533    if (menu != NULL) {
4534       if (!RefreshImportMutipageTextFileMenu(menu)) {
4535          return TgDestroyMenu(menu, TRUE);
4536       }
4537       menu->refresh_proc =
4538             ((RefreshMenuFunc*)RefreshImportMutipageTextFileMenu);
4539    }
4540    return menu;
4541 }
4542 
4543 /* ----------------------- Init and CleanUp ----------------------- */
4544 
4545 static
CleanUpCutBufferInfo()4546 void CleanUpCutBufferInfo()
4547 {
4548    ResetCutBufferInfo();
4549 }
4550 
CleanUpCutPaste()4551 void CleanUpCutPaste()
4552 {
4553    cutBufferIsTgifObj = FALSE;
4554    cutBufferIsUTF8 = FALSE;
4555    FreeDynStrBuf(&dsCutBuffer);
4556    CleanUpCutBufferInfo();
4557 }
4558 
4559 static
ResetXCutBuffers()4560 void ResetXCutBuffers()
4561 {
4562    int i=0;
4563 
4564    for (i=0; gaCutBufferAtom[i] != (Atom)0; i++) {
4565       XChangeProperty(mainDisplay, rootWindow, gaCutBufferAtom[i], XA_STRING,
4566             8, PropModeAppend, (unsigned char *)"", 0);
4567    }
4568 }
4569 
4570 static
InitCutBufferInfo()4571 void InitCutBufferInfo()
4572 {
4573    memset(&gSetCutBufferInfo, 0, sizeof(SetCutBufferInfo));
4574 }
4575 
InitCutPaste()4576 int InitCutPaste()
4577 {
4578    char *c_ptr=NULL;
4579    int ival=0;
4580 
4581    InitCutBufferInfo();
4582 
4583    pasteFromXSelectionOnly = TRUE;
4584    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"PasteFromXSelectionOnly")) !=
4585          NULL && UtilStrICmp(c_ptr, "false") == 0) {
4586       pasteFromXSelectionOnly = FALSE;
4587    }
4588    pasteFromSelectionTimeout = 10;
4589    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"PasteFromSelectionTimeout")) !=
4590          NULL) {
4591       if (sscanf(c_ptr, "%d", &ival) != 1 || ival < 0) {
4592          fprintf(stderr, TgLoadString(STID_INVALID_XDEF), TOOL_NAME,
4593                "PasteFromSelectionTimeout", c_ptr);
4594          fprintf(stderr, "\n");
4595       } else {
4596          pasteFromSelectionTimeout = ival;
4597       }
4598    }
4599    debugCopyPaste = FALSE;
4600    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"DebugCopyPaste")) != NULL &&
4601          UtilStrICmp(c_ptr, "true") == 0) {
4602       debugCopyPaste = TRUE;
4603    }
4604    memset(&dsCutBuffer, 0, sizeof(dsCutBuffer));
4605    ResetXCutBuffers();
4606 
4607    return TRUE;
4608 }
4609