** Discussion ** One of the ''gotchas'' of text was recently discussed in the wiki's chatroom [[add wikit reference later]]. The topic was setting a variable to the contents of a text widget. [suchenwi]: I think the trailing newline is obligatory in text widgets. (For saving, one often writes [[$text get 1.0 end-1c]] (where both 1.0 and 0.0 refer to the first character of the first line - 2.0 is the first of the second!) [lvirden]: Richard, so there's always an additional newline (because I sure wouldn't want to save the data without a final newline - I've been seeing data loss here because someone's application fails to write out the last line of data if it isn't terminated. [suchenwi]: Right - the bullet-proof way is to check whether the last char is a \n, and if not, append one. [lvirden]: the gotcha is that the current implementation makes it difficult to determine whether the last newline was input by the user, or is being generated by the code. [suchenwi]: Yes - hence test the presence. But if a user types three or five newlines after the text, the "correct" number may not be preserved. Just avoid that a text file gets longer by one \n on every save... ---- [GPS]: In Tcl/Tk 8.3.4 I've discovered that deleting large amounts of text is very slow (> 30 seconds). When dealing with > 3k lines with many tags it can take quite a while. A solution that I came upon was to delete text in 40 line increments, followed by a final delete 1.0 end. I haven't been able to discover what in the Text widget's code is causing this problem, but I wanted to point this out just in case anyone else runs into it. [burns]: If you hide the text area before deleting the text, it will run significantly faster. I found that when using http://sockspy.sourceforge.net/sockspy.html%|%sockspy%|%, the clear button can take a long time, but by replacing ====== .out delete 0.0 end ====== with ====== grid remove .out .out delete 0.0 end grid .out ====== You get fast results. This does add the extra burden of having the clearout code know whether or not you are using pack/grid/whatever, but leads to a much better UI experience. ---- [RS]: just discovered that the Tk text widget has a surprising use on Windows(NT): Copying text from IE to an Outlook message will remove linefeeds. Pasting it first into a Tk text widget, then copying it from there, and pasting it into Outlook Just Works... so Tk can even fix some Windows bugs, albeit with manual effort involved... ---- Question: I have a text widget and a vertical scrollbar linked to it and new text is inserted e.g. ====== .a.scroll insert end "new line\n" ====== When lines are inserted the scrollbar will change (slider becomes shorter) but it will still show the first line. However I want the scrollbar to scroll to the bottom of the text so you could always read the last line. [RS]: Easy one - after inserting text at end, add the command ====== .a.scroll see end ====== ---- [MAK]: The text widget's ''search'' command directly provides most of the functionality that you'd need to implement common search dialogs - regexp support, case sensitivity, direction, etc. The one common thing it doesn't provide directly is matching on word boundaries. You can overcome this limitation fairly easily by putting your search into a loop and using the ''-count'' option, which returns the length of the matched range, and the widget's ''compare'' command with indexes using the ''wordstart'' and ''wordend'' index modifiers. Of course, you'll want to save a list of your partial matches so you know when to stop looping, since the search function automatically wraps. Pseudocode: ====== set partialMatches [list] set start "insert" set matchRange {} while {1} { if {$direction eq "backwards"} { set index "$start -1 chars" } else { set index "$start +1 chars" } # Perform search set start [eval $w search $switches -count length -- [list $pattern $index]] if {($start eq {}) || ([lsearch -exact $partialMatches $start] != -1)} { # No partial match or already saw this one break } # Compare matched range to word range set wordstart "$start wordstart" set wordend "$start wordend" if {[$w compare $wordstart eq $start] && \ [$w compare $wordend eq "$start +$length chars"]} { # Matched whole word - store range for later use and stop. set matchRange [list $start $wordend] break } # Didn't match whole word - store start point for loop # checking and continue searching from there. lappend partialMatches $start } ====== [Vince] comments that it does provide for searching on word-boundaries. Just use '\m' and '\M' in a regexp search pattern. [MAK]: But then you have to deal with quoting hell to make sure your search pattern doesn't contain things regexp will parse as a regexp pattern if you're doing a non-regexp search. If the user wants to search for, say, [[foo]] (literal, not regexp), then you have to deal with quoting the brackets. (On the other hand, the above might not work, since wordstart and wordend will probably both be considered to be the first bracket, rather than the first and second, respectively). The search command could certainly use an extra switch for this. :) [Vince]: I wouldn't exactly call it quoting hell in this case. A straightforward regsub does the trick: ====== regsub -all {[][\$?^|*+()\.\{\}\\]} $pat {\\&} pat ====== ''You call that straightforward?'' [Vince]: Yes, I do, ''when'' the above regsub is wrapped into a proc (''quote::Regfind'' is what I use). It seems a lot more straightforward and less error-prone than the 20 lines of pseudo-code above (which, according to the above text, might not work)! ====== .text search -regexp -- "\\m[quote::Regfind $word]\\M" $pos ====== [MAK]: Well, by "might not work" I meant "might not work as a user intended." To clarify: if you do a search in, say, DevStudio (and probably other MS apps) for "Foo()" and check off the "match whole word only" option, then it will not match "Foo()" even if it is in the file you're searching and it's surrounded by whitespace. Whether you'd consider that behavior wrong or a bug in DevStudio/etc. is a separate issue, but the behavior of the above pseudocode is consistent with that behavior. However, the RE method behaves that way too: ======none % set x "Foo()" Foo() % regexp {\mFoo\(\)\M} $x 0 ====== ...so the "might not work" bit applies to the RE method as well, and in the same way. It's certainly simpler number-of-lines-wise than the loop if that substitution indeed catches all of the RE syntax. Though I'm inclined to agree with the above that that pattern isn't all that straightforward. :P I find myself wondering if you've got extra backslashes in there. I do think your quoting is wrong on your .text search, though: ======none % regexp "\mFoo\M" "Foo" 0 % regexp {\mFoo\M} "Foo" 1 ====== (fixed the quoting above, thanks!) ---- Are there any attempts out there to get a text widget to display the contents of a very large (e.g. 1 gigabyte) file in a clever incremental way (so we only show what we currently need to show, and obviously only load from disk a few pieces of the file at a time!). How about being able to edit such a huge file yet only commit the changes in one go on a 'save' operation? [ulis], 2004-01-23: If you only need to display parts of a huge file, the obvious solution is [Virtuallist] that virtualizes the display and can 'forget' what is no more needed. ---- {a m a r n r at t a t a e l x s i dot c o dot in} 2003-05-02: Hi all, I have a problem in displaying a text in Tk text widget. Actually I am getting a string "fd6" from c function using ====== Tcl_SetVar(pInterp,"testfield",fd6,0); ====== If i print the variable fd6,I am able to get the contents of string.I want to set the contents of fd6 string to a text widget (I am able to set fd6 contents to label&entry because they have -textvariable option) I want to set contents of fd6 string to text widge(text widget doesn't have -text variable option). Please help me in this with a sample code. [sudhi]: Say `.text` is ur text widget, then '.text insert end $fd6' will put your variable in the text widget. hope this helps !! ---- I am trying to prepare a notepad editor in which I am not able to create an Undo command. I was told that Ver. 8.4 has an inbuilt command. how do I use that? [DKF]: Look at the ''edit'' subcommand of the text widget. Note that you need to turn it on to use it; it is turned off by default because many uses of thetext widget don't need this sort of thing. You could also look through the Tk demos; 8.4 includes a demonstration of how to use the undo/redo feature. ---- When using Tk text widgets, I've run into a weird issue with bindings: if I bind to a particular tag on the widget (`$text tag bind hyperlink {do something}`), then this all works fine, ''unless'' ''do something'' actually creates a new [toplevel] window. What happens then is the new toplevel is created (on top of everything), but then the default `text` widget binding for `` is activated (from the original click), and now the text widget gets the focus and is moved to the front (this is on Windows). Anyone have a good suggestion for how to stop the second binding from firing, but only under these circumstances? (I already have a 'break' in the tag binding, but that doesn't seem to affect the binding on the widget itself). '''This is only an issue if you've added a `` binding to the text widget which causes it to be raised. On Windows, the Tk `B1` binding gives the text widget the focus, and so it's that second binding which will trigger to then raise the window''' ---- [FW]: Why doesn't the `text` widget have a `-readonly` option, like [entry]? [KBK]: Primarily for historical reasons. But it isn't at all difficult to do a [Read-only text widget]. [FW] (much later): IMO, the answer is "there is, and it's `-state disabled`," being that its purpose is just the same. [PYK] 2014-05-24: Except that `-state disabled` removes some of the functionality that's often desired in a read-only text widget, e.g. selection, which is why there are [Read-only text widget%|%workarounds] . ---- Werner: I have created a text widget which contains some lines of text entry boxes in a line (see example below). Now it is possible (and how) to move the cursor between this text entry boxes, when the Up-/Down arrows keys are pressed ? ======none +-----------------------------------------------------+ | .-----------. .----------. .-----------. | | Track 1: |Chris Rea | | Title 1 | | | | | `-----------' `----------' `-----------' | | .-----------. .----------. .-----------. | | Track 2: |Chris Rea | | Title 2 | | | | | `-----------' `----------' `-----------' | : : : : : : ====== e.g., if the cursor stay in the box contains "Title 1" and I press the down-key then it should be set in the text box below which contains "Title 2".. I have no idea which Tk function can move the cursor (and input focus) between text entries. [PL]: see `[focus]`. Also, have you considered using the [TkTable] widget instead? Werner: Thanks for your hint. `[focus]` exactly does what I want. [TkTable] widget ? I don't know this (function ?). I use functions only from Tcl8.4/Tk8.4 Manual, BWidget and [BLT] extensions. MSH: Try the [tablelist] widget. It's a text widget based metawidget which allows direct entry into the table (PURE Tcl) excellent. PL: see [Tktable] for the TkTable widget. It's contained in the [ActiveTcl] distribution, and as it's written by [Jeffrey Hobbs], the '''Tcl guy''', it could be considered "informally standard", if there is such a phrase. ---- [NEM] 2004-01-24: Has anyone ever tried to separate the text widget internal representation (a B-Tree IIRC) from the rest of the code? This is about the only major thing I would like to see change in the text widget. For instance, I am currently writing a widget (in [snit]) to view/edit [XML] files in a simple word-processor--like view. Currently, all information is stored twice - once in the text widget, and once in a [tDOM] data structure. It would be really nice to be able to just tell the text widget to use the DOM tree instead of its own internal representation, i.e., having a clean separation of data and view. I looked at the code that implements the Tk text widget a while ago, but I got lost quite quickly. Anyone more knowledgeable have any good pointers as to how this might be done, or know anyone who's tried it in the past? [Vince] points out that with [TIP] [http://tip.tcl.tk/169%|%169] this has actually been done to a large extent in Tk 8.5a2. It's still not as separate as you describe, but it is closer. The basic need would be to have a pluggable interface to ''all'' the TkBTree* functions in tkTextBTree.c. I think the bigger work would be providing an implementation of all of those functions for your [tDOM] data structure, rather than the modifications to the text widget (which would just require using a function lookup table for all access). By the way, I think it would be a great idea, because it would also allow things like a text widget which just showed a portion of a large (gigabyte, say) file to be written relatively easily in Tcl (assuming this interface was exposed to Tcl). ---- [FW]: Is it possible to make a tag encompass the literal beginning of the text? For instance, if I do ====== .t tag add main 0.0 end .t tag config main -background red ====== on an empty text widget, the red background starts after the cursor. Is there any way to make it so the user starts typing directly into the tag? This is very useful if, say, you want to set the margin before someone starts typing. [KJN]: I don't think this is possible, but [Auto extending text widget tags] shows how you can create bindings to alter tag ranges when text is entered. Similar code might deal with your problem. ---- [MJL] 2006-10-03: I'm having problems deleting the last line of text from the widget. The best solution I came up with was: ====== .t delete "end - 2 chars - 1 lines" "end - 2 chars" ====== but I'm sure there must be a better way... ''did you try `.t delete {end-1c linestart} end`?'' ---- [LV] 2003-03-26: Anyone have an example of how to set a minimal size on a text widget - so that it cannot be resized any smaller? ---- [WIBNI] * Tk had the built-in ability to generate [PDF] from the text widget? It would more-or-less negate the need for a printing solution, as our apps could generate PDF and spawn an external program to print. Almost equally compelling would be to be able to take a text widget and convert it to valid html+css, but I like the idea of a single file PDF more. ---- [jnc] 2009-12-02: When getting text from the text widget it will always include a final newline, regardless if the user has entered one or not. To avoid this problem, you can simply use: ====== set value [.t get 1.0 {end-1 char}] ====== `$value` will now contain everything except the final newline. ---- [MG] was surprised to find recently that the default bindings for the Text widget copy hidden (elided) text, as well as visible text. The solution if you don't want this seems to be to change `[tk_textCopy]` to use `-displaychars`, with something like ====== proc tk_textCopy [info args tk_textCopy] [ string map [list get {get -displaychars}] [info body tk_textCopy]] ====== ---- '''xview moveto" behavior''' [Daniel] 2011-06-06: Confused by the text widget documentation regarding "xview moveto". Specifically: ====== pathName xview moveto fraction ====== adjusts the view in the window so that fraction of the horizontal span of the text is off-screen to the left. Fraction is a fraction between 0 and 1." From that description, a value of 1 should move all of the text off the screen to the left. No text should be visible. Yet, for me at least, specifying `$pathname xview moveto 1` does not move 100% of the horizontal span off screen at all. Instead it just moves it as much as is needed to make the right-most element visible. Probably a much more useful way for it to work. Still, unless I'm misreading, the documentation leads one to believe it would behave otherwise. ---- '''How to save text content when the application is closed/exited?''' You cannot fire a bind script on ``. Sure you can, as the text widget is not destroyed at the very moment of invocation of the destroy-binding. However, as the text widget documentation states: the text widget is in a "half dead" state. You cannot get the contents. Instead, a ====== wm protocol . WM_DELETE_WINDOW {your exitproc} ====== must be utilized to serve the desired purpose.