BBC BASIC
Programming >> User Interface >> Text Edit
http://bbcbasic.conforums.com/index.cgi?board=ui&action=display&num=1519796122

Text Edit
Post by KenDown on Feb 28th, 2018, 04:35am

I'm putting this here because the example text editor is put in the Windows directory.

Is it possible to modify the operations of the text editor? For example, could one alter the colour of a word or two, make a word clickable, and so on?

I'm guessing that the answer is No, but just thought I'd ask.
Re: Text Edit
Post by Richard Russell on Feb 28th, 2018, 08:20am

on Feb 28th, 2018, 04:35am, KenDown wrote:
Is it possible to modify the operations of the text editor? For example, could one alter the colour of a word or two, make a word clickable, and so on?

You may want to investigate the Windows 'Rich Edit' control which is much more flexible (but at the same time more complicated!) than a regular Edit control. Certainly changing the colour of a word is straightforward - I use that feature for Syntax Colouring in LBB's right-hand pane.

There's a Wiki article on using Rich Edit controls in BB4W, but it only skims the surface of what is possible.

(Please note that the wiki is closing down later this year).

Richard.
Re: Text Edit
Post by KenDown on Feb 28th, 2018, 10:26am

Thanks. I am already using RichEdit so that I can have UTF-8 characters like Bulgarian.

