2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 #ifdef VERBOSE_REV_INFO
25 static char rcs_id[] = "$XConsortium: TermPrimBufferWc.c /main/1 1996/04/21 19:16:59 drk $";
26 #endif /* VERBOSE_REV_INFO */
30 * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company *
31 * (c) Copyright 1993, 1994, 1996 International Business Machines Corp. *
32 * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc. *
33 * (c) Copyright 1993, 1994, 1996 Novell, Inc. *
34 * (c) Copyright 1996 Digital Equipment Corporation. *
35 * (c) Copyright 1996 FUJITSU LIMITED. *
36 * (c) Copyright 1996 Hitachi. *
39 #define USE_MEMCPY /* use memcpy for line movement... */
44 #include "TermHeader.h" /* for MIN/MAX */
46 #include "TermPrimOSDepI.h"
47 #include "TermPrimBufferP.h"
48 #include "TermPrimDebug.h"
51 _termBufferValidateLineWc
86 short *widthInsert, /* width of inserted characters */
87 termChar *returnChars, /* pointer to overflow buffer */
88 short *returnCount /* count of characters in overflow buffer */
92 _primBufferOverwriteWc
101 short *widthInsert, /* width of inserted characters */
102 termChar *returnChars, /* pointer to overflow buffer */
103 short *returnCount /* count of characters in overflow buffer */
106 #ifdef USE_SUN_WCWIDTH_PATCH
109 ** A small workaround for systems that don't have wcwidth...
118 char mbstr[MB_LEN_MAX + 1];
120 status = wctomb(mbstr, wc);
123 return(euccol(mbstr));
127 #endif /* USE_SUN_WCWIDTH_PATCH */
130 _DtTermPrimBufferGetTextWc
137 const Boolean needWideChar
142 if (!VALID_ROW(tb, row) || !VALID_COL(tb, col))
147 len = MIN(length, LENGTH(LINES(tb)[row]) - col);
151 memcpy(buffer, BUFFER(LINES(tb)[row]) + col, len);
157 ** this is a helper function, that replaces a 2 column character with
158 ** two spaces if "col" falls on column 2 of 2...
166 TermCharInfo charInfo
171 line = LINE_OF_TBUF(tb, row);
174 ** make sure we are not trying to overwrite the second column
175 ** of a two column character.
177 _DtTermPrimGetCharacterInfo(tb, row, *col, charInfo);
178 if ((charInfo->width == 2) &&
179 (charInfo->startCol == *col - 1))
182 ** we are on the second column of a two column character,
183 ** replace the character with 2 spaces before we proceed
185 ** first replace the character with a space, then slide the
186 ** characters one to the right and bump the LENGTH
187 ** (the width remains constant)
189 *charInfo->u.pwc = L' ';
190 memmove(charInfo->u.pwc + 1, charInfo->u.pwc,
191 (LENGTH(line) - charInfo->idx) * sizeof(wchar_t));
195 ** remember the old starting column for later...
200 ** now update the charInfo (since we replaced the 2 column
201 ** character with 2 single column spaces)...
203 charInfo->startCol++;
211 ** determine how many wide characters in the buffer we can have before the
212 ** maxWidth is exceeded...
217 wchar_t *wcBuffer, /* buffer of wide characters */
218 short wcLen, /* number of wide chars in buffer */
219 short maxWidth, /* maximum width desired */
220 short *length, /* max num of chars with total width <= maxWidth */
221 short *width /* actual width of the retCount wide chars */
228 for (pwc = wcBuffer; pwc < wcBuffer + wcLen; pwc++)
230 switch (charWidth = wcwidth(*pwc))
234 ** invalid character, (but this should have been handled earlier)
236 /* replace the character with a space... */
238 /* and set the width to 1... */
243 ** its a null character, but is still has a width for our
251 if (*width + charWidth > maxWidth)
260 *length = pwc - wcBuffer;
264 ** This is a helper function that inserts characters into the specified
265 ** buffer in insert mode.
277 short *widthInsert, /* width of inserted characters */
278 termChar *returnChars, /* pointer to overflow buffer */
279 short *returnLength /* count of characters in overflow buffer */
284 short insertOverflow; /* # of newChars that would overflow */
285 short overflowLength;
290 TermCharInfoRec charInfo;
293 ** make a copy of *col because it may be modified by
299 ** first decide how many characters we can insert before
300 ** running off the end of the line...
302 _countWidth(newChars, numChars, COLS(tb) - localCol, &lengthInsert,
306 ** return any extra characters...
308 *returnLength = numChars - lengthInsert;
310 if (*returnLength > 0)
313 ** we have some overflow...
315 memcpy(returnChars, newChars + lengthInsert,
316 *returnLength * sizeof(wchar_t));
320 ** make sure we are not trying to overwrite the second column
321 ** of a two column character.
323 _patchUpChar(tb, row, col, &charInfo);
326 ** Decide how many characters we can insert before running off the
327 ** end of the buffer...
329 line = LINE_OF_TBUF(tb, row);
330 if (WIDTH(line) + *widthInsert <= COLS(tb))
333 ** there is no overflow, we can insert all "lengthInsert" characters...
335 *widthInc = *widthInsert;
336 *lengthInc = lengthInsert;
341 overflowWidth = WIDTH(line) + *widthInsert - COLS(tb);
344 ** inserting the new characters will overflow the line buffer,
345 ** remove as many of the current characters on the line
346 ** as necessary to prevent buffer overflow
348 if (overflowWidth > 0)
350 *widthInc = *widthInsert - overflowWidth;
352 for (pwc = ((wchar_t *)BUFFER(line)) + LENGTH(line) - 1;
353 pwc >= charInfo.u.pwc; pwc--)
355 overflowWidth -= MAX(1, wcwidth(*pwc));
357 if (overflowWidth <= 0)
360 ** we've removed enough characters, pwc points to the
361 ** first character to remove...
368 ** final adjustment to widthInc (at this point overflowWidth
369 ** is either 0 (we removed exactly "overflowWidth" worth of
370 ** characters) or -1 (we removed "overflowWidth + 1" worth of
371 ** characters because we removed some 2 column characters)
373 *widthInc += overflowWidth;
376 overflowLength = ((wchar_t *)BUFFER(line)) + LENGTH(line) - pwc;
378 if (overflowLength > 0)
380 *lengthInc = lengthInsert - overflowLength;
383 ** copy the displaced characters from the line to the
384 ** overflow buffer...
386 memcpy(returnChars + *returnLength, pwc,
387 overflowLength * sizeof(wchar_t));
388 *returnLength += overflowLength;
393 ** Any overflow has been taken care of, now it's time to make
394 ** room for the new characters...
397 ** charInfo.pchar points to the character at col
399 memmove(charInfo.u.pwc + lengthInsert, charInfo.u.pwc,
400 (((LENGTH(line) - charInfo.idx) - overflowLength) *
404 ** copy the new characters into the buffer
406 memcpy(charInfo.u.pwc, newChars, lengthInsert * sizeof(wchar_t));
410 ** This is a helper function that inserts characters into the specified
411 ** buffer in overwrite mode.
414 _primBufferOverwriteWc
423 short *widthInsert, /* width of inserted characters */
424 termChar *returnChars, /* pointer to overflow buffer */
425 short *returnLength /* count of characters in overflow buffer */
429 short insertOverflow; /* # of newChars that would overflow */
434 TermCharInfoRec charInfo;
435 TermCharInfoRec startCharInfo;
437 line = LINE_OF_TBUF(tb, row);
440 ** make a copy of *col because it may be modified by
446 ** first decide how many characters we can overwrite before
447 ** running off the end of the line.
449 _countWidth(newChars, numChars, COLS(tb) - localCol, &lengthInsert,
453 ** We are overwriting:
454 ** - determine the length and width increments
455 ** - put any extra new characters into the overflow buffer
457 if (localCol == WIDTH(line))
460 ** we are appending to the end of the line, this is easy...
462 *widthInc = *widthInsert;
463 *lengthInc = lengthInsert;
464 pStart = (char *)BUFFER(line) + (LENGTH(line) * sizeof(wchar_t));
469 ** we are overwriting characters in the middle of the line...
471 ** make sure we are not trying to overwrite the second column
472 ** of a two column character.
474 _patchUpChar(tb, row, col, &startCharInfo);
477 ** see if we have to deal with the end of the line...
479 if (localCol + *widthInsert < WIDTH(line))
482 ** the line width will remain constant, but the length may
488 ** make sure we are not trying to overwrite the first column
489 ** of a two column character.
491 _DtTermPrimGetCharacterInfo(tb, row, localCol + *widthInsert,
493 if ((charInfo.width == 2) &&
494 (charInfo.startCol == localCol + *widthInsert - 1))
497 ** We are about to overwrite column 1 of a 2 column
498 ** character. Replace it with a space before we proceed
499 ** (we make adjustments later to make it look like we
500 ** replaced the second column with a space).
502 *charInfo.u.pwc = L' ';
505 ** now update the charInfo (since we replaced the 2 column
506 ** character with a single column space)...
512 ** at this point, startCharInfo points to the first
513 ** character to replace, now we want charInfo.u.pwc
514 ** to point to the character one past the last one
515 ** we want to replace
517 *lengthInc = lengthInsert - (charInfo.u.pwc - startCharInfo.u.pwc);
521 memmove(charInfo.u.pwc + *lengthInc, charInfo.u.pwc,
522 (LENGTH(line) - charInfo.idx) * sizeof(wchar_t));
528 ** the line may get wider and longer...
530 *widthInc = localCol + *widthInsert - WIDTH(line);
531 *lengthInc = startCharInfo.idx + lengthInsert - LENGTH(line);
533 pStart = startCharInfo.u.pc;
537 ** now insert the new characters...
539 memcpy((void *)pStart, newChars, lengthInsert * sizeof(wchar_t));
542 ** put any overflow into the overflow buffer
544 *returnLength = numChars - lengthInsert;
545 if (*returnLength > 0)
547 memcpy(returnChars, newChars + lengthInsert,
548 *returnLength * sizeof(wchar_t));
553 ** Insert as many characters as possible at the specified row,col
554 ** return a count of the number of characters bumped off the end of the line
555 ** in 'returnLength' and a pointer to a buffer containing those characters
558 ** The the new column width of the line is returned as the value of the
562 ** We are trying to implement mechanism and not policy. This
563 ** routine does a minimum of checking for boundary conditions.
566 _DtTermPrimBufferInsertWc
573 Boolean insertFlag, /* if TRUE, insert, else overwrite */
574 termChar **returnChars, /* pointer to overflow buffer */
575 short *returnLength /* count of characters in overflow buffer */
578 short widthInc; /* incremental change in line width */
579 short lengthInc; /* incremental change in line length */
580 short widthInsert; /* column width of chars inserted */
584 if (!VALID_ROW(tb, row) || !VALID_COL(tb, col))
590 if (isDebugFSet('i', 1)) {
594 (void) _termBufferValidateLineWc(tb, row);
597 line = LINE_OF_TBUF(tb, row);
600 if (WIDTH(line) < col)
603 ** We're adding characters past the current end of line,
606 _DtTermPrimBufferPadLineWc(tb, row, col);
610 ** It doesn't matter if we're overwriting, or inserting at the end
611 ** of the line, the result is the same...
613 if (insertFlag == False || col == WIDTH(line))
615 _primBufferOverwriteWc(tb, row, &localCol, newChars, numChars,
616 &lengthInc, &widthInc, &widthInsert,
617 *returnChars, returnLength);
621 _primBufferInsertWc(tb, row, &localCol, newChars, numChars,
622 &lengthInc, &widthInc, &widthInsert,
623 *returnChars, returnLength);
627 ** Everything's ready:
628 ** - put the characters into the line
629 ** - adjust the line width (_DtTermPrimBufferSetLineWidth won't
630 ** let the line get shorter)...
631 ** - adjust the line length
632 ** - update the enhancements
634 WIDTH(line) += widthInc;
635 LENGTH(line) += lengthInc;
638 ** fix up the enhancments...
642 (*INSERT_ENH(tb))(tb, row, col, widthInsert, insertFlag);
645 if (isDebugFSet('i', 1)) {
649 (void) _termBufferValidateLineWc(tb, row);
656 _DtTermPrimBufferDeleteWc
662 termChar **returnChars, /* pointer to delete buffer */
663 short *returnCount /* count of bytes in delete buffer */
670 TermCharInfoRec startCharInfo;
671 TermCharInfoRec stopCharInfo;
673 if (!VALID_ROW(tb, *row) || !VALID_COL(tb, *col))
684 ** save some local copies, to originals may get modified
688 line = LINE_OF_TBUF(tb, *row);
689 *width = MAX(0, MIN(WIDTH(line) - localCol, *width));
692 ** there are 3 cases of deleting a character from a line:
694 ** the cursor is at least 2 positions past the end of the
695 ** line (col - WIDTH(line) > 0)
698 ** the cursor is in the middle of the line (copyCount > 0)
699 ** - move the remaining characters to the left
700 ** - deleteEnhancement
701 ** - adjust WIDTH and LENGTH
704 ** the cursor is at the end of the line (copyCount == 0 and
705 ** col == WIDTH(line))
706 ** - deleteEnhancement
707 ** - adjust WIDTH and LENGTH
709 if (localCol >= WIDTH(line) || *width == 0)
723 ** make any necessary adjustments to 2 column characters...
725 _patchUpChar(tb, *row, col, &startCharInfo);
727 (void) _DtTermPrimGetCharacterInfo(tb, localRow,
728 MIN(WIDTH(line), localCol + *width), &stopCharInfo);
730 if ((stopCharInfo.width == 2) &&
731 (stopCharInfo.startCol == localCol + *width - 1))
734 ** do not try to delete column 1 of 2...
736 ** replace the 2 column character with two one column spaces...
738 *stopCharInfo.u.pwc = L' ';
739 memmove(stopCharInfo.u.pwc + 1, stopCharInfo.u.pwc,
740 (LENGTH(line) - stopCharInfo.idx) * sizeof(wchar_t));
743 ** now make stopCharInfo point at the new space
745 stopCharInfo.width = 1;
746 stopCharInfo.startCol++;
747 stopCharInfo.u.pwc++;
753 ** Save the current characters before we overwrite them
754 ** (if returnChars is non-NULL 0)
756 if (returnChars != NULL)
758 *returnCount = (stopCharInfo.idx - startCharInfo.idx) * sizeof(wchar_t);
759 *returnChars = (termChar *)XtMalloc(*returnCount);
760 memmove(*returnChars, startCharInfo.u.pwc, *returnCount);
764 ** Cases 2, 3, and 4 require that we delete the enhancement...
768 (*DELETE_ENH(tb))(tb, localRow, localCol, *width);
771 copyCount = MAX(0, LENGTH(line) - stopCharInfo.idx);
777 memmove(startCharInfo.u.pwc, stopCharInfo.u.pwc,
778 copyCount * sizeof(wchar_t));
782 ** Cases 2 and 3 require that we decrement the line length...
784 WIDTH(line) -= *width;
785 LENGTH(line) -= (stopCharInfo.idx - startCharInfo.idx);
788 ** update info we need to return...
790 *width = stopCharInfo.startCol - startCharInfo.startCol;
794 ** Pad the requested row from the current width to 'newWidth' with spaces...
797 _DtTermPrimBufferPadLineWc
809 line = LINE_OF_TBUF(tb, row);
811 if (isDebugFSet('i', 1)) {
815 (void) _termBufferValidateLineWc(tb, row);
819 ** if this line is part of the selection, disown the selection...
821 if (IS_IN_SELECTION(line, MIN(width, WIDTH(line)),
822 MAX(width, WIDTH(line))))
824 (void) _DtTermPrimSelectDisown(WIDGET(tb));
827 widthInc = MIN(COLS(tb), width) - WIDTH(line);
829 for (i = 0, pwc = (wchar_t *)BUFFER(line) + MAX(0, LENGTH(line));
838 (*CLEAR_ENH(tb))(tb, row, WIDTH(line), widthInc);
840 _DtTermPrimBufferSetLineWidth(tb, row, WIDTH(line) + widthInc);
841 if (isDebugFSet('i', 1)) {
845 _termBufferValidateLineWc(tb, row);
850 ** Clear the line to the new width (just reset the line width).
853 _DtTermPrimBufferClearLineWc
861 TermCharInfoRec charInfo;
865 ** Some simple bounds checking.
867 if (!VALID_ROW(tb, row))
873 ** force the width to the desired value
875 ** (We take the direct approach because _DtTermPrimBufferSetLineWidth
876 ** doesn't allow the line width to decrease.)
878 line = LINE_OF_TBUF(tb, row);
881 ** if this line is part of the selection, disown the selection...
883 if (IS_IN_SELECTION(line, MIN(newWidth, WIDTH(line)),
884 MAX(newWidth, WIDTH(line))))
886 (void) _DtTermPrimSelectDisown(WIDGET(tb));
890 ** Clip the new width to the buffer width.
892 newWidth = MIN(newWidth, COLS(tb));
894 if (newWidth < WIDTH(line))
903 ** handle the case of clearing the second column of a two column
906 _DtTermPrimGetCharacterInfo(tb, row, MAX(0, newWidth - 1),
909 if ((charInfo.width == 2 ) &&
910 (charInfo.startCol == MAX(0, newWidth - 1)))
913 ** we are clearing column 2 of 2, replace column 1 of 1 with
916 *charInfo.u.pwc = L' ';
918 newLength = charInfo.idx + 1;
921 ** Call the helper function if it exists
925 (*CLEAR_LINE(tb))(tb, row, newWidth);
927 WRAPPED(line) = False;
928 WIDTH(line) = newWidth;
929 LENGTH(line) = newLength;
935 ** replace all characters from startCol upto stopCol with spaces,
938 ** we are dealing with double width characters, the width needs
939 ** to remain constant, but the length may have to change since we
940 ** may replace a double width character with a single width
944 _DtTermPrimBufferEraseWc
952 TermCharInfoRec startCharInfo;
953 TermCharInfoRec stopCharInfo;
961 ** make sure we are not trying to erase the second column
962 ** of a two column character.
965 _patchUpChar(tb, row, &localCol, &startCharInfo);
968 ** make sure we are not trying to erase the first column
969 ** of a two column character.
971 _DtTermPrimGetCharacterInfo(tb, row, stopCol, &stopCharInfo);
973 if ((stopCharInfo.width == 2) &&
974 (stopCharInfo.startCol == stopCol))
978 ** We are about to overwrite column 1 of a 2 column
979 ** character. Replace it with a space before we proceed
980 ** (we make adjustments later to make it look like we
981 ** replaced the second column with a space).
983 *stopCharInfo.u.pwc = L' ';
986 ** now update the charInfo (since we replaced the 2 column
987 ** character with a single column space)...
989 stopCharInfo.width = 1;
995 ** at this point, startCharInfo points to the first character
996 ** to erase, and stopCharInfo points to the last character
997 ** we want to erase, make sure there is enough space between
998 ** the two to accommodate the replacement spaces...
1000 lengthErase = stopCol - startCol + 1;
1001 lengthInc = lengthErase - (stopCharInfo.u.pwc - startCharInfo.u.pwc + 1);
1006 ** the length will have to change, make the necessary adjustments
1008 memmove(stopCharInfo.u.pwc + lengthInc, stopCharInfo.u.pwc,
1009 (LENGTH(LINE_OF_TBUF(tb, row)) - stopCharInfo.idx) *
1011 LENGTH(LINE_OF_TBUF(tb, row)) += lengthInc;
1015 ** replace the characters with spaces...
1017 for (pwchar = startCharInfo.u.pwc;
1018 pwchar < startCharInfo.u.pwc + lengthErase;
1029 _termBufferValidateLineWc
1031 const TermBuffer tb,
1038 line = LINE_OF_TBUF(tb, row);
1039 for (pwc = (wchar_t *)BUFFER(line);
1040 pwc < (wchar_t *)BUFFER(line) + LENGTH(line);
1043 if (wcwidth(*pwc) == -1)
1045 fprintf(stderr, "_termBufferValidateLineWc: invalid wide char\n");
1046 /* replace the character with a space... */
1053 #if (defined(TEST) || defined(__CODECENTER__) || defined(DEBUG))
1055 _termBufferPrintLine
1057 const TermBuffer tb,
1065 printf("Line: %d\n", row);
1067 line = LINE_OF_TBUF(tb, row);
1068 printf(" length: %3d\n", WIDTH(line));
1069 if (WIDTH(line) > 0)
1071 printf(" buffer: <");
1072 pChar = BUFFER(line);
1073 for (j = 0; j < WIDTH(line); j++)
1075 printf("%X", *pChar++);
1082 ** Print the contents of the TermBuffer.
1085 _termBufferPrintBuffer
1095 printf("TermBuffer has been freed.\n");
1099 printf("TermBuffer dimensions:\n");
1100 printf(" rows: %d\n", ROWS(tb));
1101 printf(" cols: %d\n", COLS(tb));
1103 for (i = 0; i < ROWS(tb); i++)
1105 _termBufferPrintLine(tb, i);
1108 #endif /* (defined(TEST) || defined(__CODECENTER__)) */
1112 ** Some simple tests of the termBuffer.
1114 /* the following is to allow for a single main function in the code... */
1115 #define termBufMain main
1118 const TermBuffer myTB;
1120 printf("Sizeof termEnhRec : %d\n", sizeof(struct _termEnhRec));
1121 printf("Sizeof termBufferRec : %d\n", sizeof(struct _TermBufferRec));
1123 myTB = _DtTermPrimBufferCreateBuffer(12, 80);
1124 _termBufferPrintBuffer(myTB);
1126 printf("[0,0] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 0, enhVideo, BLINK));
1127 _termBufferPrintEnhancement(myTB, 0, 0);
1128 printf("[0,1] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 1, enhVideo, INVERSE));
1129 _termBufferPrintEnhancement(myTB, 0, 0);
1130 _termBufferPrintEnhancement(myTB, 0, 1);
1131 printf("[0,9] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 9, enhVideo, UNDERLINE));
1132 _termBufferPrintEnhancement(myTB, 0, 0);
1133 _termBufferPrintEnhancement(myTB, 0, 1);
1134 _termBufferPrintEnhancement(myTB, 0, 9);
1135 printf("[0,6] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 6, enhVideo, HALF_BRIGHT));
1136 _termBufferPrintEnhancement(myTB, 0, 0);
1137 _termBufferPrintEnhancement(myTB, 0, 1);
1138 _termBufferPrintEnhancement(myTB, 0, 6);
1139 _termBufferPrintEnhancement(myTB, 0, 9);
1140 _termBufferPrintBuffer(myTB);
1142 _DtTermPrimBufferSetEnhancement(myTB, 10, 10, enhVideo, BLINK);
1143 _DtTermPrimBufferSetEnhancement(myTB, 10, 20, enhColor, 3);
1144 _termBufferPrintBuffer(myTB);
1146 _DtTermPrimBufferResizeBuffer(&myTB, 6, 40);
1147 _termBufferPrintBuffer(myTB);
1149 _DtTermPrimBufferSetEnhancement(myTB, 10, 10, enhVideo, BLINK);
1150 _DtTermPrimBufferResizeBuffer(&myTB, 12, 80);
1151 _termBufferPrintBuffer(myTB);
1153 _DtTermPrimBufferFreeBuffer(myTB);