source: trunk/kitgen/8.x/blt/generic/bltTreeViewEdit.c@ 199

Last change on this file since 199 was 175, checked in by demin, 12 years ago

initial commit

File size: 49.0 KB
Line 
1/*
2 Remember row,column where string was acquired.
3 postcombobox x y
4 icon?, text, row, column position, fg, bg button?
5 based upon style.
6 grab set
7 SetIcon
8 SetText
9 SetBg
10 SetFg
11 SetFont
12 SetButton
13 */
14
15/*
16 * bltTreeViewEdit.c --
17 *
18 * This module implements an hierarchy widget for the BLT toolkit.
19 *
20 * Copyright 1998-1999 Lucent Technologies, Inc.
21 *
22 * Permission to use, copy, modify, and distribute this software and
23 * its documentation for any purpose and without fee is hereby
24 * granted, provided that the above copyright notice appear in all
25 * copies and that both that the copyright notice and warranty
26 * disclaimer appear in supporting documentation, and that the names
27 * of Lucent Technologies or any of their entities not be used in
28 * advertising or publicity pertaining to distribution of the software
29 * without specific, written prior permission.
30 *
31 * Lucent Technologies disclaims all warranties with regard to this
32 * software, including all implied warranties of merchantability and
33 * fitness. In no event shall Lucent Technologies be liable for any
34 * special, indirect or consequential damages or any damages
35 * whatsoever resulting from loss of use, data or profits, whether in
36 * an action of contract, negligence or other tortuous action, arising
37 * out of or in connection with the use or performance of this
38 * software.
39 *
40 * The "treeview" widget was created by George A. Howlett.
41 */
42
43#include "bltInt.h"
44
45#ifndef NO_TREEVIEW
46
47#include "bltTreeView.h"
48#include <X11/Xutil.h>
49#include <X11/Xatom.h>
50
51#define TEXTBOX_FOCUS (1<<0)
52#define TEXTBOX_REDRAW (1<<1)
53
54static Tcl_IdleProc DisplayTextbox;
55static Tcl_FreeProc DestroyTextbox;
56static Tcl_TimerProc BlinkCursorProc;
57static Tcl_ObjCmdProc TextboxCmd;
58
59/*
60 * Textbox --
61 *
62 * This structure is shared by entries when their labels are
63 * edited via the keyboard. It maintains the location of the
64 * insertion cursor and the text selection for the editted entry.
65 * The structure is shared since we need only one. The "focus"
66 * entry should be the only entry receiving KeyPress/KeyRelease
67 * events at any time. Information from the previously editted
68 * entry is overwritten.
69 *
70 * Note that all the indices internally are in terms of bytes,
71 * not characters. This is because UTF-8 strings may encode a
72 * single character into a multi-byte sequence. To find the
73 * respective character position
74 *
75 * n = Tcl_NumUtfChars(string, index);
76 *
77 * where n is the resulting character number.
78 */
79typedef struct {
80
81 /*
82 * This is a SNAFU in the Tk API. It assumes that only an official
83 * Tk "toplevel" widget will ever become a toplevel window (i.e. a
84 * window whose parent is the root window). Because under Win32,
85 * Tk tries to use the widget record associated with the TopLevel
86 * as a Tk frame widget, to read its menu name. What this means
87 * is that any widget that's going to be a toplevel, must also look
88 * like a frame. Therefore we've copied the frame widget structure
89 * fields into the token.
90 */
91
92 Tk_Window tkwin; /* Window that embodies the frame. NULL
93 * means that the window has been destroyed
94 * but the data structures haven't yet been
95 * cleaned up. */
96 Display *display; /* Display containing widget. Used, among
97 * other things, so that resources can be
98 * freed even after tkwin has gone away. */
99 Tcl_Interp *interp; /* Interpreter associated with widget. Used
100 * to delete widget command. */
101 Tcl_Command widgetCmd; /* Token for frame's widget command. */
102 char *className; /* Class name for widget (from configuration
103 * option). Malloc-ed. */
104 int mask; /* Either FRAME or TOPLEVEL; used to select
105 * which configuration options are valid for
106 * widget. */
107 char *screenName; /* Screen on which widget is created. Non-null
108 * only for top-levels. Malloc-ed, may be
109 * NULL. */
110 char *visualName; /* Textual description of visual for window,
111 * from -visual option. Malloc-ed, may be
112 * NULL. */
113 char *colormapName; /* Textual description of colormap for window,
114 * from -colormap option. Malloc-ed, may be
115 * NULL. */
116 char *menuName; /* Textual description of menu to use for
117 * menubar. Malloc-ed, may be NULL. */
118 Colormap colormap; /* If not None, identifies a colormap
119 * allocated for this window, which must be
120 * freed when the window is deleted. */
121 Tk_3DBorder border; /* Structure used to draw 3-D border and
122 * background. NULL means no background
123 * or border. */
124 int borderWidth; /* Width of 3-D border (if any). */
125 int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */
126 int highlightWidth; /* Width in pixels of highlight to draw
127 * around widget when it has the focus.
128 * 0 means don't draw a highlight. */
129 XColor *highlightBgColorPtr;
130 /* Color for drawing traversal highlight
131 * area when highlight is off. */
132 XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
133 int width; /* Width to request for window. <= 0 means
134 * don't request any size. */
135 int height; /* Height to request for window. <= 0 means
136 * don't request any size. */
137 Tk_Cursor cursor; /* Current cursor for window, or None. */
138 char *takeFocus; /* Value of -takefocus option; not used in
139 * the C code, but used by keyboard traversal
140 * scripts. Malloc'ed, but may be NULL. */
141 int isContainer; /* 1 means this window is a container, 0 means
142 * that it isn't. */
143 char *useThis; /* If the window is embedded, this points to
144 * the name of the window in which it is
145 * embedded (malloc'ed). For non-embedded
146 * windows this is NULL. */
147 int flags; /* Various flags; see below for
148 * definitions. */
149
150 /* Textbox-specific fields */
151 TreeView *tvPtr;
152 int x, y; /* Position of window. */
153
154 int active; /* Indicates that the frame is active. */
155 int exportSelection;
156
157 int insertPos; /* Position of the cursor within the
158 * array of bytes of the entry's label. */
159
160 int cursorX, cursorY; /* Position of the insertion cursor in the
161 * textbox window. */
162 short int cursorWidth; /* Size of the insertion cursor symbol. */
163 short int cursorHeight;
164
165 int selAnchor; /* Fixed end of selection. Used to extend
166 * the selection while maintaining the
167 * other end of the selection. */
168 int selFirst; /* Position of the first character in the
169 * selection. */
170 int selLast; /* Position of the last character in the
171 * selection. */
172
173 int cursorOn; /* Indicates if the cursor is displayed. */
174 int onTime, offTime; /* Time in milliseconds to wait before
175 * changing the cursor from off-to-on
176 * and on-to-off. Setting offTime to 0 makes
177 * the cursor steady. */
178 Tcl_TimerToken timerToken; /* Handle for a timer event called periodically
179 * to blink the cursor. */
180 /* Data-specific fields. */
181 TreeViewEntry *entryPtr; /* Selected entry */
182 TreeViewColumn *columnPtr; /* Column of entry to be edited */
183 TreeViewStyle *stylePtr;
184 TreeViewIcon icon;
185 int gap;
186 char *string;
187 TextLayout *textPtr;
188 Tk_Font font;
189 GC gc;
190
191 Tk_3DBorder selBorder;
192 int selRelief;
193 int selBorderWidth;
194 XColor *selFgColor; /* Text color of a selected entry. */
195 int buttonBorderWidth;
196 Tk_3DBorder buttonBorder;
197 int buttonRelief;
198} Textbox;
199
200#define DEF_TEXTBOX_BACKGROUND RGB_WHITE
201#define DEF_TEXTBOX_BORDERWIDTH STD_BORDERWIDTH
202#ifdef WIN32
203#define DEF_TEXTBOX_BUTTON_BACKGROUND RGB_GREY85
204#else
205#define DEF_TEXTBOX_BUTTON_BACKGROUND RGB_GREY90
206#endif
207#define DEF_TEXTBOX_BUTTON_BORDERWIDTH "2"
208#define DEF_TEXTBOX_BUTTON_RELIEF "raised"
209#define DEF_TEXTBOX_CURSOR (char *)NULL
210#define DEF_TEXTBOX_EXPORT_SELECTION "no"
211#define DEF_TEXTBOX_NORMAL_BACKGROUND STD_NORMAL_BACKGROUND
212#define DEF_TEXTBOX_NORMAL_FG_MONO STD_ACTIVE_FG_MONO
213#define DEF_TEXTBOX_RELIEF "sunken"
214#define DEF_TEXTBOX_SELECT_BACKGROUND RGB_LIGHTBLUE0
215#define DEF_TEXTBOX_SELECT_BG_MONO STD_SELECT_BG_MONO
216#define DEF_TEXTBOX_SELECT_BORDERWIDTH "1"
217#define DEF_TEXTBOX_SELECT_FOREGROUND STD_SELECT_FOREGROUND
218#define DEF_TEXTBOX_SELECT_FG_MONO STD_SELECT_FG_MONO
219#define DEF_TEXTBOX_SELECT_RELIEF "flat"
220
221/* Textbox Procedures */
222static Blt_ConfigSpec textboxConfigSpecs[] =
223{
224 {BLT_CONFIG_BORDER, "-background", "background", "Background",
225 DEF_TEXTBOX_BACKGROUND, Blt_Offset(Textbox, border), 0},
226 {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,0},
227 {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0,0},
228 {BLT_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
229 DEF_TEXTBOX_CURSOR, Blt_Offset(Textbox, cursor),
230 BLT_CONFIG_NULL_OK},
231 {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
232 DEF_TEXTBOX_BORDERWIDTH, Blt_Offset(Textbox, borderWidth),
233 BLT_CONFIG_DONT_SET_DEFAULT},
234 {BLT_CONFIG_BORDER, "-buttonbackground", "buttonBackground",
235 "ButtonBackground", DEF_TEXTBOX_BUTTON_BACKGROUND,
236 Blt_Offset(Textbox, buttonBorder), 0},
237 {BLT_CONFIG_RELIEF, "-buttonrelief", "buttonRelief", "ButtonRelief",
238 DEF_TEXTBOX_BUTTON_RELIEF, Blt_Offset(Textbox, buttonRelief),
239 BLT_CONFIG_DONT_SET_DEFAULT},
240 {BLT_CONFIG_DISTANCE, "-buttonborderwidth", "buttonBorderWidth",
241 "ButtonBorderWidth", DEF_TEXTBOX_BUTTON_BORDERWIDTH,
242 Blt_Offset(Textbox, buttonBorderWidth),
243 BLT_CONFIG_DONT_SET_DEFAULT},
244 {BLT_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
245 "ExportSelection", DEF_TEXTBOX_EXPORT_SELECTION,
246 Blt_Offset(Textbox, exportSelection),
247 BLT_CONFIG_DONT_SET_DEFAULT},
248 {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
249 DEF_TEXTBOX_RELIEF, Blt_Offset(Textbox, relief), 0},
250 {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
251 DEF_TEXTBOX_SELECT_BG_MONO, Blt_Offset(Textbox, selBorder),
252 BLT_CONFIG_MONO_ONLY},
253 {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
254 DEF_TEXTBOX_SELECT_BACKGROUND, Blt_Offset(Textbox, selBorder),
255 BLT_CONFIG_COLOR_ONLY},
256 {BLT_CONFIG_DISTANCE, "-selectborderwidth", "selectBorderWidth",
257 "BorderWidth", DEF_TEXTBOX_SELECT_BORDERWIDTH,
258 Blt_Offset(Textbox, selBorderWidth),
259 BLT_CONFIG_DONT_SET_DEFAULT},
260 {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
261
262 DEF_TEXTBOX_SELECT_FG_MONO, Blt_Offset(Textbox, selFgColor),
263 BLT_CONFIG_MONO_ONLY},
264 {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
265 DEF_TEXTBOX_SELECT_FOREGROUND, Blt_Offset(Textbox, selFgColor),
266 BLT_CONFIG_COLOR_ONLY},
267 {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
268 DEF_TEXTBOX_SELECT_RELIEF, Blt_Offset(Textbox, selRelief),
269 BLT_CONFIG_DONT_SET_DEFAULT},
270 {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL,
271 0, 0}
272};
273
274static Tk_LostSelProc TextboxLostSelectionProc;
275static Tk_SelectionProc TextboxSelectionProc;
276static Tk_EventProc TextboxEventProc;
277
278/*
279 *----------------------------------------------------------------------
280 *
281 * EventuallyRedraw --
282 *
283 * Queues a request to redraw the widget at the next idle point.
284 *
285 * Results:
286 * None.
287 *
288 * Side effects:
289 * Information gets redisplayed. Right now we don't do selective
290 * redisplays: the whole window will be redrawn.
291 *
292 *----------------------------------------------------------------------
293 */
294static void
295EventuallyRedraw(tbPtr)
296 Textbox *tbPtr;
297{
298 if ((tbPtr->tkwin != NULL) &&
299 ((tbPtr->flags & TEXTBOX_REDRAW) == 0)) {
300 tbPtr->flags |= TEXTBOX_REDRAW;
301 Tcl_DoWhenIdle(DisplayTextbox, tbPtr);
302 }
303}
304
305/*
306 *----------------------------------------------------------------------
307 *
308 * BlinkCursorProc --
309 *
310 * This procedure is called as a timer handler to blink the
311 * insertion cursor off and on.
312 *
313 * Results:
314 * None.
315 *
316 * Side effects:
317 * The cursor gets turned on or off, redisplay gets invoked,
318 * and this procedure reschedules itself.
319 *
320 *----------------------------------------------------------------------
321 */
322static void
323BlinkCursorProc(clientData)
324 ClientData clientData; /* Pointer to record describing entry. */
325{
326 Textbox *tbPtr = clientData;
327 int interval;
328
329 if (!(tbPtr->flags & TEXTBOX_FOCUS) || (tbPtr->offTime == 0)) {
330 return;
331 }
332 if (tbPtr->active) {
333 tbPtr->cursorOn ^= 1;
334 interval = (tbPtr->cursorOn) ? tbPtr->onTime : tbPtr->offTime;
335 tbPtr->timerToken =
336 Tcl_CreateTimerHandler(interval, BlinkCursorProc, tbPtr);
337 EventuallyRedraw(tbPtr);
338 }
339}
340
341/*
342 * --------------------------------------------------------------
343 *
344 * TextboxEventProc --
345 *
346 * This procedure is invoked by the Tk dispatcher for various
347 * events on treeview widgets.
348 *
349 * Results:
350 * None.
351 *
352 * Side effects:
353 * When the window gets deleted, internal structures get
354 * cleaned up. When it gets exposed, it is redisplayed.
355 *
356 * --------------------------------------------------------------
357 */
358static void
359TextboxEventProc(clientData, eventPtr)
360 ClientData clientData; /* Information about window. */
361 XEvent *eventPtr; /* Information about event. */
362{
363 Textbox *tbPtr = clientData;
364
365 if (eventPtr->type == Expose) {
366 if (eventPtr->xexpose.count == 0) {
367 EventuallyRedraw(tbPtr);
368 }
369 } else if (eventPtr->type == ConfigureNotify) {
370 EventuallyRedraw(tbPtr);
371 } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
372 if (eventPtr->xfocus.detail == NotifyInferior) {
373 return;
374 }
375 if (eventPtr->type == FocusIn) {
376 tbPtr->flags |= TEXTBOX_FOCUS;
377 } else {
378 tbPtr->flags &= ~TEXTBOX_FOCUS;
379 }
380 Tcl_DeleteTimerHandler(tbPtr->timerToken);
381 if ((tbPtr->active) && (tbPtr->flags & TEXTBOX_FOCUS)) {
382 tbPtr->cursorOn = TRUE;
383 if (tbPtr->offTime != 0) {
384 tbPtr->timerToken = Tcl_CreateTimerHandler(tbPtr->onTime,
385 BlinkCursorProc, tbPtr);
386 }
387 } else {
388 tbPtr->cursorOn = FALSE;
389 tbPtr->timerToken = (Tcl_TimerToken) NULL;
390 }
391 EventuallyRedraw(tbPtr);
392 } else if (eventPtr->type == DestroyNotify) {
393 if (tbPtr->tkwin != NULL) {
394 tbPtr->tkwin = NULL;
395 }
396 if (tbPtr->flags & TEXTBOX_REDRAW) {
397 Tcl_CancelIdleCall(DisplayTextbox, tbPtr);
398 }
399 if (tbPtr->timerToken != NULL) {
400 Tcl_DeleteTimerHandler(tbPtr->timerToken);
401 }
402 tbPtr->tvPtr->comboWin = NULL;
403 Tcl_EventuallyFree(tbPtr, DestroyTextbox);
404 }
405}
406
407/*
408 *----------------------------------------------------------------------
409 *
410 * TextboxLostSelectionProc --
411 *
412 * This procedure is called back by Tk when the selection is
413 * grabbed away from a Text widget.
414 *
415 * Results:
416 * None.
417 *
418 * Side effects:
419 * The existing selection is unhighlighted, and the window is
420 * marked as not containing a selection.
421 *
422 *----------------------------------------------------------------------
423 */
424static void
425TextboxLostSelectionProc(clientData)
426 ClientData clientData; /* Information about Text widget. */
427{
428 Textbox *tbPtr = clientData;
429
430 if ((tbPtr->selFirst >= 0) && (tbPtr->exportSelection)) {
431 tbPtr->selFirst = tbPtr->selLast = -1;
432 EventuallyRedraw(tbPtr);
433 }
434}
435
436static int
437PointerToIndex(tbPtr, x, y)
438 Textbox *tbPtr;
439 int x, y;
440{
441 TextLayout *textPtr;
442 Tk_FontMetrics fontMetrics;
443 TextFragment *fragPtr;
444 int nBytes;
445 register int i;
446 int total;
447
448 if ((tbPtr->string == NULL) || (tbPtr->string[0] == '\0')) {
449 return 0;
450 }
451 x -= tbPtr->selBorderWidth;
452 y -= tbPtr->selBorderWidth;
453
454 textPtr = tbPtr->textPtr;
455
456 /* Bound the y-coordinate within the window. */
457 if (y < 0) {
458 y = 0;
459 } else if (y >= textPtr->height) {
460 y = textPtr->height - 1;
461 }
462 /*
463 * Compute the line that contains the y-coordinate.
464 *
465 * FIXME: This assumes that segments are distributed
466 * line-by-line. This may change in the future.
467 */
468 Tk_GetFontMetrics(tbPtr->font, &fontMetrics);
469 fragPtr = textPtr->fragArr;
470 total = 0;
471 for (i = (y / fontMetrics.linespace); i > 0; i--) {
472 total += fragPtr->count;
473 fragPtr++;
474 }
475 if (x < 0) {
476 nBytes = 0;
477 } else if (x >= textPtr->width) {
478 nBytes = fragPtr->count;
479 } else {
480 int newX;
481
482 /* Find the character underneath the pointer. */
483 nBytes = Tk_MeasureChars(tbPtr->font, fragPtr->text, fragPtr->count,
484 x, 0, &newX);
485 if ((newX < x) && (nBytes < fragPtr->count)) {
486 double fract;
487 int length, charSize;
488 char *next;
489
490 next = fragPtr->text + nBytes;
491#if HAVE_UTF
492 {
493 Tcl_UniChar dummy;
494
495 length = Tcl_UtfToUniChar(next, &dummy);
496 }
497#else
498 length = 1;
499#endif
500 charSize = Tk_TextWidth(tbPtr->font, next, length);
501 fract = ((double)(x - newX) / (double)charSize);
502 if (ROUND(fract)) {
503 nBytes += length;
504 }
505 }
506 }
507 return nBytes + total;
508}
509
510static int
511IndexToPointer(tbPtr)
512 Textbox *tbPtr;
513{
514 int x, y;
515 int maxLines;
516 TextLayout *textPtr;
517 Tk_FontMetrics fontMetrics;
518 int nBytes;
519 int sum;
520 TextFragment *fragPtr;
521 register int i;
522
523 textPtr = tbPtr->textPtr;
524 Tk_GetFontMetrics(tbPtr->font, &fontMetrics);
525 maxLines = (textPtr->height / fontMetrics.linespace) - 1;
526
527 sum = 0;
528 x = y = tbPtr->borderWidth;
529 if (tbPtr->icon != NULL) {
530 x += TreeViewIconWidth(tbPtr->icon) + 2 * tbPtr->gap;
531 }
532 fragPtr = textPtr->fragArr;
533 for (i = 0; i <= maxLines; i++) {
534 /* Total the number of bytes on each line. Include newlines. */
535 nBytes = fragPtr->count + 1;
536 if ((sum + nBytes) > tbPtr->insertPos) {
537 x += Tk_TextWidth(tbPtr->font, fragPtr->text,
538 tbPtr->insertPos - sum);
539 break;
540 }
541 y += fontMetrics.linespace;
542 sum += nBytes;
543 fragPtr++;
544 }
545 tbPtr->cursorX = x;
546 tbPtr->cursorY = y;
547 tbPtr->cursorHeight = fontMetrics.linespace;
548 tbPtr->cursorWidth = 3;
549 return TCL_OK;
550}
551
552static void
553UpdateLayout(tbPtr)
554 Textbox *tbPtr;
555{
556 TextStyle ts;
557 int width, height;
558 TextLayout *textPtr;
559 int gap;
560 int iconWidth, iconHeight;
561
562 gap = iconWidth = iconHeight = 0;
563 if (tbPtr->icon != NULL) {
564 iconWidth = TreeViewIconWidth(tbPtr->icon) + 4;
565 iconHeight = TreeViewIconHeight(tbPtr->icon);
566 gap = tbPtr->gap;
567 }
568
569 /* The layout is based upon the current font. */
570 Blt_InitTextStyle(&ts);
571 ts.anchor = TK_ANCHOR_NW;
572 ts.justify = TK_JUSTIFY_LEFT;
573 ts.font = tbPtr->font;
574 textPtr = Blt_GetTextLayout(tbPtr->string, &ts);
575 if (tbPtr->textPtr != NULL) {
576 Blt_Free(tbPtr->textPtr);
577 }
578 tbPtr->textPtr = textPtr;
579
580 width = iconWidth + textPtr->width + gap * 2;
581 height = MAX(iconHeight, textPtr->height);
582
583 if (width <= tbPtr->columnPtr->width) {
584 width = tbPtr->columnPtr->width;
585 }
586 if (height < tbPtr->entryPtr->height) {
587 height = tbPtr->entryPtr->height;
588 }
589 tbPtr->width = width + (2 * tbPtr->borderWidth);
590 tbPtr->height = height + (2 * tbPtr->borderWidth);
591 IndexToPointer(tbPtr);
592 Tk_MoveResizeWindow(tbPtr->tkwin, tbPtr->x, tbPtr->y,
593 tbPtr->width, tbPtr->height);
594 Tk_MapWindow(tbPtr->tkwin);
595 XRaiseWindow(tbPtr->display, Tk_WindowId(tbPtr->tkwin));
596}
597
598static void
599InsertText(tbPtr, insertText, insertPos, nBytes)
600 Textbox *tbPtr;
601 char *insertText;
602 int insertPos;
603 int nBytes;
604{
605 int oldSize, newSize;
606 char *oldText, *newText;
607
608 oldText = tbPtr->string;
609 oldSize = strlen(oldText);
610 newSize = oldSize + nBytes;
611 newText = Blt_Malloc(sizeof(char) * (newSize + 1));
612 if (insertPos == oldSize) { /* Append */
613 strcpy(newText, oldText);
614 strcat(newText, insertText);
615 } else if (insertPos == 0) {/* Prepend */
616 strcpy(newText, insertText);
617 strcat(newText, oldText);
618 } else { /* Insert into existing. */
619 char *p;
620
621 p = newText;
622 strncpy(p, oldText, insertPos);
623 p += insertPos;
624 strcpy(p, insertText);
625 p += nBytes;
626 strcpy(p, oldText + insertPos);
627 }
628
629 /*
630 * All indices from the start of the insertion to the end of the
631 * string need to be updated. Simply move the indices down by the
632 * number of characters added.
633 */
634 if (tbPtr->selFirst >= insertPos) {
635 tbPtr->selFirst += nBytes;
636 }
637 if (tbPtr->selLast > insertPos) {
638 tbPtr->selLast += nBytes;
639 }
640 if ((tbPtr->selAnchor > insertPos) || (tbPtr->selFirst >= insertPos)) {
641 tbPtr->selAnchor += nBytes;
642 }
643 if (tbPtr->string != NULL) {
644 Blt_Free(tbPtr->string);
645 }
646 tbPtr->string = newText;
647 tbPtr->insertPos = insertPos + nBytes;
648 UpdateLayout(tbPtr);
649}
650
651static int
652DeleteText(tbPtr, firstPos, lastPos)
653 Textbox *tbPtr;
654 int firstPos, lastPos;
655{
656 char *oldText, *newText;
657 int oldSize, newSize;
658 int nBytes;
659 char *p;
660
661 oldText = tbPtr->string;
662 if (firstPos > lastPos) {
663 return TCL_OK;
664 }
665 lastPos++; /* Now is the position after the last
666 * character. */
667
668 nBytes = lastPos - firstPos;
669
670 oldSize = strlen(oldText) + 1;
671 newSize = oldSize - nBytes + 1;
672 newText = Blt_Malloc(sizeof(char) * newSize);
673 p = newText;
674 if (firstPos > 0) {
675 strncpy(p, oldText, firstPos);
676 p += firstPos;
677 }
678 *p = '\0';
679 if (lastPos < oldSize) {
680 strcpy(p, oldText + lastPos);
681 }
682 Blt_Free(oldText);
683
684 /*
685 * Since deleting characters compacts the character array, we need to
686 * update the various character indices according. It depends where
687 * the index occurs in relation to range of deleted characters:
688 *
689 * before Ignore.
690 * within Move the index back to the start of the deletion.
691 * after Subtract off the deleted number of characters,
692 * since the array has been compressed by that
693 * many characters.
694 *
695 */
696 if (tbPtr->selFirst >= firstPos) {
697 if (tbPtr->selFirst >= lastPos) {
698 tbPtr->selFirst -= nBytes;
699 } else {
700 tbPtr->selFirst = firstPos;
701 }
702 }
703 if (tbPtr->selLast >= firstPos) {
704 if (tbPtr->selLast >= lastPos) {
705 tbPtr->selLast -= nBytes;
706 } else {
707 tbPtr->selLast = firstPos;
708 }
709 }
710 if (tbPtr->selLast <= tbPtr->selFirst) {
711 tbPtr->selFirst = tbPtr->selLast = -1; /* Cut away the entire
712 * selection. */
713 }
714 if (tbPtr->selAnchor >= firstPos) {
715 if (tbPtr->selAnchor >= lastPos) {
716 tbPtr->selAnchor -= nBytes;
717 } else {
718 tbPtr->selAnchor = firstPos;
719 }
720 }
721 if (tbPtr->insertPos >= firstPos) {
722 if (tbPtr->insertPos >= lastPos) {
723 tbPtr->insertPos -= nBytes;
724 } else {
725 tbPtr->insertPos = firstPos;
726 }
727 }
728 tbPtr->string = newText;
729 UpdateLayout(tbPtr);
730 EventuallyRedraw(tbPtr);
731 return TCL_OK;
732}
733
734static int
735AcquireText(tvPtr, tbPtr, entryPtr, columnPtr)
736 TreeView *tvPtr;
737 Textbox *tbPtr;
738 TreeViewEntry *entryPtr;
739 TreeViewColumn *columnPtr;
740{
741 TreeViewStyle *stylePtr;
742 int x, y;
743 char *string;
744 TreeViewIcon icon;
745
746 if (columnPtr == &tvPtr->treeColumn) {
747 int level;
748
749 level = DEPTH(tvPtr, entryPtr->node);
750 x = SCREENX(tvPtr, entryPtr->worldX);
751 y = SCREENY(tvPtr, entryPtr->worldY);
752 x += ICONWIDTH(level) + ICONWIDTH(level + 1) + 4;
753 string = GETLABEL(entryPtr);
754 stylePtr = columnPtr->stylePtr;
755 icon = Blt_TreeViewGetEntryIcon(tvPtr, entryPtr);
756 } else {
757 TreeViewValue *valuePtr;
758
759 x = SCREENX(tvPtr, columnPtr->worldX);
760 y = SCREENY(tvPtr, entryPtr->worldY);
761 stylePtr = columnPtr->stylePtr;
762 valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
763 string = valuePtr->string;
764 if (valuePtr->stylePtr != NULL) {
765 stylePtr = valuePtr->stylePtr;
766 }
767 icon = stylePtr->icon;
768 }
769 if (tbPtr->textPtr != NULL) {
770 Blt_Free(tbPtr->textPtr);
771 tbPtr->textPtr = NULL;
772 }
773 if (tbPtr->string != NULL) {
774 Blt_Free(tbPtr->string);
775 }
776 if (string == NULL) {
777 string = "";
778 }
779 tbPtr->icon = icon;
780 tbPtr->entryPtr = entryPtr;
781 tbPtr->columnPtr = columnPtr;
782 tbPtr->x = x - tbPtr->borderWidth;
783 tbPtr->y = y - tbPtr->borderWidth;
784
785 tbPtr->gap = stylePtr->gap;
786 tbPtr->string = Blt_Strdup(string);
787 tbPtr->gc = Blt_TreeViewGetStyleGC(stylePtr);
788 tbPtr->font = Blt_TreeViewGetStyleFont(tvPtr, stylePtr);
789 tbPtr->selFirst = tbPtr->selLast = -1;
790 UpdateLayout(tbPtr);
791 Tk_MapWindow(tbPtr->tkwin);
792 EventuallyRedraw(tbPtr);
793 return TCL_OK;
794}
795
796
797/*
798 *---------------------------------------------------------------------------
799 *
800 * GetIndexFromObj --
801 *
802 * Parse an index into an entry and return either its value
803 * or an error.
804 *
805 * Results:
806 * A standard Tcl result. If all went well, then *indexPtr is
807 * filled in with the character index (into entryPtr) corresponding to
808 * string. The index value is guaranteed to lie between 0 and
809 * the number of characters in the string, inclusive. If an
810 * error occurs then an error message is left in the interp's result.
811 *
812 * Side effects:
813 * None.
814 *
815 *---------------------------------------------------------------------------
816 */
817static int
818GetIndexFromObj(interp, tbPtr, objPtr, indexPtr)
819 Tcl_Interp *interp;
820 Textbox *tbPtr;
821 Tcl_Obj *objPtr;
822 int *indexPtr;
823{
824 int textPos;
825 char c;
826 char *string;
827
828 string = Tcl_GetString(objPtr);
829 if ((tbPtr->string == NULL) || (tbPtr->string[0] == '\0')) {
830 *indexPtr = 0;
831 return TCL_OK;
832 }
833 c = string[0];
834 if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
835 textPos = tbPtr->selAnchor;
836 } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
837 textPos = strlen(tbPtr->string);
838 } else if ((c == 'i') && (strcmp(string, "insert") == 0)) {
839 textPos = tbPtr->insertPos;
840 } else if ((c == 'n') && (strcmp(string, "next") == 0)) {
841 textPos = tbPtr->insertPos;
842 if (textPos < (int)strlen(tbPtr->string)) {
843 textPos++;
844 }
845 } else if ((c == 'l') && (strcmp(string, "last") == 0)) {
846 textPos = tbPtr->insertPos;
847 if (textPos > 0) {
848 textPos--;
849 }
850 } else if ((c == 's') && (strcmp(string, "sel.first") == 0)) {
851 if (tbPtr->selFirst < 0) {
852 textPos = -1;
853 } else {
854 textPos = tbPtr->selFirst;
855 }
856 } else if ((c == 's') && (strcmp(string, "sel.last") == 0)) {
857 if (tbPtr->selLast < 0) {
858 textPos = -1;
859 } else {
860 textPos = tbPtr->selLast;
861 }
862 } else if (c == '@') {
863 int x, y;
864
865 if (Blt_GetXY(interp, tbPtr->tkwin, string, &x, &y) != TCL_OK) {
866 return TCL_ERROR;
867 }
868 textPos = PointerToIndex(tbPtr, x, y);
869 } else if (isdigit((int)c)) {
870 int number;
871 int maxChars;
872
873 if (Tcl_GetIntFromObj(interp, objPtr, &number) != TCL_OK) {
874 return TCL_ERROR;
875 }
876 /* Don't allow the index to point outside the label. */
877 maxChars = Tcl_NumUtfChars(tbPtr->string, -1);
878 if (number < 0) {
879 textPos = 0;
880 } else if (number > maxChars) {
881 textPos = strlen(tbPtr->string);
882 } else {
883 textPos = Tcl_UtfAtIndex(tbPtr->string, number) -
884 tbPtr->string;
885 }
886 } else {
887 if (interp != NULL) {
888 Tcl_AppendResult(interp, "bad label index \"", string, "\"",
889 (char *)NULL);
890 }
891 return TCL_ERROR;
892 }
893 *indexPtr = textPos;
894 return TCL_OK;
895}
896
897/*
898 *----------------------------------------------------------------------
899 *
900 * SelectText --
901 *
902 * Modify the selection by moving its un-anchored end. This
903 * could make the selection either larger or smaller.
904 *
905 * Results:
906 * None.
907 *
908 * Side effects:
909 * The selection changes.
910 *
911 *----------------------------------------------------------------------
912 */
913static int
914SelectText(tbPtr, textPos)
915 Textbox *tbPtr; /* Information about textbox. */
916 int textPos; /* Index of element that is to
917 * become the "other" end of the
918 * selection. */
919{
920 int selFirst, selLast;
921
922 /*
923 * Grab the selection if we don't own it already.
924 */
925 if ((tbPtr->exportSelection) && (tbPtr->selFirst == -1)) {
926 Tk_OwnSelection(tbPtr->tkwin, XA_PRIMARY,
927 TextboxLostSelectionProc, tbPtr);
928 }
929 /* If the anchor hasn't been set yet, assume the beginning of the text*/
930 if (tbPtr->selAnchor < 0) {
931 tbPtr->selAnchor = 0;
932 }
933 if (tbPtr->selAnchor <= textPos) {
934 selFirst = tbPtr->selAnchor;
935 selLast = textPos;
936 } else {
937 selFirst = textPos;
938 selLast = tbPtr->selAnchor;
939 }
940 if ((tbPtr->selFirst != selFirst) || (tbPtr->selLast != selLast)) {
941 tbPtr->selFirst = selFirst;
942 tbPtr->selLast = selLast;
943 EventuallyRedraw(tbPtr);
944 }
945 return TCL_OK;
946}
947
948/*
949 *----------------------------------------------------------------------
950 *
951 * TextboxSelectionProc --
952 *
953 * This procedure is called back by Tk when the selection is
954 * requested by someone. It returns part or all of the selection
955 * in a buffer provided by the caller.
956 *
957 * Results:
958 * The return value is the number of non-NULL bytes stored at
959 * buffer. Buffer is filled (or partially filled) with a
960 * NUL-terminated string containing part or all of the
961 * selection, as given by offset and maxBytes.
962 *
963 * Side effects:
964 * None.
965 *
966 *----------------------------------------------------------------------
967 */
968static int
969TextboxSelectionProc(clientData, offset, buffer, maxBytes)
970 ClientData clientData; /* Information about the widget. */
971 int offset; /* Offset within selection of first
972 * character to be returned. */
973 char *buffer; /* Location in which to place
974 * selection. */
975 int maxBytes; /* Maximum number of bytes to place
976 * at buffer, not including terminating
977 * NULL character. */
978{
979 Textbox *tbPtr = clientData;
980 int size;
981
982 size = strlen(tbPtr->string + offset);
983 /*
984 * Return the string currently in the textbox.
985 */
986 strncpy(buffer, tbPtr->string + offset, maxBytes);
987 buffer[maxBytes] = '\0';
988 return (size > maxBytes) ? maxBytes : size;
989}
990
991
992static void
993DestroyTextbox(data)
994 DestroyData data;
995{
996 Textbox *tbPtr = (Textbox *)data;
997
998 Blt_FreeObjOptions(textboxConfigSpecs, (char *)tbPtr,
999 tbPtr->display, 0);
1000
1001 if (tbPtr->string != NULL) {
1002 Blt_Free(tbPtr->string);
1003 }
1004 if (tbPtr->textPtr != NULL) {
1005 Blt_Free(tbPtr->textPtr);
1006 }
1007 if (tbPtr->timerToken != NULL) {
1008 Tcl_DeleteTimerHandler(tbPtr->timerToken);
1009 }
1010 if (tbPtr->tkwin != NULL) {
1011 Tk_DeleteSelHandler(tbPtr->tkwin, XA_PRIMARY, XA_STRING);
1012 }
1013 Blt_Free(tbPtr);
1014}
1015
1016static void
1017ConfigureTextbox(tbPtr)
1018 Textbox *tbPtr;
1019{
1020#ifdef notdef
1021 GC newGC;
1022 Textbox *tbPtr = tvPtr->tbPtr;
1023 XGCValues gcValues;
1024 unsigned long gcMask;
1025
1026 /*
1027 * GC for edit window.
1028 */
1029 gcMask = 0;
1030 newGC = Tk_GetGC(tbPtr->tkwin, gcMask, &gcValues);
1031 if (tbPtr->gc != NULL) {
1032 Tk_FreeGC(tbPtr->display, tbPtr->gc);
1033 }
1034 tbPtr->gc = newGC;
1035 tbPtr->width = tbPtr->textPtr->width +
1036 2 * (tbPtr->borderWidth + tbPtr->highlightWidth);
1037 tbPtr->height = tbPtr->textPtr->height +
1038 2 * (tbPtr->borderWidth + tbPtr->highlightWidth);
1039
1040 if (Tk_IsMapped(tbPtr->tkwin)) {
1041 if ((tbPtr->height != Tk_Height(tbPtr->tkwin)) ||
1042 (tbPtr->width != Tk_Width(tbPtr->tkwin))) {
1043 Tk_ResizeWindow(tbPtr->tkwin, tbPtr->width, tbPtr->height);
1044 }
1045 }
1046#endif
1047}
1048
1049int
1050Blt_TreeViewTextbox(tvPtr, entryPtr, columnPtr)
1051 TreeView *tvPtr;
1052 TreeViewEntry *entryPtr;
1053 TreeViewColumn *columnPtr;
1054{
1055 Tk_Window tkwin;
1056 Textbox *tbPtr;
1057 char editClass[20];
1058
1059 if (tvPtr->comboWin != NULL) {
1060 Tk_DestroyWindow(tvPtr->comboWin);
1061 }
1062 tkwin = Tk_CreateWindow(tvPtr->interp, tvPtr->tkwin, "edit", (char *)NULL);
1063 if (tkwin == NULL) {
1064 return TCL_ERROR;
1065 }
1066
1067 Tk_MakeWindowExist(tkwin);
1068
1069 sprintf(editClass, "%sEditor", Tk_Class(tvPtr->tkwin));
1070 Tk_SetClass(tkwin, editClass);
1071
1072 tbPtr = Blt_Calloc(1, sizeof(Textbox));
1073 assert(tbPtr);
1074
1075 tbPtr->interp = tvPtr->interp;
1076 tbPtr->display = Tk_Display(tkwin);
1077 tbPtr->tkwin = tkwin;
1078 tbPtr->borderWidth = 1;
1079 tbPtr->relief = TK_RELIEF_SOLID;
1080 tbPtr->selRelief = TK_RELIEF_FLAT;
1081 tbPtr->selBorderWidth = 1;
1082 tbPtr->selAnchor = -1;
1083 tbPtr->selFirst = tbPtr->selLast = -1;
1084 tbPtr->onTime = 600;
1085 tbPtr->active = TRUE;
1086 tbPtr->offTime = 300;
1087 tbPtr->tvPtr = tvPtr;
1088 tbPtr->buttonRelief = TK_RELIEF_SUNKEN;
1089 tbPtr->buttonBorderWidth = 1;
1090 tvPtr->comboWin = tkwin;
1091#if (TK_MAJOR_VERSION > 4)
1092 Blt_SetWindowInstanceData(tkwin, tbPtr);
1093#endif
1094 Tk_CreateSelHandler(tkwin, XA_PRIMARY, XA_STRING,
1095 TextboxSelectionProc, tbPtr, XA_STRING);
1096 Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask |
1097 FocusChangeMask, TextboxEventProc, tbPtr);
1098 Tcl_CreateObjCommand(tvPtr->interp, Tk_PathName(tkwin),
1099 TextboxCmd, tbPtr, NULL);
1100 if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tkwin, textboxConfigSpecs, 0,
1101 (Tcl_Obj **)NULL, (char *)tbPtr, 0) != TCL_OK) {
1102 Tk_DestroyWindow(tkwin);
1103 return TCL_ERROR;
1104 }
1105 AcquireText(tvPtr, tbPtr, entryPtr, columnPtr);
1106 tbPtr->insertPos = strlen(tbPtr->string);
1107
1108 Tk_MoveResizeWindow(tkwin, tbPtr->x, tbPtr->y, tbPtr->width,
1109 tbPtr->height);
1110 Tk_MapWindow(tkwin);
1111 Tk_MakeWindowExist(tkwin);
1112 XRaiseWindow(tbPtr->display, Tk_WindowId(tkwin));
1113 EventuallyRedraw(tbPtr);
1114 return TCL_OK;
1115}
1116
1117static void
1118DisplayTextbox(clientData)
1119 ClientData clientData;
1120{
1121 Textbox *tbPtr = clientData;
1122 Pixmap drawable;
1123 register int i;
1124 int x1, x2;
1125 int count, nChars;
1126 int leftPos, rightPos;
1127 int selStart, selEnd, selLength;
1128 int x, y;
1129 TextFragment *fragPtr;
1130 Tk_FontMetrics fontMetrics;
1131#ifdef notdef
1132 int buttonX, buttonY, buttonWidth, buttonHeight;
1133#endif
1134 tbPtr->flags &= ~TEXTBOX_REDRAW;
1135 if (!Tk_IsMapped(tbPtr->tkwin)) {
1136 return;
1137 }
1138 if (tbPtr->columnPtr == NULL) {
1139 return;
1140 }
1141 drawable = Tk_GetPixmap(tbPtr->display, Tk_WindowId(tbPtr->tkwin),
1142 Tk_Width(tbPtr->tkwin), Tk_Height(tbPtr->tkwin),
1143 Tk_Depth(tbPtr->tkwin));
1144
1145 Blt_Fill3DRectangle(tbPtr->tkwin, drawable, tbPtr->border, 0, 0,
1146 Tk_Width(tbPtr->tkwin), Tk_Height(tbPtr->tkwin),
1147 tbPtr->borderWidth, tbPtr->relief);
1148
1149 x = tbPtr->borderWidth + tbPtr->gap;
1150 y = tbPtr->borderWidth;
1151
1152#ifdef notdef
1153 buttonX = Tk_Width(tbPtr->tkwin) -
1154 (tbPtr->borderWidth + tbPtr->gap + 1);
1155 buttonY = tbPtr->borderWidth + 1;
1156#endif
1157
1158 if (tbPtr->icon != NULL) {
1159 y += (tbPtr->height - TreeViewIconHeight(tbPtr->icon)) / 2;
1160 Tk_RedrawImage(TreeViewIconBits(tbPtr->icon), 0, 0,
1161 TreeViewIconWidth(tbPtr->icon),
1162 TreeViewIconHeight(tbPtr->icon),
1163 drawable, x, y);
1164 x += TreeViewIconWidth(tbPtr->icon) + tbPtr->gap;
1165 }
1166
1167 Tk_GetFontMetrics(tbPtr->font, &fontMetrics);
1168 fragPtr = tbPtr->textPtr->fragArr;
1169 count = 0;
1170 y = tbPtr->borderWidth;
1171 if (tbPtr->height > fontMetrics.linespace) {
1172 y += (tbPtr->height - fontMetrics.linespace) / 2;
1173 }
1174 for (i = 0; i < tbPtr->textPtr->nFrags; i++, fragPtr++) {
1175 leftPos = count;
1176 count += fragPtr->count;
1177 rightPos = count;
1178 if ((rightPos < tbPtr->selFirst) || (leftPos > tbPtr->selLast)) {
1179 /* No part of the text fragment is selected. */
1180 Tk_DrawChars(tbPtr->display, drawable, tbPtr->gc,
1181 tbPtr->font, fragPtr->text, fragPtr->count,
1182 x + fragPtr->x, y + fragPtr->y);
1183 continue;
1184 }
1185
1186 /*
1187 * A text fragment can have up to 3 regions:
1188 *
1189 * 1. Text before the start the selection
1190 * 2. Selected text itself (drawn in a raised border)
1191 * 3. Text following the selection.
1192 */
1193
1194 selStart = leftPos;
1195 selEnd = rightPos;
1196 /* First adjust selected region for current line. */
1197 if (tbPtr->selFirst > leftPos) {
1198 selStart = tbPtr->selFirst;
1199 }
1200 if (tbPtr->selLast < rightPos) {
1201 selEnd = tbPtr->selLast;
1202 }
1203 selLength = (selEnd - selStart);
1204 x1 = x;
1205
1206 if (selStart > leftPos) { /* Normal text preceding the selection */
1207 nChars = (selStart - leftPos);
1208 Tk_MeasureChars(tbPtr->font, tbPtr->string + leftPos,
1209 nChars, 10000, DEF_TEXT_FLAGS, &x1);
1210 x1 += x;
1211 }
1212 if (selLength > 0) { /* The selection itself */
1213 int width;
1214
1215 Tk_MeasureChars(tbPtr->font, fragPtr->text + selStart, selLength,
1216 10000, DEF_TEXT_FLAGS, &x2);
1217 x2 += x;
1218 width = (x2 - x1) + 1;
1219 Blt_Fill3DRectangle(tbPtr->tkwin, drawable, tbPtr->selBorder,
1220 x1, y + fragPtr->y - fontMetrics.ascent,
1221 width, fontMetrics.linespace,
1222 tbPtr->selBorderWidth, tbPtr->selRelief);
1223 }
1224 Tk_DrawChars(Tk_Display(tbPtr->tkwin), drawable, tbPtr->gc,
1225 tbPtr->font, fragPtr->text, fragPtr->count,
1226 fragPtr->x + x, fragPtr->y + y);
1227 }
1228 if ((tbPtr->flags & TEXTBOX_FOCUS) && (tbPtr->cursorOn)) {
1229 int left, top, right, bottom;
1230
1231 IndexToPointer(tbPtr);
1232 left = tbPtr->cursorX + 1;
1233 right = left + 1;
1234 top = tbPtr->cursorY;
1235 if (tbPtr->height > fontMetrics.linespace) {
1236 top += (tbPtr->height - fontMetrics.linespace) / 2;
1237 }
1238 bottom = top + tbPtr->cursorHeight - 1;
1239 XDrawLine(tbPtr->display, drawable, tbPtr->gc, left, top, left,
1240 bottom);
1241 XDrawLine(tbPtr->display, drawable, tbPtr->gc, left - 1, top, right,
1242 top);
1243 XDrawLine(tbPtr->display, drawable, tbPtr->gc, left - 1, bottom,
1244 right, bottom);
1245 }
1246#ifdef notdef
1247 buttonWidth = STD_ARROW_WIDTH + 6 + 2 * tbPtr->buttonBorderWidth;
1248 buttonX -= buttonWidth;
1249 buttonHeight = Tk_Height(tbPtr->tkwin) - 4;
1250 Blt_Fill3DRectangle(tbPtr->tkwin, drawable, tbPtr->buttonBorder,
1251 buttonX, buttonY, buttonWidth, buttonHeight,
1252 tbPtr->buttonBorderWidth, tbPtr->buttonRelief);
1253
1254 buttonX += buttonWidth / 2;
1255 buttonY = tbPtr->height / 2;
1256 Blt_DrawArrow(tbPtr->display, drawable, tbPtr->gc, buttonX,
1257 buttonY, STD_ARROW_HEIGHT, ARROW_DOWN);
1258#endif
1259 Blt_Draw3DRectangle(tbPtr->tkwin, drawable, tbPtr->border, 0, 0,
1260 Tk_Width(tbPtr->tkwin), Tk_Height(tbPtr->tkwin),
1261 tbPtr->borderWidth, tbPtr->relief);
1262 XCopyArea(tbPtr->display, drawable, Tk_WindowId(tbPtr->tkwin),
1263 tbPtr->gc, 0, 0, Tk_Width(tbPtr->tkwin),
1264 Tk_Height(tbPtr->tkwin), 0, 0);
1265 Tk_FreePixmap(tbPtr->display, drawable);
1266}
1267
1268/*ARGSUSED*/
1269static int
1270ApplyOp(tbPtr, interp, objc, objv)
1271 Textbox *tbPtr;
1272 Tcl_Interp *interp;
1273 int objc; /* Not used. */
1274 Tcl_Obj *CONST *objv; /* Not used. */
1275{
1276 TreeViewEntry *entryPtr;
1277 TreeView *tvPtr = tbPtr->tvPtr;
1278
1279 entryPtr = tbPtr->entryPtr;
1280 if (tbPtr->columnPtr == &tvPtr->treeColumn) {
1281 if (entryPtr->labelUid != NULL) {
1282 Blt_TreeViewFreeUid(tvPtr, entryPtr->labelUid);
1283 }
1284 if (tbPtr->string == NULL) {
1285 entryPtr->labelUid = Blt_TreeViewGetUid(tvPtr, "");
1286 } else {
1287 entryPtr->labelUid = Blt_TreeViewGetUid(tvPtr, tbPtr->string);
1288 }
1289 } else {
1290 TreeViewColumn *columnPtr;
1291 Tcl_Obj *objPtr;
1292
1293 columnPtr = tbPtr->columnPtr;
1294 objPtr = Tcl_NewStringObj(tbPtr->string, -1);
1295 if (Blt_TreeSetValueByKey(interp, tvPtr->tree, entryPtr->node,
1296 columnPtr->key, objPtr) != TCL_OK) {
1297 Tcl_DecrRefCount(objPtr);
1298 return TCL_ERROR;
1299 }
1300 entryPtr->flags |= ENTRY_DIRTY;
1301 }
1302 if (tvPtr != NULL) {
1303 Blt_TreeViewConfigureEntry(tvPtr, entryPtr, 0, NULL,
1304 BLT_CONFIG_OBJV_ONLY);
1305 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
1306 Blt_TreeViewEventuallyRedraw(tvPtr);
1307 }
1308 Tk_DestroyWindow(tbPtr->tkwin);
1309 return TCL_OK;
1310}
1311
1312/*ARGSUSED*/
1313static int
1314CancelOp(tbPtr, interp, objc, objv)
1315 Textbox *tbPtr;
1316 Tcl_Interp *interp; /* Not used. */
1317 int objc; /* Not used. */
1318 Tcl_Obj *CONST *objv; /* Not used. */
1319{
1320 Tk_DestroyWindow(tbPtr->tkwin);
1321 return TCL_OK;
1322}
1323
1324/*
1325 *----------------------------------------------------------------------
1326 *
1327 * CgetOp --
1328 *
1329 *----------------------------------------------------------------------
1330 */
1331/*ARGSUSED*/
1332static int
1333CgetOp(tbPtr, interp, objc, objv)
1334 Textbox *tbPtr;
1335 Tcl_Interp *interp;
1336 int objc; /* Not used. */
1337 Tcl_Obj *CONST *objv;
1338{
1339 return Blt_ConfigureValueFromObj(interp, tbPtr->tkwin,
1340 textboxConfigSpecs, (char *)tbPtr, objv[2], 0);
1341}
1342
1343/*
1344 *----------------------------------------------------------------------
1345 *
1346 * ConfigureOp --
1347 *
1348 * This procedure is called to process a list of configuration
1349 * options database, in order to reconfigure the one of more
1350 * entries in the widget.
1351 *
1352 * .c configure option value
1353 *
1354 * Results:
1355 * A standard Tcl result. If TCL_ERROR is returned, then
1356 * interp->result contains an error message.
1357 *
1358 * Side effects:
1359 * Configuration information, such as text string, colors, font,
1360 * etc. get set for tvPtr; old resources get freed, if there
1361 * were any. The hypertext is redisplayed.
1362 *
1363 *----------------------------------------------------------------------
1364 */
1365static int
1366ConfigureOp(tbPtr, interp, objc, objv)
1367 Textbox *tbPtr;
1368 Tcl_Interp *interp;
1369 int objc;
1370 Tcl_Obj *CONST *objv;
1371{
1372 if (objc == 2) {
1373 return Blt_ConfigureInfoFromObj(interp, tbPtr->tkwin,
1374 textboxConfigSpecs, (char *)tbPtr, (Tcl_Obj *)NULL, 0);
1375 } else if (objc == 3) {
1376 return Blt_ConfigureInfoFromObj(interp, tbPtr->tkwin,
1377 textboxConfigSpecs, (char *)tbPtr, objv[3], 0);
1378 }
1379 if (Blt_ConfigureWidgetFromObj(interp, tbPtr->tkwin,
1380 textboxConfigSpecs, objc - 2, objv + 2, (char *)tbPtr,
1381 BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
1382 return TCL_ERROR;
1383 }
1384 ConfigureTextbox(tbPtr);
1385 EventuallyRedraw(tbPtr);
1386 return TCL_OK;
1387}
1388
1389/*
1390 *----------------------------------------------------------------------
1391 *
1392 * DeleteOp --
1393 *
1394 * Remove one or more characters from the label of an entry.
1395 *
1396 * Results:
1397 * None.
1398 *
1399 * Side effects:
1400 * Memory gets freed, the entry gets modified and (eventually)
1401 * redisplayed.
1402 *
1403 *----------------------------------------------------------------------
1404 */
1405/*ARGSUSED*/
1406static int
1407DeleteOp(tbPtr, interp, objc, objv)
1408 Textbox *tbPtr;
1409 Tcl_Interp *interp; /* Not used. */
1410 int objc;
1411 Tcl_Obj *CONST *objv;
1412{
1413 int firstPos, lastPos;
1414
1415 if (tbPtr->entryPtr == NULL) {
1416 return TCL_OK;
1417 }
1418 if (GetIndexFromObj(interp, tbPtr, objv[2], &firstPos) != TCL_OK) {
1419 return TCL_ERROR;
1420 }
1421 lastPos = firstPos;
1422 if ((objc == 4) &&
1423 (GetIndexFromObj(interp, tbPtr, objv[3], &lastPos) != TCL_OK)) {
1424 return TCL_ERROR;
1425 }
1426 if (firstPos > lastPos) {
1427 return TCL_OK;
1428 }
1429 return DeleteText(tbPtr, firstPos, lastPos);
1430}
1431
1432
1433/*
1434 *----------------------------------------------------------------------
1435 *
1436 * IcursorOp --
1437 *
1438 * Returns the numeric index of the given string. Indices can be
1439 * one of the following:
1440 *
1441 * "anchor" Selection anchor.
1442 * "end" End of the label.
1443 * "insert" Insertion cursor.
1444 * "sel.first" First character selected.
1445 * "sel.last" Last character selected.
1446 * @x,y Index at X-Y screen coordinate.
1447 * number Returns the same number.
1448 *
1449 * Results:
1450 * A standard Tcl result. If the argument does not represent a
1451 * valid label index, then TCL_ERROR is returned and the interpreter
1452 * result will contain an error message.
1453 *
1454 *----------------------------------------------------------------------
1455 */
1456/*ARGSUSED*/
1457static int
1458IcursorOp(tbPtr, interp, objc, objv)
1459 Textbox *tbPtr;
1460 Tcl_Interp *interp;
1461 int objc; /* Not used. */
1462 Tcl_Obj *CONST *objv;
1463{
1464 int textPos;
1465
1466 if (GetIndexFromObj(interp, tbPtr, objv[2], &textPos) != TCL_OK) {
1467 return TCL_ERROR;
1468 }
1469 if (tbPtr->columnPtr != NULL) {
1470 tbPtr->insertPos = textPos;
1471 IndexToPointer(tbPtr);
1472 EventuallyRedraw(tbPtr);
1473 }
1474 return TCL_OK;
1475}
1476
1477
1478/*
1479 *----------------------------------------------------------------------
1480 *
1481 * IndexOp --
1482 *
1483 * Returns the numeric index of the given string. Indices can be
1484 * one of the following:
1485 *
1486 * "anchor" Selection anchor.
1487 * "end" End of the label.
1488 * "insert" Insertion cursor.
1489 * "sel.first" First character selected.
1490 * "sel.last" Last character selected.
1491 * @x,y Index at X-Y screen coordinate.
1492 * number Returns the same number.
1493 *
1494 * Results:
1495 * A standard Tcl result. If the argument does not represent a
1496 * valid label index, then TCL_ERROR is returned and the interpreter
1497 * result will contain an error message.
1498 *
1499 *----------------------------------------------------------------------
1500 */
1501/*ARGSUSED*/
1502static int
1503IndexOp(tbPtr, interp, objc, objv)
1504 Textbox *tbPtr;
1505 Tcl_Interp *interp;
1506 int objc; /* Not used. */
1507 Tcl_Obj *CONST *objv;
1508{
1509 int textPos;
1510
1511 if (GetIndexFromObj(interp, tbPtr, objv[2], &textPos) != TCL_OK) {
1512 return TCL_ERROR;
1513 }
1514 if ((tbPtr->columnPtr != NULL) && (tbPtr->string != NULL)) {
1515 int nChars;
1516
1517 nChars = Tcl_NumUtfChars(tbPtr->string, textPos);
1518 Tcl_SetObjResult(interp, Tcl_NewIntObj(nChars));
1519 }
1520 return TCL_OK;
1521}
1522
1523/*
1524 *----------------------------------------------------------------------
1525 *
1526 * InsertOp --
1527 *
1528 * Add new characters to the label of an entry.
1529 *
1530 * Results:
1531 * None.
1532 *
1533 * Side effects:
1534 * New information gets added to tbPtr; it will be redisplayed
1535 * soon, but not necessarily immediately.
1536 *
1537 *----------------------------------------------------------------------
1538 */
1539/*ARGSUSED*/
1540static int
1541InsertOp(tbPtr, interp, objc, objv)
1542 Textbox *tbPtr;
1543 Tcl_Interp *interp; /* Not used. */
1544 int objc;
1545 Tcl_Obj *CONST *objv;
1546{
1547 int extra;
1548 int insertPos;
1549 char *string;
1550
1551 if (tbPtr->entryPtr == NULL) {
1552 return TCL_ERROR;
1553 }
1554 if (GetIndexFromObj(interp, tbPtr, objv[2], &insertPos) != TCL_OK) {
1555 return TCL_ERROR;
1556 }
1557 string = Tcl_GetStringFromObj(objv[3], &extra);
1558 if (extra == 0) { /* Nothing to insert. Move the cursor anyways. */
1559 tbPtr->insertPos = insertPos;
1560 } else {
1561 InsertText(tbPtr, string, insertPos, extra);
1562 }
1563 return TCL_OK;
1564}
1565
1566/*ARGSUSED*/
1567static int
1568SelectionAdjustOp(tbPtr, interp, objc, objv)
1569 Textbox *tbPtr;
1570 Tcl_Interp *interp; /* Not used. */
1571 int objc;
1572 Tcl_Obj *CONST *objv;
1573{
1574 int textPos;
1575 int half1, half2;
1576
1577 if (GetIndexFromObj(interp, tbPtr, objv[3], &textPos) != TCL_OK) {
1578 return TCL_ERROR;
1579 }
1580 half1 = (tbPtr->selFirst + tbPtr->selLast) / 2;
1581 half2 = (tbPtr->selFirst + tbPtr->selLast + 1) / 2;
1582 if (textPos < half1) {
1583 tbPtr->selAnchor = tbPtr->selLast;
1584 } else if (textPos > half2) {
1585 tbPtr->selAnchor = tbPtr->selFirst;
1586 }
1587 return SelectText(tbPtr, textPos);
1588}
1589
1590/*ARGSUSED*/
1591static int
1592SelectionClearOp(tbPtr, interp, objc, objv)
1593 Textbox *tbPtr;
1594 Tcl_Interp *interp; /* Not used. */
1595 int objc; /* Not used. */
1596 Tcl_Obj *CONST *objv; /* Not used. */
1597{
1598 if (tbPtr->selFirst != -1) {
1599 tbPtr->selFirst = tbPtr->selLast = -1;
1600 EventuallyRedraw(tbPtr);
1601 }
1602 return TCL_OK;
1603}
1604
1605/*ARGSUSED*/
1606static int
1607SelectionFromOp(tbPtr, interp, objc, objv)
1608 Textbox *tbPtr;
1609 Tcl_Interp *interp; /* Not used. */
1610 int objc;
1611 Tcl_Obj *CONST *objv;
1612{
1613 int textPos;
1614
1615 if (GetIndexFromObj(interp, tbPtr, objv[3], &textPos) != TCL_OK) {
1616 return TCL_ERROR;
1617 }
1618 tbPtr->selAnchor = textPos;
1619 return TCL_OK;
1620}
1621
1622/*ARGSUSED*/
1623static int
1624SelectionPresentOp(tbPtr, interp, objc, objv)
1625 Textbox *tbPtr;
1626 Tcl_Interp *interp;
1627 int objc; /* Not used. */
1628 Tcl_Obj *CONST *objv; /* Not used. */
1629{
1630 int bool;
1631
1632 bool = (tbPtr->selFirst != -1);
1633 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
1634 return TCL_OK;
1635}
1636
1637/*ARGSUSED*/
1638static int
1639SelectionRangeOp(tbPtr, interp, objc, objv)
1640 Textbox *tbPtr;
1641 Tcl_Interp *interp; /* Not used. */
1642 int objc;
1643 Tcl_Obj *CONST *objv;
1644{
1645 int selFirst, selLast;
1646
1647 if (GetIndexFromObj(interp, tbPtr, objv[3], &selFirst) != TCL_OK) {
1648 return TCL_ERROR;
1649 }
1650 if (GetIndexFromObj(interp, tbPtr, objv[4], &selLast) != TCL_OK) {
1651 return TCL_ERROR;
1652 }
1653 tbPtr->selAnchor = selFirst;
1654 return SelectText(tbPtr, selLast);
1655}
1656
1657/*ARGSUSED*/
1658static int
1659SelectionToOp(tbPtr, interp, objc, objv)
1660 Textbox *tbPtr;
1661 Tcl_Interp *interp; /* Not used. */
1662 int objc;
1663 Tcl_Obj *CONST *objv;
1664{
1665 int textPos;
1666
1667 if (GetIndexFromObj(interp, tbPtr, objv[3], &textPos) != TCL_OK) {
1668 return TCL_ERROR;
1669 }
1670 return SelectText(tbPtr, textPos);
1671}
1672
1673
1674static Blt_OpSpec selectionOps[] =
1675{
1676 {"adjust", 1, (Blt_Op)SelectionAdjustOp, 4, 4, "index",},
1677 {"clear", 1, (Blt_Op)SelectionClearOp, 3, 3, "",},
1678 {"from", 1, (Blt_Op)SelectionFromOp, 4, 4, "index"},
1679 {"present", 1, (Blt_Op)SelectionPresentOp, 3, 3, ""},
1680 {"range", 1, (Blt_Op)SelectionRangeOp, 5, 5, "start end",},
1681 {"to", 1, (Blt_Op)SelectionToOp, 4, 4, "index"},
1682};
1683
1684static int nSelectionOps = sizeof(selectionOps) / sizeof(Blt_OpSpec);
1685
1686/*
1687 * This procedure handles the individual options for text
1688 * selections. The selected text is designated by start and end
1689 * indices into the text pool. The selected segment has both a
1690 * anchored and unanchored ends. The following selection
1691 * operations are implemented:
1692 *
1693 * "adjust" - resets either the first or last index
1694 * of the selection.
1695 * "clear" - clears the selection. Sets first/last
1696 * indices to -1.
1697 * "from" - sets the index of the selection anchor.
1698 * "present" - return "1" if a selection is available,
1699 * "0" otherwise.
1700 * "range" - sets the first and last indices.
1701 * "to" - sets the index of the un-anchored end.
1702 */
1703static int
1704SelectionOp(tbPtr, interp, objc, objv)
1705 Textbox *tbPtr;
1706 Tcl_Interp *interp;
1707 int objc;
1708 Tcl_Obj *CONST *objv;
1709{
1710 Blt_Op proc;
1711 int result;
1712
1713 proc = Blt_GetOpFromObj(interp, nSelectionOps, selectionOps, BLT_OP_ARG2,
1714 objc, objv, 0);
1715 if (proc == NULL) {
1716 return TCL_ERROR;
1717 }
1718 result = (*proc) (tbPtr, interp, objc, objv);
1719 return result;
1720}
1721
1722/*
1723 *----------------------------------------------------------------------
1724 *
1725 * TextboxCmd --
1726 *
1727 * This procedure handles entry operations.
1728 *
1729 * Results:
1730 * A standard Tcl result.
1731 *
1732 *----------------------------------------------------------------------
1733 */
1734static Blt_OpSpec comboOps[] =
1735{
1736 {"apply", 1, (Blt_Op)ApplyOp, 2, 2, "",},
1737 {"cancel", 2, (Blt_Op)CancelOp, 2, 2, "",},
1738 {"cget", 2, (Blt_Op)CgetOp, 3, 3, "value",},
1739 {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value...?",},
1740 {"delete", 1, (Blt_Op)DeleteOp, 3, 4, "first ?last?"},
1741 {"icursor", 2, (Blt_Op)IcursorOp, 3, 3, "index"},
1742 {"index", 3, (Blt_Op)IndexOp, 3, 3, "index"},
1743 {"insert", 3, (Blt_Op)InsertOp, 4, 4, "index string"},
1744 {"selection", 1, (Blt_Op)SelectionOp, 2, 0, "args"},
1745};
1746static int nComboOps = sizeof(comboOps) / sizeof(Blt_OpSpec);
1747
1748static int
1749TextboxCmd(clientData, interp, objc, objv)
1750 ClientData clientData;
1751 Tcl_Interp *interp;
1752 int objc;
1753 Tcl_Obj *CONST *objv;
1754{
1755 Textbox *tbPtr = clientData;
1756 Blt_Op proc;
1757 int result;
1758
1759 proc = Blt_GetOpFromObj(interp, nComboOps, comboOps, BLT_OP_ARG1, objc,
1760 objv, 0);
1761 if (proc == NULL) {
1762 return TCL_ERROR;
1763 }
1764 result = (*proc) (tbPtr, interp, objc, objv);
1765 return result;
1766}
1767
1768#endif
1769
Note: See TracBrowser for help on using the repository browser.