JS/HTML troubles in WebGadget / RTF troubles in EditorGadget

Just starting out? Need help? Post your questions and find answers here.
Seymour Clufley
Addict
Addict
Posts: 1265
Joined: Wed Feb 28, 2007 9:13 am
Location: London

JS/HTML troubles in WebGadget / RTF troubles in EditorGadget

Post by Seymour Clufley »

I'm trying to make a specialised plain text editor - using RTF to achieve certain display effects. Over the last few weeks I have tried different approaches and have asked specific questions on this forum regarding each approach. At this stage, none of these approaches has worked and to be honest I'm feeling pretty demoralised.

Since this project has gone on long enough, I'm going to describe the whole thing here so that anyone interested knows exactly what I'm after, instead of just one aspect of it. Apologies if this seems overbearing, but I don't want you to get the wrong idea about what I'm trying to do.

This text editor is a plugin for an external program. As such, there could be up to 4 instances of it onscreen at once. (Four copies of the DLL are inside the program's folder and it summons them as and when it needs to.)

So what is specialised about this editor? The host program is used for building libraries of notes. These notes can be "linked" to each other. Initially I had an index file and the links between notes were recorded there, but then I thought it'd be better to allow the user to place the link to another note at the position they wanted it in the text. Since it would be long-winded to update the position of a link in the index file every time it was changed, I abandoned the index and now links are stored inside each note. When the note is loaded, the text editor replaces all links inside it with a clickable hyperlink. Ideally, I want these hyperlinks to be non-editable.

Let's say a note is opened and it has only one link. The link is to a note called "Extra Text". Here's how the text editor should look when it first opens the note:

Image

All the text is editable except for the hyperlink, which I want to be "locked". (And I can’t figure out how to use RTF LockText command!)

If the user clicks on the link to "Extra Text", this is what should happen:

Image

Again, all the text should be editable - both outside and inside the boxes. I have managed this in MS-Word using a table with one row and one column. The table is automatically sized to the width of the page, and it resizes automatically whenever a new line is created (or needed) inside the box.

Then they would click the hyperlink "EXPANDED: EXTRA TEXT" and the box would be collapsed (and the contents saved to the linked note's file).

All this would have to be done with PB code, catching hyperlink clicks inside the gadget.

The two images above are screenshots from MS-Word, where I have constructed exactly the visual effect I'm looking for (I didn't manage the collapsing, but since the text would have to be updated from file every time the user expanded a link, MS-Word code wouldn't be valid anyway). I used the test hyperlink target "www.hotmail.com" just for the sake of it.

Since Word clutters RTF files with unbelievable amounts of bumf, I copied the whole thing in collapsed and expanded states, then pasted it into WordPad and saved it as separate RTFs. This produces much simpler RTF code, which still displays correctly in both WordPad and Word. I then "injected" the RTF code into the EditorGadget. Here's how it shows the collapsed state:

Image

The first problem: the hyperlink is not displaying correctly (alias and URL are consecutive instead of "merged"). I've tried different ways of coding the hyperlink but the EditorGadget always displays it the same way. Does anyone know how to achieve this?

The second problem: the hyperlink is not centered.

Now say the user clicks the hyperlink. We inject the RTF code for the expanded state and it looks like this:

Image

As you can see, more problems:
  • Internal colour of table is not showing.
  • Table is not "wrapped", nor are linebreaks catered to. The table has been created with only one line in height
  • If you press RETURN inside the table, a new row is created instead of a new line. (See image below).
Image

These five problems are what I need help with.

Do you think these problems could be inherent in the EditorGadget's handling of RTF code?

Here is the RTF code for the collapsed state:

Code: Select all

{\rtf1\ansi\ansicpg1252\deff0\deflang2057\deflangfe2057{\fonttbl{\f0\fmodern\fprq1\fcharset0 Courier New;}}
{\colortbl ;\red255\green255\blue255;\red0\green0\blue255;\red255\green0\blue255;\red0\green93\blue186;}
\viewkind4\uc1\pard\f0\fs20 First line of normal text.\par
\par


{\field{\*\fldinst{HYPERLINK "http://www.hotmail.com/"}}{\fldrslt{-> \ul HIDDEN: EXTRA TEXT\ulnone  <-}}} \par \par

Second line of normal text.
}
and the expanded state:

Code: Select all

{\rtf1\ansi\ansicpg1252\deff0\deflang2057\deflangfe2057{\fonttbl{\f0\fmodern\fprq1\fcharset0 Courier New;}}
{\colortbl ;\red255\green255\blue255;\red0\green0\blue255;\red255\green0\blue255;\red0\green93\blue186;}
\viewkind4\uc1\pard\f0\fs20 First line of normal text.\par
\par

\trowd\trgaph108\trleft-108\trqc\trbrdrl\brdrs\brdrw20\brdrcf3 \trbrdrt\brdrs\brdrw20\brdrcf3 \trbrdrr\brdrs\brdr20\brdrcf3 \trbrdrb\brdrs\brdrw20\brdrcf3 \trpaddl108\trpaddr108\trpaddfl3\trpaddfr3
\clcbpat4\clbrdrl\brdrw20\brdrs\brdrcf3\clbrdrt\brdrw20\brdrs\brdrcf3\clbrdrr\brdrw20\brdrs\brdrcf3\clbrdrb\brdrw20\brdrs\brdrcf3 \cellx8414\pard\intbl\qc\cf1

{\field{\*\fldinst{HYPERLINK "http://www.hotmail.com/"}}{\fldrslt{-> \ul EXPANDED: EXTRA TEXT\ulnone  <-}}}
\f0\fs20\par
\pard\intbl\par
First line inside box.\par
Second line inside box.
\cell\row\pard\cf0\par
Second line of normal text.
}
And the PB code:

Code: Select all

Declare InjectRTF(rtf$)
Global EG.l

window=OpenWindow(#PB_Any,0,0,700,450,"test")
CreateGadgetList(WindowID(window))
EG=EditorGadget(#PB_Any,0,0,700,450)
SetGadgetColor(EG,#PB_Gadget_BackColor,#Black)
SendMessage_(GadgetID(EG), #EM_SETTARGETDEVICE, #Null, 0)

file$="test collapsed.rtf"
;file$="test expanded.rtf"

file=OpenFile(#PB_Any,file$)
If file
    While Not Eof(file)
        rtf$=rtf$+ReadString(file)+Chr(13)+Chr(10)
    Wend
    CloseFile(file)
EndIf

InjectRTF(rtf$)


Repeat
    Delay(5)
    If GetAsyncKeyState_(#VK_ESCAPE) : Break : EndIf
Until WindowEvent()=#PB_Event_CloseWindow



Procedure InjectRTF(rtf$)
  SetGadgetText(EG, rtf$)
  SetGadgetColor(EG,#PB_Gadget_FrontColor,#White)
EndProcedure

The text editor has additional functions not related to this problem, but which need to be accommodated for by the solution. The first four I have already achieved with two different approaches (the EditorGadget and the ScintillaGadget):
  • Automatic colouring of designated "keywords"
  • A custom right-click menu
  • Custom keyboard shortcuts
  • Normal behind-the-scenes editor functions (Get current word, current line etc.)
  • Lockable text for the hyperlinks
  • Possibly, multiple layers of tables (boxes within boxes within boxes, following the links between notes). However, I'll only attempt this if I can conquer one layer!
  • Possibly, graphics
  • All text needs to be fed into the editor whenever uses clicks to expand a hyperlink, since the linked notes could be getting updated dynamically.

Unless the problems mentioned can be overcome within the EditorGadget, a different tool will be needed. I've been considering my options...
  • Scintilla. I don’t really think this is an option. Whilst it’s very good for source code editing, Scintilla is not optimised for RTF and can’t do tables, graphics or text alignment. I would need to code extensions for each of these, and that’s far beyond my expertise. It would take huge amounts of time and effort to learn and do, and I can't believe there isn't a simpler solution.
  • WebGadget. I could do the whole thing in HTML and stream it into the WebGadget. Problem: I don't know how to do editable text in HTML, and everything has to function outwardly like a normal text editor (plus behind-the-scenes functions - ability to get current word, etc.)
  • RTF dll. This could be expensive, and if I'm not mistaken, 4 copies of it would be necessary IN ADDITION TO the 4 copies of the PB dll. So it seems pretty clumsy.
As you can see it’s a fairly complex problem – surprising since the goal is fairly simple. What should I do? Obviously the ideal course would be for the five problems with the EditorGadget to be solved.

PS. If you’ve read this far, thanks for reading!
Last edited by Seymour Clufley on Fri Oct 26, 2007 4:36 pm, edited 2 times in total.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Editable webgadget:

Code: Select all

OpenWindow(0, 0, 0, 512, 384, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
CreateGadgetList(WindowID(0))
WebGadget(0, 0, 0, 512, 384, "")
SetGadgetItemText(0, #PB_Web_HtmlCode, "<body contenteditable=true></body>")

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
  EndSelect
ForEver
Seymour Clufley
Addict
Addict
Posts: 1265
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post by Seymour Clufley »

Thanks for the info, Trond.

Using the webgadget approach, I believe the main obstacles would be:
  • How to get current word in webgadget? (I can't find anything on the forums about this)
  • How to get current line in webgadget? (ditto)
  • How to make hyperlinks non-editable and non-movable? (using <SPAN>contenteditable=false</SPAN> doesn't stop them moving the hyperlink)
Presumably the solution to the scrolling problem would be:
  • get current scroll Y position
  • get current scroll height (total area)
  • add the text
  • get new scroll height
  • calculate equivalent of old scroll Y position based on new height
Does anyone know how to get the scroll height? I mean the total scroll height, the height of the "inner area".
Seymour Clufley
Addict
Addict
Posts: 1265
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post by Seymour Clufley »

Have re-titled the thread since the webgadget may be "the battleground" now, and edited previous post so only remaining problems are listed.

In particular, I would appreciate any help getting the current word/line from the webgadget. MSDN seems to have nothing about this either!
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post by tinman »

I could be way off here, and it certainly seems like a lot of work, but you might need to use the COM interfaces provided by the webgadget control. There doesn't appear to be any plain API functions for working with it. Have a look at:

WebBrowser Control for an overview
http://msdn2.microsoft.com/en-us/library/aa752040.aspx

IWebBrowser2 main interface for the web browser - you might be able to use a pointer to this interface as the result of creating the web gagdet?
http://msdn2.microsoft.com/en-us/library/aa752127.aspx

IHTMLDocument seems to be the starting point for working with the HTML document loaded in the web browser control.
http://msdn2.microsoft.com/en-us/library/aa752641.aspx

IHTMLEditHost will allow you to be called as a callback when an HTML element is resized or moved
http://msdn2.microsoft.com/en-us/library/aa704054.aspx

Of course, this assumes you can get some kind of handle to any of this stuff out of PureBasic's gadget. I don't know what type of value GadgetID() or the result of WebGadget() gives you.
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Post by DoubleDutch »

There may be a clue in events $8002, $113 and/or $118?

Code: Select all

#Web=10

OpenWindow(0, 0, 0, 512, 384, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
CreateGadgetList(WindowID(0))
WebGadget(#Web, 0, 0, 512, 384, "")
SetGadgetItemText(10, #PB_Web_HtmlCode, "<body contenteditable=true>test<b>test2</b></body>")

Repeat
  event=WaitWindowEvent()
  Select event
    Case #PB_Event_CloseWindow
      Break
		Case	#PB_Event_Gadget
			gadget=EventGadget()
     	Select gadget
     	
     	
     		Case	#Web
     			Debug("Web")
     		Default
     			Debug("Unknown gadget:"+Hex(gadget))
     	EndSelect
    Case	#PB_EventType_TitleChange
    	Debug("Web - Title change")
    Case	#PB_EventType_StatusChange 
    	Debug("Web - Status chnage")	
    Case	#PB_EventType_DownloadStart
    	Debug("Web - Download start")
    Case	#PB_EventType_DownloadProgress
    	Debug("Web - Download progress")
    Case	#PB_EventType_DownloadEnd
    	Debug("Web - Download end")
    Case	#PB_EventType_PopupWindow
    	Debug("Web - Popup window")
    Case	#PB_EventType_PopupMenu
    	Debug("Web - Popup menu")
    	
  	Case	$8002
  		Debug("Web? $8002 - when mouse moves?")
    Case	$118
    	Debug("Web? $118 - also when not editable") 	
    Case	$113
    	Debug("Web? $113 - also when not editable")
    	
		Case #PB_Event_Repaint
	;			Debug("Repaint")
		Case #PB_Event_ActivateWindow
  ; 		Debug("activate")
   		
   		
		Case	$200
;     	Debug("mouse move")
		Case	$201
;			Debug("Lbutton down")
		Case	$202
;			Debug("Lbutton up")
		Case	$203
;			Debug("Lbutton double click")
		Case	$204
;			Debug("Rbutton down")
		Case	$205
;			Debug("Rbutton up")
		Case	$206
;			Debug("Rbutton double click")
		Case	$207
;			Debug("Mbutton down")
		Case	$208
;			Debug("Mbutton up")
		Case	$209
	;		Debug("Mbutton double click")
		Case	$20a
	;		Debug("Mouse wheel")
		Case	$20b
;			Debug("Ebutton down")
		Case	$20c
;			Debug("Ebutton up")
		Case	$20d
;			Debug("Ebutton double click")
    Default
;    	Debug("Event:"+Hex(event))
  EndSelect
ForEver
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post by tinman »

I just noticed another thread on the fourm here thanks to a new post to it. Editable web gadget http://www.purebasic.fr/english/viewtopic.php?t=23312. You might want to start there.
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Seymour Clufley
Addict
Addict
Posts: 1265
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post by Seymour Clufley »

Thanks Tinman, but I've already studied the Editable Webgadget library. Unfortunately there don't seem to be any functions there for getting the current word, or anything like that.

And as I said before, MSDN has nothing about this.

This could be the stumbling block. It's essential that the program can get the current word. I can get the current selection, but if the user just has the caret in the middle of a word and presses a keyboard shortcut (for thesaurus, etc.), I need to be able to get the word the caret is in.

And would you believe it - you can do this easily with the EditorGadget and Scintilla!
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post by tinman »

Clean this up, whack it into the edit webgadget code:

Code: Select all

Procedure.s GetCurrentWord(WebGadget) ; by Fr34k - modified by Zapman
  Result$ = ""
  DocumentDispatch.IDispatch = GetDocumentDispatch(WebGadget)
  If DocumentDispatch
    If DocumentDispatch\QueryInterface(?IID_IHTMLDocument2, @Document.IHTMLDocument2) = #S_OK And Document
       If Document\get_selection(@Selection.IHTMLSelectionObject) = #S_OK  And Selection
            ;If Selection\createRange(@TextRangeDispatch.IDispatch) = #S_OK    
            If Selection\createRange(@TextRangeDispatch.IHTMLTxtRange) = #S_OK
              ; bstr_name = MakeBSTR("htmlText") to get html code  or  bstr_name = MakeBSTR("text") to get the text
              bstr_name = StringToBSTR("text")
              bstr_unit = StringToBStr("word")
              
              TextRangeDispatch\QueryInterface(?IID_IHTMLTxtRange, @TextRange.IHTMLTxtRange)
              
              foo.l = 0
              TextRange\expand("word", @foo)
              If #VARIANT_TRUE=foo
                  ; get the dispid of the property we want to get       
                  If TextRangeDispatch\GetIDsOfNames(?IID_NULL, @bstr_name, 1, 0, @dispid.l) = #S_OK 
                    arguments.DISPPARAMS\cArgs = 0
                    ;
                    ; now read the actual property. 
                    result= TextRangeDispatch\Invoke(dispid, ?IID_NULL, 0, #DISPATCH_PROPERTYGET, @arguments, @varResult.VARIANT, 0, 0) 
                    If result = #S_OK And varResult\vt = #VT_BSTR 
                      SelectedText$ = ReadBSTR(varResult\bstrVal) 
                      SysFreeString_(varResult\bstrVal) 
                      Result$ = SelectedText$
                    Else 
                      ErrorMessage(result) 
                    EndIf 
                  EndIf
              EndIf
              SysFreeString_(bstr_unit)
              SysFreeString_(bstr_name)
              TextRangeDispatch\Release() 
            EndIf        
          Selection\Release() 
        EndIf
        
      Document\Release()
    EndIf
    
    DocumentDispatch\Release()
  EndIf
  ProcedureReturn Result$
EndProcedure
Add this to the example at line 77:

Code: Select all

tx$ + "Current word: "+GetCurrentWord(WebGadget)
Run it, turn on edit mode, click on some text and then the "Display" button. It works for me but I've not tested it against any error conditions.
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Seymour Clufley
Addict
Addict
Posts: 1265
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post by Seymour Clufley »

That works a treat, Tinman! Where did it come from? It's nowhere else on the forums and Google doesn't recognise it! (I did a search in case there were any other useful procedures accompanying it - namely GetCurrentLine.)

However, GetCurrentWord was the main obstacle. Seems HTML is the way to go - thank god.

Incidentally I've found that if you have an editable page in the Webgadget and you use GetGadgetItemText(WG,#PB_Web_HtmlCode), it returns the original HTML that was loaded, not the current version with any edits. I don't know if this should be regarded as a bug. Either way, the problem can be circumvented using the equivalent procedure in the Webgadget Extras library - which does deliver the current state of affairs.

New problems are arising. The Webgadget's NavigationCallback procedure is invoked by right-clicking. Unfortunately, this conflicts with my own procedure for detecting right-click, causing the app to crash. Can anyone think of a way to avoid this? It could mean replacing the NavigationCallback procedure with a custom version (perhaps the one in Webgadget Extra library, which I'll try), or to abandon the right-click detect procedure if the text underneath is identified as a hyperlink. The latter, I don't know how to do.
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post by tinman »

It came from reading the MSDN and me hacking one of the existing procedures in the web gadget include :)

There;s a section here about controlling the context menu: http://msdn2.microsoft.com/en-us/library/aa741313.aspx
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Seymour Clufley
Addict
Addict
Posts: 1265
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post by Seymour Clufley »

Thanks for the info, Tinman. I will see if I can adapt it for acquiring the current line.

The next challenge is keyword colouring - or syntax highlighting. The editor needs to be able to colour certain keywords.

Now, initially I thought this could be done by simply adding the colour tags on either side of each keyword: <SPAN style=text-colour:#FF00FF>keyword</SPAN>. This process would be applied to the file when it was first loaded. But there's a problem with that: if the user types inside a keyword, its colour will be inherited by the new letters they are typing!

Unfortunately this is not like the EditorGadget where you can select text and alter it willy-nilly without affecting the user's experience. To have a constant loop running that finds all instances of a keyword and adds the tags to each, would mean constantly disrupting the user's experience by loading revised HTML into the Webgadget.

Does anyone know a trick for syntax colouring inside HTML?

On a sidenote, in investigating this I found a rather entertaining piece of code posted two years ago: Highlight/mark text in browser. Alas, set it to contenteditable=true and the same problems arise with colours being inherited.
Seymour Clufley
Addict
Addict
Posts: 1265
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post by Seymour Clufley »

tinman wrote:It came from reading the MSDN and me hacking one of the existing procedures in the web gadget include :)
Can you remember where you found the necessary info in the MSDN?
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post by tinman »

Seymour Clufley wrote:
tinman wrote:It came from reading the MSDN and me hacking one of the existing procedures in the web gadget include :)
Can you remember where you found the necessary info in the MSDN?
You probably want the IHTMLTxtRange: http://msdn2.microsoft.com/en-us/library/aa741548.aspx

And the IHTMLSelectionObject: http://msdn2.microsoft.com/en-us/library/aa768849.aspx

Probably more luck finding it than judgement. The code for getting access to this stuff was basically already written in the web gadget. I just had to modify to include the call to "expand".

That may be the problem in that it takes the current selection and creates a new text range object to get the current word. So if you add the HTML around it you may only be adding it to your copy and not the data that's in the document. Going by the way editor controls work I guess you'll need to find a method in the document to set the selection then replace it with your own text.
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Seymour Clufley
Addict
Addict
Posts: 1265
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post by Seymour Clufley »

I've been tinkering with that JavaScript code I found earlier. At first I thought it was just a background highlighter, so not fit for the purpose - but actually it can do both background and text colour. Here's the revised code:

Code: Select all

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html> 
<head> 
<title>Highlight test</title> 
<script language="JavaScript" type="text/javascript"> 
function HighlightText() { 
   var formInput = "th"; 
   var BodyHtml = document.getElementById("bodytext").innerText; 
   document.getElementById("bodytext").innerHTML = BodyHtml; 
   var RegText = new RegExp(formInput,"g") 
   var StyledText = "<span class='highlighter'>" + formInput + "</span>"; 
   BodyHtml = BodyHtml.replace(RegText,StyledText); 
   document.getElementById("bodytext").innerHTML = BodyHtml 
} 
</script> 
<style> 
.highlighter { 
  color: #cc00FF; 
  background-color: #ffffff
} 
</style> 
</head> 
<body> 

<input type="button" name="go" value="Go" onclick="HighlightText();"> 

<p>&nbsp;</p> 

<div id="bodytext" contenteditable=true>PureBasic is a programming language based on 
  established BASIC rules. The key features of PureBasic are 
  portability (Windows, AmigaOS and Linux are currently fully supported), 
  the production of very fast and highly optimized executables and, of 
  course, the very simple BASIC syntax. PureBasic has been created for 
  the beginner and expert alike. We have put a lot of effort into its 
  realization to produce a fast, reliable and system friendly language. 
</div> 
</body> 
</html>
Please save that as an HTML page and have a look at what it does. It scans the text and recolours all instances of "th". Presumably one could duplicate the important lines inside the HighlightText function so as to apply different colours to different strings.

But I need the revising to be done automatically whenever the text changes. I've had a look and "onChange" seems to be the JavaScript word for this. What I don't know is how to apply that to the main body text (and included boxes, where linked notes are displayed), so that the process is kicked off whenever the user types or pastes etc.

There are two other problems:

1. I'd like the scan to be case-insensitive.

2. The scan deletes linebreaks.

Can any JavaScripters lend a hand?
Post Reply