Page 1 of 2

Editable WebGadget library

Posted: Mon Aug 21, 2006 5:01 am
by zapman*
Thanks to Fr34k, I understood how the Interface of Internet Explorer works and how to use it through a WebGadget. I began to make a WYSIWYG Html Editor and created many procedure to rich this target.

You can download the open source resulting library at
http://www.rankspirit.com/downloads/wg_library.zip

This file includes about 60 procedures (with comments) to deal with the Internet Explorer Interface.

Posted: Mon Aug 21, 2006 8:09 am
by Flype
seems good zapman.
can you provide a small example, please ?

Posted: Mon Aug 21, 2006 8:18 am
by Dare
Looks promising! Thanks.

Second the request for a small example.

Posted: Mon Aug 21, 2006 1:26 pm
by zapman*
Here it is:

Code: Select all

XIncludeFile "WebGadgetExtras.pb"
;
ButtonWidth = 60 : ButtonHeight = 18 : ButtonLineHeight = ButtonHeight + 6
;
MyWindow=OpenWindow(#PB_Any,0,0,600,400,"WebgadgetLibrary demo",#PB_Window_ScreenCentered|#PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_TitleBar) 
If CreateGadgetList(WindowID(MyWindow))=0:End:EndIf
;
WebGadget = WebGadget(#PB_Any,0,0,WindowWidth(MyWindow),WindowHeight(MyWindow)-ButtonLineHeight,"about:blank")
;
HTMLSample$ = "<html><head></head><body><div align="+Chr(34)+"center"+Chr(34)+"><p>WebGadget Library demo</p><p>&nbsp;</p><p>By Fr34k And Zapman</p></div></body></html>"
SetDocumentHTMLContent(WebGadget,HTMLSample$)
;
HPos = 5
ButtonFind = ButtonGadget(#PB_Any,HPos,WindowHeight(MyWindow)-ButtonHeight-2,ButtonWidth,ButtonHeight,"Find")
HPos + ButtonWidth +10
ComboContent = ComboBoxGadget(#PB_Any,HPos,WindowHeight(MyWindow)-ButtonHeight-4,100,100)
AddGadgetItem(ComboContent,-1,"Element 0 infos")
AddGadgetItem(ComboContent,-1,"Element 1 infos")
AddGadgetItem(ComboContent,-1,"Element 2 infos")
AddGadgetItem(ComboContent,-1,"Element 3 infos")
AddGadgetItem(ComboContent,-1,"Element 4 infos")
AddGadgetItem(ComboContent,-1,"Element 5 infos")
AddGadgetItem(ComboContent,-1,"Element 6 infos")
AddGadgetItem(ComboContent,-1,"Element 7 infos")
SetGadgetState(ComboContent,0)
HPos  +102
ButtonDisplay = ButtonGadget(#PB_Any,HPos,WindowHeight(MyWindow)-ButtonHeight-2,ButtonWidth,ButtonHeight,">>Display")
HPos  + ButtonWidth + 10
ButtonEdit = ButtonGadget(#PB_Any,HPos,WindowHeight(MyWindow)-ButtonHeight-2,80,ButtonHeight,"Edit mode")
HPos  + 80 + 10
ButtonRankSpirit = ButtonGadget(#PB_Any,HPos,WindowHeight(MyWindow)-ButtonHeight-2,ButtonWidth,ButtonHeight,"RankSpirit")
HPos  + ButtonWidth + 40
MouseText = TextGadget(#PB_Any,HPos,WindowHeight(MyWindow)-ButtonHeight,130,ButtonHeight,"Last clic was over...")

EditMode$ = "Off"
Repeat  
  ;
  EventID = WaitWindowEvent()
  If  EventID = #PB_Event_CloseWindow
    Quit = 1
  ElseIf EventID=#WM_SIZE
    ResizeGadget(WebGadget,#PB_Ignore,#PB_Ignore,WindowWidth(MyWindow),WindowHeight(MyWindow)-ButtonLineHeight)
    ResizeGadget(ButtonFind,#PB_Ignore,WindowHeight(MyWindow)-ButtonHeight-2,#PB_Ignore,#PB_Ignore)
    ResizeGadget(ComboContent,#PB_Ignore,WindowHeight(MyWindow)-ButtonHeight-4,#PB_Ignore,#PB_Ignore)
    ResizeGadget(ButtonDisplay,#PB_Ignore,WindowHeight(MyWindow)-ButtonHeight-2,#PB_Ignore,#PB_Ignore)
    ResizeGadget(ButtonEdit,#PB_Ignore,WindowHeight(MyWindow)-ButtonHeight-2,#PB_Ignore,#PB_Ignore)
    ResizeGadget(ButtonRankSpirit,#PB_Ignore,WindowHeight(MyWindow)-ButtonHeight-2,#PB_Ignore,#PB_Ignore)
    ResizeGadget(MouseText,#PB_Ignore,WindowHeight(MyWindow)-ButtonHeight,#PB_Ignore,#PB_Ignore)
  ElseIf EventID = #WM_LBUTTONDOWN
    GetWindowRect_(GadgetID(WebGadget),re.RECT) 
    GetCursorPos_(pt.POINT)
    ;
    If pt\y<re\bottom
      pt\x - re\left
      pt\y - re\top
      x = pt\X
      y = pt\y
      GetElementInfosFromPoint(WebGadget,x,y,@Element.ElementInfo)
      SetGadgetText(MouseText,"Last clic was over: "+Element.ElementInfo\tagname)
      SetGadgetState(ComboContent,Element\Index)
    EndIf
    
  ElseIf EventID=#PB_Event_Gadget
    If EventGadget() = ButtonFind
      SearchString$ = InputRequester("Search","Enter the expression to search:","Zapman")
      If SearchString$
        FindInWebGadget(SearchString$,0,WebGadget)
      EndIf
    ElseIf EventGadget() = ButtonDisplay
      Index = GetGadgetState(ComboContent)
      If Index<0 : Index = 0 : EndIf
      GetElementInfosFromIndex(WebGadget,Index,Element.ElementInfo)
      tx$ = "TagPosition: "+Element\taglist+Chr(13)
      tx$ + "posX: "+Str(Element\offsetTop)+Chr(13)
      tx$ + "posY: "+Str(Element\offsetLeft)+Chr(13)+Chr(13)
      tx$ + "Content: "+ Element\outerHTML
      MessageRequester("Element 1 content",tx$,0)
    ElseIf EventGadget() = ButtonEdit
      If EditMode$ = "Off"
        EditMode$ = "On"
        SetGadgetText(ButtonEdit,"Browser Mode")
      Else
        EditMode$ = "Off"
        SetGadgetText(ButtonEdit,"Edit Mode")
      EndIf
      SetWebGadgetEditable(WebGadget,EditMode$)
    ElseIf EventGadget() = ButtonRankSpirit
      SetGadgetText(WebGadget,"about:<body><h1>Loading...</h1></body>")
      While WindowEvent() : Wend
      SetGadgetText(WebGadget,"http://www.rankspirit.com")
      SetWebGadgetEditable(WebGadget,EditMode$)
        
    EndIf
  EndIf
Until Quit

Posted: Mon Aug 21, 2006 6:29 pm
by Tranquil
Great!! many thanks for shareing!

Posted: Mon Aug 21, 2006 6:52 pm
by Denis
Excellent !

Posted: Tue Aug 22, 2006 12:49 am
by Dare
Hi zapman*,

Thanks again. This is really useful.

Edit:

This is weird. If I replace the HTMLSample$="html stuff" with

Code: Select all

HTMLSample$=""
ReadFile(2,"path\to\test.html")
While Not Eof(2)
  HTMLSample$ + ReadString(2)
Wend
CloseFile(2)
Then it runs only once in every (about) 5 times. The other times it just disappears. MessageRequesters show it doesn't even get as far as the structures in the "extras" file. (They don't pop up)

However if I don't do the read, or place something like:

Code: Select all

HTMLSample$="<html><title>A</title><head></head><body>OK</body></html>"
immediately after the read (thus negating the efforts of the read) then it always works!

Any ideas? (the test file is about 2k in size.)

Edit again:

Using

Code: Select all

FileBuffersSize(2,0)
makes it work nearly every time.

Is there a way to know if a file operation is really completed?


Edit yet again:

Okay, this is not the place for this question, I'll take it to coding questions as I don't think it has anything to do with this (except incidentally).

Posted: Tue Aug 22, 2006 6:18 am
by Bonne_den_kule
Use FlushFileBuffers()

Posted: Tue Aug 22, 2006 10:11 am
by Dare
Hi Bonne_den_kule,

Thanks for the suggestion.

Actually zapman* pointed me at the problem in a post in coding questions (I had threadsafe on to test a possible bug for someone and forgot to turn it off).

zapman*, this lib is really great, thanks - and for the good call. :)

Posted: Thu Oct 12, 2006 8:47 am
by Dare
Hi zapman*,

Just remembered this thread was around so decided to be a pain and post a question for you here.

Do you know how to insert HTML at a given point in the document using this approach? That is, insert actual HTML at the caret position so that it shows as html and not htmldecoded text?

(Please say yes - oh - and please show how!)

Thanks!

Posted: Fri Oct 13, 2006 1:06 am
by zapman*
Hi Dare

Using the example described in this topic, add a button and exec the following when you clic on this button:

Code: Select all

StringToPaste$ = "<h1>Just to try</h1>"
      #OLECMDID_PASTE = 13
      VarIn.variant\VT = #VT_BSTR
      VarIn\bstrVal = StringToBStr (StringToPaste$)
      WebGadget_Exec(WebGadget,#OLECMDID_PASTE,#OLECMDEXECOPT_DONTPROMPTUSER,@VarIn,@VarOut.variant)
      SysFreeString_(VarIn\bstrVal)
by that way, the "<h1>Just to try</h1>" code is not interpreted but just past "as it" into the page.

Is it what you were looking at?

Posted: Fri Oct 13, 2006 1:41 am
by Dare
Hi zapman*,

Thanks for the response!

Mate, sorry for the confusing request.

I can paste/insert text, what I am looking to do is insert html so the the html is encoded and behaves like HTML. So inserting something like "<b>BOLD</b>" would produce BOLD.

I know things like Bold, Italic, Fonts, colours and etc can be set, images can be inserted, etc, etc, using #IDM_constants and am doing that. However there are cases where I need to be able to insert chunks of html code and have them wysiwyg-ed.

I cannot work out how to get the html in there and make it act like HTML! I am in danger of going bald trying to do this. :)

Perhaps I need to get the caret or selection position, get the inner html (<body>) into a string, parse the string to locate caret/selection position, and then insert the desired code, cut the original selection (if selection), clear the doc, rewrite the updated string, reposition caret/selection. Fiddly.

There must be a cleaner way. I just cannot find it. :?

If you have the answer, great! But if you have to spend time chasing it, please don't, I don't want to waste your time. Just hoping you have the answer at your fingertips.


Thanks for your help so far.


PS: I believe there is a command PasteHTML available via MSHTML.DLL which I assume would do the job, however I don't know how to access/use it.

Posted: Fri Oct 13, 2006 3:38 am
by zapman*
Dear Dare,

I'm working on exactly the same problem as you since four weeks and i'm completely bald actually.

I tried a dozain of different solutions to get the user-selected (highlighted) html code, transform it by my own procedures and replace it inside the document. And now, I hate hate hate hate the Microsoft developpers who worked on IE Explorer. IE is probably the worst part of Windows in terms of liability and logic. Many functions are buggy and written with the 4th dimension's way of thincking. I'm not used to be critical about Microsoft but I'm badly training myself at this occasion.

Just as an example, when you try to get the html code matching with a selection using ANY of the IE functions, the code returned is not the "real" one. ie:
Suppose that the user has selected the words "some words" in a sentence as "some words of a sample text" included in a h1 tag. You expect to get something as

Code: Select all

 <h1>some words
but you will get

Code: Select all

 <h1>some words</h1>
(IE will "complete" the code before giving it to you).
So, when you want to transform this code and to reinsert it into your document, you will obtain something like

Code: Select all

 <h1>some words</h1> of a sample text</h1>
After all that pain, I personally concluded that there is not any "elegant" way of doing that and that the only reliable one is to do approx what you suggest:
get the caret or selection position, get the inner html (<body>) into a string, parse the string to locate caret/selection position, and then insert the desired code, cut the original selection (if selection), clear the doc, rewrite the updated string, reposition caret/selection
I'm actually making a last attempt to paste the transformed code inside the document instead of
clear the doc, rewrite the updated string, reposition caret/selection
but I have not finished.

Here are two of my procedures (not well tested) to do that:
(those procedures use my version of the WebGadgetExtra library which you can download using the link at the beggining of this topic)

Code: Select all

Procedure.s PutMyMarkupsAndGetTheElementCode(WebGadget) ; by Zapman
 TextRange.IHTMLTxtRange = GetTxtRange(WebGadget)
  If TextRange
    bstr.l = 0
    If TextRange\get_text(@bstr) = #S_OK And bstr And PeekS(bstr,-1,#PB_Unicode)
      SelectedText$ = PeekS(bstr,-1,#PB_Unicode)
      SysFreeString_(bstr)
      If TextRange\parentElement(@Element.IHTMLElement) = #S_OK And Element
        bstr.l = 0
        If Element\get_outerText(@bstr) = #S_OK And bstr And PeekS(bstr,-1,#PB_Unicode)
          ElementText$ = PeekS(bstr,-1,#PB_Unicode)
          SysFreeString_(bstr)
          MarkupServices.IMarkupServices = GetIMarkupServices(WebGadget)
          If MarkupServices
            MarkupServices\CreateMarkupPointer (@MarkupPointer1.IMarkupPointer)
            If MarkupPointer1
              MarkupServices\CreateMarkupPointer (@MarkupPointer2.IMarkupPointer)
              If MarkupPointer2
                If Trim(SelectedText$) = Trim(ElementText$) ; Selection matchs with the whole Element content -> Include the tags into the selection.
                  MarkupPointer1\MoveAdjacentToElement(Element,#ELEM_ADJ_BeforeBegin)
                  MarkupPointer2\MoveAdjacentToElement(Element,#ELEM_ADJ_AfterEnd)
                Else
                  MarkupServices\MovePointersToRange(TextRange,MarkupPointer1,MarkupPointer2)
                EndIf
                InsertedString1$ = "hereisthestartoftheoldcode"
                bStr = StringToBStr (InsertedString1$)
                MarkupServices\InsertText(bStr,Len(InsertedString1$),MarkupPointer1)
                SysFreeString_(bStr)
                InsertedString2$ = "hereistheendoftheoldcode"
                bStr = StringToBStr (InsertedString2$)
                MarkupServices\InsertText(bStr,Len(InsertedString2$),MarkupPointer2)
                SysFreeString_(bStr)
                MarkupPointer2\Release()
                If Element\get_outerHTML(@bStr) = #S_OK And bStr
                  Result$ = ReadBSTR(bstr)
                  SysFreeString_(bStr)
                  If FindString(Result$,InsertedString1$,0)=0 Or FindString(Result$,InsertedString2$,0)=0 ; one of the markups has been inserted before the entry tag of after the ending tag
                    If Element\get_parentElement(@Parent.IHTMLElement) = #S_OK And Parent
                      Element\Release()
                      Element = Parent
                      If Element\get_outerHTML(@bStr) = #S_OK And bStr
                        Result$ = ReadBSTR(bstr)
                        SysFreeString_(bStr)
                      EndIf
                    Else
                      Result$ = ""
                    EndIf
                  EndIf
                EndIf
              EndIf
              MarkupPointer1\Release()
            EndIf
            MarkupServices\Release()
          EndIf
        EndIf
        Element\Release()
      EndIf
    EndIf
    TextRange\Release() 
  EndIf
  ProcedureReturn Result$
EndProcedure
;
Procedure RemoveMyMarkupsAndSelectTextInside(WebGadget) ; by Zapman
  TextRange.IHTMLTxtRange = GetTxtRange(WebGadget)
  If TextRange
    MarkupServices.IMarkupServices = GetIMarkupServices(WebGadget)
    If MarkupServices
      If TextRange\parentElement(@Element.IHTMLElement) = #S_OK And Element 
        MarkupServices\CreateMarkupPointer (@MarkupPointerS1.IMarkupPointer)
        If MarkupPointerS1
          MarkupServices\CreateMarkupPointer (@MarkupPointerS2.IMarkupPointer)
          If MarkupPointerS2
            MarkupPointerS1\MoveAdjacentToElement(Element,#ELEM_ADJ_BeforeBegin) ; Extend TextRange to the whole Element content
            MarkupPointerS2\MoveAdjacentToElement(Element,#ELEM_ADJ_AfterEnd)
            MarkupServices\MoveRangeToPointers(MarkupPointerS1,MarkupPointerS2,TextRange)
            bStr = StringToBStr ("hereisthestartoftheoldcode")
            varOut.VARIANT\vt = #VT_BOOL
            If TextRange\findText(PeekS(bstr, -1, #PB_Unicode),$7FFFFFFF,0,@varOut) = #S_OK       ; look for the first markup
              MarkupServices\MovePointersToRange(TextRange,MarkupPointerS1,MarkupPointerS2)
              MarkupServices\Remove(MarkupPointerS1,MarkupPointerS2)                                                   ; remove my first markup
              bStr = StringToBStr ("hereistheendoftheoldcode")
              varOut.VARIANT\vt = #VT_BOOL
              If TextRange\findText(PeekS(bstr, -1, #PB_Unicode),$7FFFFFFF,0,@varOut) = #S_OK      ; look for the second markup
                MarkupServices\CreateMarkupPointer (@MarkupPointerE1.IMarkupPointer)
                If MarkupPointerE1
                  MarkupServices\CreateMarkupPointer (@MarkupPointerE2.IMarkupPointer)
                  If MarkupPointerE2
                    MarkupServices\MovePointersToRange(TextRange,MarkupPointerE1,MarkupPointerE2)
                    MarkupServices\Remove(MarkupPointerE1,MarkupPointerE2)                                              ; remove my second markup
                    MarkupServices\MoveRangeToPointers(MarkupPointerS1,MarkupPointerE2,TextRange)
                     TextRange\select()
                    MarkupPointerE2\Release()
                    Result = 1
                  EndIf ; If MarkupPointerE2
                  MarkupPointerE1\Release()
                EndIf ; If MarkupPointerE1
              EndIf ; If TextRange\findText(PeekS(bstr, -1, #PB_Unicode),$7FFFFFFF,0,@varOut) = #S_OK
            EndIf ; If TextRange\findText(PeekS(bstr, -1, #PB_Unicode),$7FFFFFFF,0,@varOut) = #S_OK
            MarkupPointerS2\Release()
          EndIf ; If MarkupPointerS2
          MarkupPointerS1\Release()
        EndIf ; If MarkupPointerS1
        Element\Release() 
      EndIf ; If TextRange\parentElement(@Element.IHTMLElement) = #S_OK And Element 
      MarkupServices\Release()
    EndIf ; If MarkupServices
    TextRange\Release() 
  EndIf ; If TextRange
  ProcedureReturn Result
EndProcedure

To use the PasteHTML command you must do something as:

Code: Select all

Procedure.s PutHTMLCodeAtCaretPos(WebGadget,HTMLCode$) ; by Zapman
  WebGadget_Exec(WebGadget,#OLECMDID_DELETE,#OLECMDEXECOPT_DODEFAULT,0,0) ; this line is necessary because PasteHTML doesn't always erase completely the old content (that's one of the IE bugs)
  TextRange.IHTMLTxtRange = GetTxtRange(WebGadget)
  If TextRange
    bstr = StringToBStr (HTMLCode$)
    TextRange\PasteHTML(ReadBSTR(bstr))
    SysFreeString_(bstr)
    TextRange\Release()
  EndIf
EndProcedure
Because of all the problems I got precedently, I'm not really confident with this PastHTML command. I'll test it.

Be strong, boy, the way is long!![/code]

Posted: Fri Oct 13, 2006 2:23 pm
by Dare
Hi zapman*,

lol, some good humour in your post! And as you're also struggling with this my 2 brain cells are feeling a little better about themselves.

Thanks for the code, I'll have a go with it.

I had a thought (dangerous thing to have happen). With my online wysiwyg code (dom, js) I can load another webpage, ctrl-A (select all), ctrl-c (copy), set the focus and insertion point on the wysiwyg and ctrl-P (paste). Inserts everything.

So ... I am going to try having a hidden 2nd webgadget, writing the insert stuff to that, copying to clipboard and then pasting to the main webgadget from the clipboard.

But all this tomorrow, currently have some other stuff to do.

Thanks for the help!

Posted: Thu Oct 25, 2007 12:31 pm
by Kwai chang caine
Hello at all

What a very great code 8)
That's exactely what i search for a long time ago. :D
Congratulation at the creator 8)

But i have a problem.
This code don't work whith all internet page :(
I tested it whith "http://www.google.fr" and it not run :(

For example the function FindInWebGadget() work fine with http://www.purebasic.com and don't work with "http://www.google.fr".

Il somebody know why ? :roll:
Is it my fault ? :oops:

Thanks.