Hyperlink in webgadget calls PB procedure?
-
- Addict
- Posts: 1265
- Joined: Wed Feb 28, 2007 9:13 am
- Location: London
Hyperlink in webgadget calls PB procedure?
I've got HTML being streamed into the webgadget. When the user clicks a hyperlink, I want a PB procedure to run and do something with the hyperlink's target URL.
So far I've been trying to make this happen using a JavaScript procedure. The hyperlink calls the JS procedure, which in turn creates a file. PB is running a loop, checking each time for the existence of the file. If the file exists, PB deletes it and calls the procedure.
That method is hardly elegant. But it gets much worse. In order to create a file, JavaScript needs to use an ActiveX object. That yellow bar will appear when the user clicks the hyperlink!
Does anyone know any other ways to get a hyperlink to call a PB procedure? The URL of the hyperlink needs to be passed somehow to PB, so that it knows what it's dealing with.
Thanks for reading,
Seymour.
So far I've been trying to make this happen using a JavaScript procedure. The hyperlink calls the JS procedure, which in turn creates a file. PB is running a loop, checking each time for the existence of the file. If the file exists, PB deletes it and calls the procedure.
That method is hardly elegant. But it gets much worse. In order to create a file, JavaScript needs to use an ActiveX object. That yellow bar will appear when the user clicks the hyperlink!
Does anyone know any other ways to get a hyperlink to call a PB procedure? The URL of the hyperlink needs to be passed somehow to PB, so that it knows what it's dealing with.
Thanks for reading,
Seymour.
I think there's stuff here: http://www.purebasic.fr/english/viewtopic.php?t=16837
there is no sig, only zuul (and the following disclaimer)
WARNING: may be talking out of his hat
WARNING: may be talking out of his hat
-
- Addict
- Posts: 1265
- Joined: Wed Feb 28, 2007 9:13 am
- Location: London
I don't understand, why not use a navigation callback (PB 4.1 only) :
Code: Select all
Procedure NavigationCallback(Gadget, Url$)
Debug Url$
ProcedureReturn #True
EndProcedure
If OpenWindow(0, 0, 0, 600, 300, "WebGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) And CreateGadgetList(WindowID(0))
WebGadget(0, 10, 10, 580, 280, "http://www.purebasic.com")
SetGadgetAttribute(0, #PB_Web_NavigationCallback, @NavigationCallback())
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
I may look like a mule, but I'm not a complete ass.
-
- Addict
- Posts: 1265
- Joined: Wed Feb 28, 2007 9:13 am
- Location: London
After some research, it appears there is no way for a browser to silently create a file on the client's computer. One way or another, security blocks it. Therefore the only way seems to be the webgadget's navigation callback. I was reluctant to use this method because early experiments revealed complications. I'll describe them.
The webgadget is loading custom HTML. I've found that SetGadgetItemText causes a crash if used in a thread, so instead I'm saving the HTML to a file and then directing the webgadget to it using SetGadgetText. The clicking sound is a bugger, but better than a crash.
This custom HTML contains hyperlinks. The idea is that when the user presses a hyperlink, PB revises the HTML accordingly and the webgadget's contents changes. For example, the user clicks a hyperlink called "Create box" -> PB intercepts this and revises the HTML to include a box, then the webgadget is reloaded with the new HTML which is identical to the previous HTML except that it includes a box.
The trouble with using the navigation callback is with the return variable. You have to set it to #False to stop the webgadget from going to an invalid URL (since the hyperlink is just a code, signalling PB to do something). However, having set it to false, SetGadgetText is also blocked. It treats reloading by code the same way it treats reloading by user interaction: as a click.
I've tried to work around this by setting global variables that the main loop picks up on, but the problem persists.
To recap, the problem is: stopping the webgadget from responding to a hyperlink click, and making it load a completely different file (that has to be created after the click).
Can anyone think of a workaround?
The webgadget is loading custom HTML. I've found that SetGadgetItemText causes a crash if used in a thread, so instead I'm saving the HTML to a file and then directing the webgadget to it using SetGadgetText. The clicking sound is a bugger, but better than a crash.
This custom HTML contains hyperlinks. The idea is that when the user presses a hyperlink, PB revises the HTML accordingly and the webgadget's contents changes. For example, the user clicks a hyperlink called "Create box" -> PB intercepts this and revises the HTML to include a box, then the webgadget is reloaded with the new HTML which is identical to the previous HTML except that it includes a box.
The trouble with using the navigation callback is with the return variable. You have to set it to #False to stop the webgadget from going to an invalid URL (since the hyperlink is just a code, signalling PB to do something). However, having set it to false, SetGadgetText is also blocked. It treats reloading by code the same way it treats reloading by user interaction: as a click.
I've tried to work around this by setting global variables that the main loop picks up on, but the problem persists.
To recap, the problem is: stopping the webgadget from responding to a hyperlink click, and making it load a completely different file (that has to be created after the click).
Can anyone think of a workaround?
Well, a global variable wouldn't be my first choice; I'd prefer to send a message to the main window, but I thought I'd give it a try.
Run the following and click the 'Intoroduction' link and see it redirected to MSN instead of the Purebasic page :
I'm not sure if this is the kind of thing which will help you?
Run the following and click the 'Intoroduction' link and see it redirected to MSN instead of the Purebasic page :
Code: Select all
Global flag=0
Procedure NavigationCallback(Gadget, Url$)
If Url$ = "http://www.purebasic.com/index.php3"
flag = 1
ProcedureReturn #False
EndIf
ProcedureReturn #True
EndProcedure
If OpenWindow(0, 0, 0, 600, 300, "WebGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) And CreateGadgetList(WindowID(0))
WebGadget(0, 10, 10, 580, 280, "http://www.purebasic.com")
SetGadgetAttribute(0, #PB_Web_NavigationCallback, @NavigationCallback())
Repeat
If flag = 1
SetGadgetText(0, "www.msn.com")
flag = 0
EndIf
Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
I may look like a mule, but I'm not a complete ass.
Then don't use it in a thread. Simple, huh?The webgadget is loading custom HTML. I've found that SetGadgetItemText causes a crash if used in a thread, so instead I'm saving the HTML to a file and then directing the webgadget to it using SetGadgetText. The clicking sound is a bugger, but better than a crash.
You should obviously check the URL before returning true/false... And you should of course use SetGadgetItemText() instead of loading the text from disk.This custom HTML contains hyperlinks. The idea is that when the user presses a hyperlink, PB revises the HTML accordingly and the webgadget's contents changes. For example, the user clicks a hyperlink called "Create box" -> PB intercepts this and revises the HTML to include a box, then the webgadget is reloaded with the new HTML which is identical to the previous HTML except that it includes a box.
The trouble with using the navigation callback is with the return variable. You have to set it to #False to stop the webgadget from going to an invalid URL (since the hyperlink is just a code, signalling PB to do something). However, having set it to false, SetGadgetText is also blocked. It treats reloading by code the same way it treats reloading by user interaction: as a click.

Here's the code with boxes, no threads, no files, no global variables and basically everything you (say you) need (except the realization that maybe you should use one gadget for each box?)
Code: Select all
Procedure WebGadgetCallback(Gadget, Url.s)
Select Url
Case "pb:addbox"
A.s = GetGadgetItemText(0, #PB_Web_HtmlCode)
Pos = FindString(A, "<!-- box -->", 0)-1
A = Left(A, Pos) + "<br>Mike Tyson is a boxer" + Right(A, Len(A)-Pos)
SetGadgetItemText(0, #PB_Web_HtmlCode, A)
Default
Debug Url
EndSelect
ProcedureReturn 0
EndProcedure
OpenWindow(0, 0, 0, 512, 384, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
CreateGadgetList(WindowID(0))
WebGadget(0, 0, 0, 512, 384, "")
SetGadgetAttribute(0, #PB_Web_NavigationCallback, @WebGadgetCallback())
While GetGadgetState(0) = #PB_Web_Busy : WindowEvent() : Delay(10) : Wend
SetGadgetItemText(0, #PB_Web_HtmlCode, PeekS(?Page))
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
EndSelect
ForEver
End
DataSection
Page:
Data.s "<html><head></head><body> <a href='pb:addbox'>Add box</a> <!-- box --> </body></html>"
EndDataSection
-
- Addict
- Posts: 1265
- Joined: Wed Feb 28, 2007 9:13 am
- Location: London
Seymour wrote:The webgadget is loading custom HTML. I've found that SetGadgetItemText causes a crash if used in a thread
Unfortunately, not possible. This is a plugin for a non-PB program. If I don't use threads, the host program gets frozen until the Repeat..Until loop finishes.Trond wrote:Then don't use it in a thread. Simple, huh?
Besides, if SetGadgetItemText doesn't work in a thread even when SetGadgetText does work in exactly the same situation, then there is a bug with SetGadgetItemText. The solution isn't "don't use it in a thread", but "fix the bug".
Seymour wrote:The trouble with using the navigation callback is with the return variable. You have to set it to #False to stop the webgadget from going to an invalid URL (since the hyperlink is just a code, signalling PB to do something). However, having set it to false, SetGadgetText is also blocked. It treats reloading by code the same way it treats reloading by user interaction: as a click.
I would much prefer to use SetGadgetItemText, but as I say, until the bug is fixed with using it in a thread, I'm forced to load the html from disk.Trond wrote:You should obviously check the URL before returning true/false... And you should of course use SetGadgetItemText() instead of loading the text from disk.![]()
Thanks for the code, Trond. There are a few tricks in there that I might use.Here's the code with boxes, no threads, no files, no global variables and basically everything you (say you) need
That might be better. But for now, given that the main app is not written in PB, I need to confine all activity to one plugin window = one webgadget.(except the realization that maybe you should use one gadget for each box?)
Here's the code for using SetGadgetItemText() from a thread. I hope you don't have any further requirements that you haven't mentioned.
To see which thread you are in at any point, insert a Debug GetCurrentThreadId_().
Code: Select all
Procedure WebGadgetCallback(Gadget, Url.s)
Select Url
Case "pb:addbox"
A.s = GetGadgetItemText(0, #PB_Web_HtmlCode)
Pos = FindString(A, "<!-- box -->", 0)-1
A = Left(A, Pos) + "<br>Mike Tyson is a boxer" + Right(A, Len(A)-Pos)
SetGadgetItemText(0, #PB_Web_HtmlCode, A)
Default
Debug Url
EndSelect
ProcedureReturn 0
EndProcedure
Procedure Main(None)
OpenWindow(0, 0, 0, 512, 384, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
CreateGadgetList(WindowID(0))
WebGadget(0, 0, 0, 512, 384, "")
SetGadgetAttribute(0, #PB_Web_NavigationCallback, @WebGadgetCallback())
While GetGadgetState(0) = #PB_Web_Busy : WindowEvent() : Delay(10) : Wend
SetGadgetItemText(0, #PB_Web_HtmlCode, PeekS(?Page))
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
EndSelect
ForEver
EndProcedure
WaitThread(CreateThread(@Main(), 0))
End
DataSection
Page:
Data.s "<html><head></head><body> <a href='pb:addbox'>Add box</a> <!-- box --> </body></html>"
EndDataSection
- utopiomania
- Addict
- Posts: 1655
- Joined: Tue May 10, 2005 10:00 pm
- Location: Norway
I used the code below to check out the webgadget news in PB 4.10 yesterday, and it works fine. It basically shows you how to use the wegbadget callback as an eventhandler for a webgadget based user interface.Does anyone know any other ways to get a hyperlink to call a PB procedure? The URL of the hyperlink needs to be passed somehow to PB, so that it knows what it's dealing with.
Code: Select all
;-program notes
;-initialize
;-
enumeration
#WIN
#WEB
endEnumeration
global winW = 800, winH = 600
declare openMainWindow()
declare navigationCallback(id, url.s)
declare.s userInterfacePage1()
;-program entry
openMainWindow()
;-program event handler
repeat
event = waitWindowEvent()
select event
case #PB_EVENT_SIZEWINDOW
winW = windowWidth(#WIN)
winH = windowHeight(#WIN)
resizeGadget(#WEB, 0, 0, winW, winH)
case #PB_EVENT_CLOSEWINDOW
;-program exit
;-
end
endSelect
forever
procedure openMainWindow()
if openWindow(#WIN, 0, 0, winW, winH, "program template", $CF0001)
if createGadgetList(windowID(#WIN))
webGadget(#WEB, 0, 0, winW, winH, "")
;load the html
setGadgetItemText(#WEB, #PB_WEB_HTMLCODE, userInterfacePage1())
;callback to monitor navigation
setGadgetAttribute(#WEB, #PB_WEB_NAVIGATIONCALLBACK, @navigationCallback())
endIf
endIf
endProcedure
procedure navigationCallback(id, url.s)
url = lcase(url)
if findString(url, "object1_clicked", 1)
;handle event...
messageRequester("UI Event handler", "Object1 click event")
;cancel navigation
procedureReturn 0
else
;allow navigation
procedureReturn 1
endIf
endProcedure
procedure.s userInterfacepage1()
;html
html.s + "<html><head>" + #CR$
html + "<style type = 'text/css'>" + #CR$
html + " body{font-family:calibri; font-size:18px; font-weight:800}" + #CR$
html + "</style>" + #CR$ + #CR$
html + "<script language = 'vbscript'>" + #CR$
html + "sub object1_onMouseOver()" + #CR$
html + " object1.style.cursor = " + #DQUOTE$ +"hand" + #DQUOTE$ + #CR$
html + " object1.style.color = " + #DQUOTE$ +"#FFFFFF" + #DQUOTE$ + #CR$
html + " object1.style.backgroundcolor = " + #DQUOTE$ +"#FF8040" + #DQUOTE$ + #CR$
html + "end sub" + #CR$ + #CR$
html + "sub object1_onMouseOut()" + #CR$
html + " object1.style.cursor = " + #DQUOTE$ +"arrow" + #DQUOTE$ + #CR$
html + " object1.style.color = " + #DQUOTE$ +"#FF8040" + #DQUOTE$ + #CR$
html + " object1.style.backgroundcolor = " + #DQUOTE$ +"#FFFFFF" + #DQUOTE$ + #CR$
html + "end sub" + #CR$ + #CR$
html + "sub object1_onClick()" + #CR$
html + " window.location = " + #DQUOTE$ +"object1_clicked" + #DQUOTE$ + #CR$
html + "end sub" + #CR$
html + "</script>" + #CR$
html + "</head>" + #CR$ + #CR$
html + "<span id = 'object1'" + #CR$
html + " style = 'position:absolute; left:120; top:80; width:100; height:22; color:#FF0000'>" + #CR$
html + " <center>Object1</center>" + #CR$
html + "</span>" + #CR$
html + "</body></html>"
procedureReturn html
endProcedure
Last edited by utopiomania on Sun Nov 11, 2007 8:08 pm, edited 1 time in total.
- utopiomania
- Addict
- Posts: 1655
- Joined: Tue May 10, 2005 10:00 pm
- Location: Norway
You can use my code above to do that if you only take the time to check it out. Or, did I miss something?To recap, the problem is: stopping the webgadget from responding to a hyperlink click, and making it load a completely different file (that has to be created after the click).
BTW, just changed it to demo some other possibilities.