I have read the Wiki, though I thought it was only for dialog boxes (now you'll tell me that a text window is really just a dialog box!) so I'll have another look at it. Thanks.

However it is the clicking on something that I would really like: I bring up a Bible chapter and would like to click (or double-click) on a verse and have it inserted elsewhere in the program.
Re: Text Edit
Post by Richard Russell on Feb 28th, 2018, 11:27am

on Feb 28th, 2018, 10:26am, KenDown wrote:
However it is the clicking on something that I would really like

You can always use a 'low tech' approach. Poll the mouse using the MOUSE statement and when you see the left button pressed get the current caret position from the Rich Edit control (because clicking will have moved the caret there!).

Trivial, but because it relies on polling there is a risk that a mouse click may be missed if it is done too quickly (and by the same token a double-click will be detected even less reliably). But because it's so simple probably worth a try.

Richard.

Re: Text Edit
Post by KenDown on Feb 28th, 2018, 9:00pm

Thanks. I'll try it - but I'd need to read what is at the caret. Can that be done without reading the whole text edit box?
Re: Text Edit
Post by Richard Russell on Feb 28th, 2018, 9:36pm

on Feb 28th, 2018, 9:00pm, KenDown wrote:
Thanks. I'll try it - but I'd need to read what is at the caret. Can that be done without reading the whole text edit box?

I had assumed that the contents of the edit control were there because you had put them there. You referred to "bringing up a Bible chapter" which I assumed was 'set in stone' (if that isn't a mixed metaphor) and therefore knowing where the caret is would allow you easily to work out which paragraph etc. it is in.

If that's not the case, for example because the user is allowed to edit the text, then as you say you'd have to read the text in the vicinity of the caret. That shouldn't be difficult though, for example you could presumably use EM_LINEFROMCHAR and EM_GETLINE to read the line containing the caret.

But I'm not an edit control expert. You'll make much faster progress by scanning the list of EM_* messages yourself. I don't know how adept you are at finding things at MSDN but it's a skill which is absolutely invaluable. Asking here is at best a very poor substitute.

Richard.
Re: Text Edit
Post by KenDown on Mar 1st, 2018, 06:20am

The difficulty, as I see it, is this: the contents of the text edit window are held in an array line$() with one verse per array entry. So far so simple.

In the window, however, each verse takes up a different number of lines, from a single line for "Jesus wept" to multiple lines for some of St Paul's more prolix statements. Knowing that the cursor is on line 72 of the edit window does not tell me which verse is involved.

That is why I say that I would need to read the contents of the window - preferably the couple of lines above and below the cursor so that I could find the verse number.

Thanks for the tip about EM_ messages, though I don't anticipate much success. No doubt there are folk who can read the stuff on the MSDN site like others read the Sun newspaper, but I am not one of them. I get only the vaguest of ideas of what each parameter means from what is, essentially, gobbledy-gook. If anyone wants to write a best-seller, a translator for MSDN conventions would probably be it!

I will, however, try to make sense of the two calls you mention.

Many thanks for your patience.
Re: Text Edit
Post by Richard Russell on Mar 1st, 2018, 09:52am

on Mar 1st, 2018, 06:20am, KenDown wrote:
Knowing that the cursor is on line 72 of the edit window does not tell me which verse is involved.

I suggested that you poll the mouse to see when the left button is pressed, and then read back the caret position. What you get back is a character index into the displayed text, i.e. it may say that the caret is at offset 1234 with respect to the start of the text; you can surely then easily find where in your original string array character 1234 is?

Getting the row and column coordinates of the caret requires extra work (for example using the EM_LINEFROMCHAR message) and I only suggested that you do that in the context of reading back the line containing the caret. As you say it's not useful when wanting to refer back to the array, so why do it?

Quote:
I get only the vaguest of ideas of what each parameter means from what is, essentially, gobbledy-gook.

The function prototypes at MSDN are described in C syntax, and anybody who can program in BASIC can easily understand simple C syntax because they are so similar. Really the only major difference, from the point of view of understanding MSDN, is that in C parameters have an explicit type (e.g. int myVar) whereas in BBC BASIC they are implicitly typed by means of a suffix character (e.g. myVar%).

Quote:
If anyone wants to write a best-seller, a translator for MSDN conventions would probably be it!

It exists, and has done for years! It's called API Viewer and can be freely downloaded. However (and it's a major issue) the plugin you need for BB4W was written by Jon Ripley and - at his own insistence - was available only from his website. Sadly Jon closed that website some time ago, and he seems to be uncontactable.

That means that anybody who downloaded and installed API Viewer (plus Jon's plugin) when they were available has a fully-automated Windows API to BBC BASIC translator, which is invaluable especially to people like you who find it a struggle. Those that didn't now can't (legally) get hold of it.

Richard.

Re: Text Edit
Post by KenDown on Mar 1st, 2018, 2:48pm

Hmmm. I imagine that I can find out which element of the array contains character 1234 by repeatedly subtracting line lengths until ...

However looking through the Wiki I discovered that you have a routine for putting clickable links into a dialog box or the main output window. Can that be adapted to work with a rich text edit window? That would solve all the problems at once!
Re: Text Edit
Post by Richard Russell on Mar 1st, 2018, 3:59pm

on Mar 1st, 2018, 2:48pm, KenDown wrote:
Hmmm. I imagine that I can find out which element of the array contains character 1234 by repeatedly subtracting line lengths until ...

If that's an unacceptable overhead you're using the wrong programming language! Maybe something like Lisp would meet your exacting requirements, but with BBC BASIC that's the sort of thing you have to do all the time.

Quote:
Can that be adapted to work with a rich text edit window? That would solve all the problems at once!

A Google search found this. I would caution, however, that the click notifications are apparently sent as a WM_NOTIFY message so the warning in the documentation for SUBCLASS.BBC that "Some Windows messages, for example WM_NOTIFY, cannot be subclassed using this technique" may apply.

Richard.

Re: Text Edit
Post by DDRM on Mar 1st, 2018, 8:07pm

This link:

http://jonripley.com/bb4w/win32api/viewer/

Appears to be active?

Best wishes,

D
Re: Text Edit
Post by Richard Russell on Mar 2nd, 2018, 09:03am

on Mar 1st, 2018, 8:07pm, DDRM wrote:
This link: Appears to be active?

Amazing. Grab it while you can!

Richard.
Re: Text Edit
Post by KenDown on Mar 2nd, 2018, 4:32pm

Hooray!

After much head-banging I finally realised why links appeared in your window but not in mine - it was the "Link Window" instead of "RichEdit20"!

So now I have verse numbers appearing as links; so far so great. There are two problems: although by using a flag of &A00044 I have a scroll bar appearing on the right, no amount of clicking or dragging will scroll the window contents.

The second problem is that UTF-8 characters - such as Bulgarian - are not rendered.

I would be grateful for pointers to the solution, assuming that there is a solution!

bibledisp%=FN_createwindow(!bibledlg%,"Link Window","",4,4,230,558,99,&A00044,0)
Re: Text Edit
Post by Richard Russell on Mar 2nd, 2018, 4:51pm

on Mar 2nd, 2018, 4:32pm, KenDown wrote:
I would be grateful for pointers to the solution, assuming that there is a solution!

It's years since I've used a Link Control (I'm not even sure I've ever used one in a real program) and I've forgotten everything I ever knew about them. If it's not scrolling I fear the most likely explanation is that scrolling is not supported. sad

I'm pretty confident that Unicode text should work however. You would of course need to use WINLIB5U or WINLIB5W (depending on whether it's UTF-8 or UTF-16/UCS-2) rather than the plain WINLIB5(A) library, but otherwise I would be surprised if there is a problem.

I wonder, however, why you've seemingly abandoned the Rich Edit control because that does support scrolling (and colouring, which is something else you wanted I believe) and if you can successfully respond to the WM_NOTIFY messages from a Link Control there's no obvious reason why the same technique would not work for a Rich Edit control too.

I was thinking that you would need to use the SUBCLASS library, which is explicitly documented as not working with WM_NOTIFY messages, but the Link Window seems to work without it. I'd actually forgotten (no surprise there!) that I'd modified BB4W so that ON SYS handles WM_NOTIFY messages if you execute a *SYS 1.

Richard.

Re: Text Edit
Post by KenDown on Mar 8th, 2018, 10:28am

Weyhey! I've got part of it working - the hymn list, which is one hymn per line. Just in case anyone's interested:

ON SYS click%=FNsys(@msg%,@wparam%,@lparam%):RETURN

and then in the main program loop:

REPEAT
CASETRUE OF
WHEN!outlinedlg%>0:REM When the dialog box is open
IF@wparam%=&1000000THEN
!text%=256:SYS"SendMessage",hymnlist%,&C4,0,text%:
SYS"SendMessage",hymnlist%,&C9,-1TOline%
SYS"SetDlgItemText",!outlinedlg%,caret%,hymn$(line%)
ELSE
PROCoutlineclick(click%)
ENDIF
ENDCASE
UNTILFALSE

There is, of course, much else in the REPEAT...UNTIL loop.

I set caret% by clicking on a button beside the edit box into which the hymn will be written. (For some reason clicking on the edit box itself didn't work reliably.) PROCoutlineclick handles all other clicks in that particular dialog box.

Note that the value line% was coming out wrong until I realised that a couple of hymn titles were so long they were wrapping onto the next line. The solution was to change the final digits of the flag in the edit box creation call to &C4 instead of &44.

So thanks, Richard, for pointing me in the right direction. Now I can start battering my brains on the Bible verses!

Re: Text Edit
Post by KenDown on Mar 8th, 2018, 8:06pm

We make progress!

However I am stymied by a problem that I have noticed before. In RiscOs, if you had a menu that came up over a window and you clicked on a menu item, that was fine. With windows, however, it is as though you had clicked on the underlying window. It usually returns the correct value for the menu item, but it also acts as though you had clicked on the window at that point.

Is there any way round that, please?
Re: Text Edit
Post by Richard Russell on Mar 8th, 2018, 8:58pm

on Mar 8th, 2018, 8:06pm, KenDown wrote:
Is there any way round that, please?

Actually, in Windows, mouse clicks aren't seen by an underlying window, except in special cases when the window itself is transparent. However, if you use the MOUSE statement (or INKEY) you aren't testing for a click, you are testing the state of the mouse buttons directly.

So if you want to avoid seeing mouse clicks on a menu, use the ON MOUSE statement rather than the MOUSE statement. This is preferable anyway, because ON MOUSE will respond to a click however short it is, whereas to guarantee it will be seen by MOUSE you have to hold the button down for a minimum time period.

This really goes back to the earlier discussions in this thread, when you wanted to detect a click on a Rich Edit control. Then you were actually taking advantage of the fact that MOUSE sees the state of the buttons directly, so you could see the click even though it was on the Rich Edit window rather than your program's window.

So, to summarise, MOUSE sees the state of the buttons, irrespective of what window the mouse is over. ON MOUSE responds to a click on BBC BASIC's window, but won't see a click on a different window. ON SYS can see clicks on other windows, if they support that functionality (which fortunately we discovered a Rich Edit control does).

Richard.

Re: Text Edit
Post by KenDown on Mar 9th, 2018, 04:16am

Well, I've got it working - and thanks for all your help. I could not have done it without your patience and guidance.

Mind you, it is a horrible bodge: there are five nested IF...THEN...ENDIFs to test if the correct window is open, the mouse button is 4, the mouse x and the mouse y are in the right place and if nothing else (ie. a menu item) has been clicked on first. I'm sure that RiscOs wasn't nearly so complicated!

Incidentally, you were there at the time: is it true that the WIMP part of RiscOs was written by a games developer?
Re: Text Edit
Post by Richard Russell on Mar 10th, 2018, 10:47pm

on Mar 9th, 2018, 04:16am, KenDown wrote:
Incidentally, you were there at the time: is it true that the WIMP part of RiscOs was written by a games developer?

I don't know who that question was directed to. Not to me, I hope, since I know nothing about RISC OS (and I certainly wasn't "there at the time").

Richard.

Re: Text Edit
Post by KenDown on Mar 11th, 2018, 05:06am

My apologies. I thought you had been involved in the Archimedes.
Re: Text Edit
Post by Richard Russell on Mar 11th, 2018, 10:31am

on Mar 11th, 2018, 05:06am, KenDown wrote:
I thought you had been involved in the Archimedes.

No. My involvement with the project ended at the very beginning of Acorn's development of what became the Archimedes. I had some exchanges with Sophie over the enhancements she proposed to BBC BASIC (for example I was responsible for insisting that WHILE...ENDWHILE loops be nestable, which they weren't in her proposal), but I was 'off the case' before RISC OS itself came into being and I've never owned or used an Archimedes, RISC PC or any other Acorn ARM-based device.

You can detect some of this history in 'BBC BASIC for Windows' which has good compatibility with the old 6502 BBC BASIC, implements the new language constructs (like WHILE..ENDWHILE and CASE..ENDCASE) accurately, but has poor compatibility with the Archimedes and RISC OS. For example the MODE and SOUND statements, and the TINT function, work quite differently from those in Sophie's ARM BASIC.

Richard.

Re: Text Edit
Post by KenDown on Mar 11th, 2018, 4:58pm

Ah. I've had the original Archimedes and a RiscOS. Especially in RiscOS the WIMP and the facilities it offered a programmer were - in my opinion - far superior to Bill Gates' offering.

Oh well.

Now, getting back on-topic: I've got this thing working just fine: click on a verse and it gets inserted; select some text and just the selected text gets inserted. Yippee!

(I'm using the repeated subtractions method to find the correct array element pointed to by the EM_ call, as discussed earlier.)

Then I opened up the Hungarian Bible and it has all gone to pot. The problem, I suspect, is the LEN statement. Is that fully UTF-8 compatible? Is there some quirk in the text editor which returns the wrong value with UTF-8 text?

Any suggestions would be gratefully received.
Re: Text Edit
Post by Richard Russell on Mar 11th, 2018, 5:41pm

on Mar 11th, 2018, 4:58pm, KenDown wrote:
is the LEN statement. Is that fully UTF-8 compatible?

It depends what you mean by "compatible". LEN will of course work with UTF-8 strings the same as any other, but (by design) it returns the number of bytes in the string, not the number of characters. Given that LEN is commonly used to return the length of binary data (e.g. a bitmap stored in a string) that's how it should be, but it may not be what you want.

To get the number of characters in a UTF-8 string you could call the MultiByteToWideChar API, or you may find it easier to code it yourself in BASIC (if it's fast enough for your needs).

Richard.

Re: Text Edit
Post by KenDown on Mar 11th, 2018, 7:10pm

The question, of course, is which does Windows return from the EM_GETLINE function - bytes or characters? I'm guessing it is characters, as clicking on verse 16 caused verse 10 to be selected!

As you say, I might have to go down the WideChar route (I already have the routine incorporated in the program, so that won't be too difficult).

Thanks.
Re: Text Edit
Post by KenDown on Mar 12th, 2018, 04:09am

Hmmmm. Famous last words!

FNulen was not too difficult, but the rest of the routine falls flat. http://bb4w.wikispaces.com/Counting+the+characters+in+a+Unicode+string mentions that WIDTH, TAB(x) and COUNT don't work with UTF-8, but you need to add that MID$() and LEFT$() (and, I presume, RIGHT$()) don't work either.

The trouble is that I don't see an easy alternative to MID$. I presume that extracting half a UTF character is not terribly useful!
Re: Text Edit
Post by Richard Russell on Mar 12th, 2018, 12:12pm

on Mar 12th, 2018, 04:09am, KenDown wrote:
you need to add that MID$() and LEFT$() (and, I presume, RIGHT$()) don't work either

Ahem, I don't need to do anything: it's a Wiki, the essence of which is that it's a user-editable resource. If you feel it would benefit from that addition, you add it! grin

Quote:
The trouble is that I don't see an easy alternative to MID$. I presume that extracting half a UTF character is not terribly useful!

I'm sure you're right that UTF-8 equivalents of the string slicing functions would occasionally be useful. If you end up writing such routines, please also add them to the Wiki page (or create another if you feel that is more appropriate).

Perhaps an entire UTF-8 library might be of value, if you fancy writing such a thing (although in that case I would urge you to make it OS-agnostic so it can be used with both BB4W and BBCSDL, and on all platforms).

Richard.

Re: Text Edit
Post by KenDown on Mar 12th, 2018, 3:58pm

It was more a generic "you" rather than you personally. I don't have the skill to write a UTF version of LEFT$, MID$ or RIGHT$, but in case it is of interest to anyone, here's the code I've come up with.

REM One verse per item in the array bibleln$()
SYS"SendMessage",bibledisp%,EM_GETSEL,^stpos%,^endpos%
REM Count the number of lines to the start character
i%=0
REPEAT
pos%+=FNulen(bibleln$(i%))
i%+=1
UNTILpos%+FNulen(bibleln$(i%))>stpos%
REM Count the number of characters in those lines
len%=0
FORj%=0TOi%-1:len%+=FNulen(bibleln$(j%))+1:NEXT
REM stpos% is set to the character before the cursor
REM hence the +1
p%=stpos%-len%+1
e%=endpos%-stpos%
REM Now sort out what is between the start and end
b$=""
REPEAT
REM If the selection is shorter than the verse
IFe%<FNulen(bibleln$(i%))THEN
REM Increase the byte count until it matches the
REM start character count
sp%=0
REPEAT
sp%+=1
UNTILFNulen(LEFT$(bibleln$(i%),sp%))>=p%
REM Increase the byte count until it matches the
REM end character count
ep%=0
REPEAT
ep%+=1
UNTILFNulen(MID$(bibleln$(i%),sp%,ep%))>=e%
b$+=MID$(bibleln$(i%),sp%,ep%)
e%-=FNulen(MID$(bibleln$(i%),sp%,ep%))
ELSE
REM IF the selection is longer than a single verse
REM Increase the byte count until it matches the
REM start character count
sp%=0
REPEAT
sp%+=1
UNTILFNulen(LEFT$(bibleln$(i%),sp%))=p%
REM b$=the rest of the verse from the start point
b$+=MID$(bibleln$(i%),sp%)+" "
REM The verse number is held in four bytes
e%-=(FNulen(MID$(bibleln$(i%),sp%)+" ")+4)
REM Increment the array pointer to the next verse
i%+=1
p%=4:REM To exclude the verse number
ENDIF
UNTILe%<=0
REM b$ now contains the required text
slide$(cursel%,slide%,6)=b$

I imagine you'll now come up with some genius method that will incorporate all the above in three lines of code. :-)