 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
keyboardfreak
Joined: 09 Oct 2004 Posts: 216 Location: Budapest, Hungary
|
Posted: Fri Mar 13, 2009 2:03 pm Post subject: Look up any information anywhere |
|
|
I found the hover help script a bit pushy in practice, so I created an other version keeping the GUI interface. It works from anywhere where you can select text, no javascript is needed.
In this version you select some word or expression, press a hotkey (by default: pause) and the GUI appears with the definition and stays open until you keep the mouse in it (with configurable distance threshold).
You can easily add new lookup sources following the examples given in the script. If you create new sources then please post them in this thread, so that everyone benefits from them.
The script requires COM.ahk and IE.ahk which you find here.
| Code: | ; Look information up anywhere by keyboardfreak
;
; $LastChangedDate: 2009-03-30 18:08:57 +0200 $
;
; http://www.autohotkey.com/forum/viewtopic.php?t=42005
;
; Contributors:
; Drugwash
;
#NoEnv
; OS version check for alternate options
if A_OSVersion in WIN_NT4,WIN_95,WIN_98,WIN_ME
w9x := true
; user config --------------------------------------------------------
; list of source names you want to use separated by |
; (e.g. Wikipedia|Google|IMDb)
;
; only those sources appear in the dropdown box which are present in
; this list and they appear in the given order
;
; if you leave it empty then all sources appear in the order they are
; created in the script
sources_to_use =
; time to wait after copy for the copied contents to appear on the
; clipboard (ms)
;
; set this to a non-zero value if the tool doesn't pick up the
; selected text when pressing the hotkey
sleep_after_copy := (w9x = true) ? 500 : 0
; delay after which the browser windows is updated when the expression
; or the source is changed
update_browser_delay = 1000
; width of the info window (pixels)
gui_width = 600
; height of the info window (pixels)
gui_height = 400
; width of margin around the browser window (pixels)
browser_margin = 5
; the mouse position is checked this often to see if the popup window
; should be hidden (ms)
delay_to_hide = 10
; if the mouse is further away than this distance from the info window
; then it is hidden automatically (pixels)
hide_distance = 50
; enable automatic popup of help window for selection (needs
; javascript support)
enable_automatic_popup := false
; add a checkbox to the gui which can be used to change to value of
; enable_automatic_popup on the fly
add_popup_control := false
; self explanatory
gui_background_color = 66CCFF
; background color of the little indicator box which appears if
; selection monitoring is enabled
selection_indicator_background_color = 66FF00
; size of the selection indicator box in pixels
selection_indicator_size = 15
; distance of selection indicator box from the mouse cursor in pixels
selection_indicator_distance = 10
; if the distance of mouse cursor from selection indicator box is
; larger than this value (in pixels) then the indicator is hidden
selection_indicator_hide_distance := selection_indicator_distance + 10
; put your settings for the above variables into this file if you
; don't want to modify the script every time a new version comes out
#include *i localconfig.ahk
; initialization ------------------------------------------------------
gui_id =
gui_title := "quick info lookup"
dummytext := "some dummy text"
sources_num = 0
current_source = 1
add_sources()
Gui, +LastFound -SysMenu +Owner
Gui, Color, % gui_background_color
Gui, Add, Text, section, Text:
Gui, Add, Edit, ys vText gTextChanged
list =
if (sources_to_use == "")
loop %sources_num%
{
name := sources%a_index%_name
if (list == "")
list = %name%
else
list = %list%|%name%
}
else
loop parse, sources_to_use, |
{
name := A_LoopField
if (list == "")
list = %name%
else
list = %list%|%name%
}
Gui, Add, DropDownList, ys Choose%current_source% vSource gSourceChanged, %list%
Gui, Add, Button,ys ggoback, Back
Gui, Add, Button,ys gpopout, &Pop out
if (add_popup_control)
Gui, Add, CheckBox, ys+5 gautopopup vAutopopup, Auto popup
; we need this as workaround for SlimBrowser
MouseGetPos, MoldX, MoldY
if (enable_automatic_popup)
{
if (add_popup_control)
GuiControl,,Autopopup, 1
gosub start_selection_monitoring
}
Gui 2: +Owner -Caption +AlwaysOnTop
Gui 2: Color, % selection_indicator_background_color
; hotkeys -------------------------------------------------------------
$esc::
if gui_visible
gosub guihide
else
send {esc}
return
pause::
oldclipboard := clipboard
clipboard := dummytext
sendinput ^c
Sleep, % sleep_after_copy
expression := clipboard
if (clipboard == dummytext)
expression =
clipboard := oldclipboard
gui_shown_automatically := false
gosub showgui
return
; subroutines and functions -------------------------------------------
showgui:
gosub hide_selection_indicator
PosGui(gui_width, gui_height) ; call the active monitor detection function
Gui, Show, x%gui_x% y%gui_y% w%gui_width% h%gui_height% , %gui_title%
DllCall("SetCursorPos", int, gui_cx, int, gui_cy)
guicontrol,,Text, %expression%
guicontrol focus, Text
sendinput {end}
if (expression <> "")
guicontrol focus, Source
if (gui_id == "")
{
top_margin = 30
IE_Init()
gui_id := WinExist(gui_title)
pweb := IE_Add(gui_id, browser_margin, browser_margin + top_margin
, gui_width - (2 * browser_margin)
, gui_height - (2 * browser_margin) - top_margin)
COM_Invoke(pweb, "Silent", True)
}
if (expression <> "")
load_url()
settimer hidegui, %delay_to_hide%
gui_visible := true
return
hidegui:
MouseGetPos x, y
if (x < -hide_distance
or x > (gui_width + hide_distance)
or y < -hide_distance
or y > (gui_height + hide_distance))
gosub guihide
Return
get_url()
{
global
return sources%current_source%_url . expression
}
load_url()
{
global
IE_LoadURL(pweb, get_url())
start_load_monitoring_if_needed()
}
start_load_monitoring_if_needed()
{
global
SetTimer wait_for_page_load, off
SetTimer check_if_new_page_is_loading, off
if (sources%current_source%_pagefix <> "")
SetTimer wait_for_page_load, 100
}
textchanged:
settimer textchanged_idle, % update_browser_delay
return
textchanged_idle:
settimer textchanged_idle, off
guicontrolget expression,,text
if (expression <> "")
{
if (sources%current_source%_suggestions)
{
func := "get_suggestions_" . sources%current_source%_name
result := %func%(expression)
}
else
result =
if (result == ""
or regexmatch(result, "\|.*\|") == 0) ; only one
load_url()
else
{
html := "<h3>Suggestions:</h3><ul id=""suggestionlist""></ul>"
IE_LoadHTML(pweb, html)
while (IE_ReadyState(pweb) <> 4)
{
sleep 10
}
Loop, parse, result, |
{
COM_Invoke(pweb, "document.parentwindow.execscript"
, "ul=document.getElementById('suggestionlist');"
. "li=document.createElement('li');"
. "ul.appendChild(li);"
. "a=document.createElement('a');"
. "li.appendChild(a);"
. "a.appendChild(document.createTextNode('" . A_LoopField . "'));"
. "a.href='http://en.wikipedia.org/wiki/" . A_LoopField . "'")
}
start_load_monitoring_if_needed()
}
}
Return
wait_for_page_load:
if (IE_ReadyState(pweb) == 4)
{
settimer wait_for_page_load, off
COM_Invoke(pweb, "document.parentwindow.execscript"
, "try{" . sources%current_source%_pagefix . "}catch(e){}")
settimer check_if_new_page_is_loading, 100
}
return
check_if_new_page_is_loading:
if (IE_ReadyState(pweb) <> 4)
{
settimer check_if_new_page_is_loading, off
start_load_monitoring_if_needed()
}
return
guihide:
gui, hide
gui_visible := false
SetTimer hidegui, off
SetTimer wait_for_page_load, off
SetTimer check_if_new_page_is_loading, off
; if GUI was shown automatically and current window is Opera or
; SlimBrowser, then cancel the selection, so that the GUI doesn't
; appear again automatically
if (gui_shown_automatically)
{
WinWaitNotActive ahk_class gui_id
WinGetClass class, A
if (class == "OpWindow")
send {esc}
else if class in SlimBrowser MainFrame
{
; SlimBrowser doesn't react to ESC in ANY way
; so we need to send a mouse click to cancel selection
SendInput {click, MoldX, MoldY}
sleep, 2*%sleep_after_copy%
}
}
return
GuiClose:
COM_Release(pweb)
Gui, Destroy
IE_Term()
return
SourceChanged:
SetTimer sourcechanged_idle, % update_browser_delay
return
SourceChanged_idle:
SetTimer sourcechanged_idle, off
GuiControlGet source_name,,source
loop %sources_num%
{
if (source_name == sources%a_index%_name)
{
current_source := a_index
break
}
}
load_url()
Return
goback:
IE_GoBack(pweb)
start_load_monitoring_if_needed()
return
popout:
gosub guihide
Run % IE_GetUrl(pweb)
return
check_selection_in_title:
MouseGetPos, McurX, McurY
if !w9x
{
if (A_TimeIdle < 500)
return
}
else
{
if ((McurX != MoldX) || (McurY != MoldY))
{
Tlast := A_TickCount
MoldX := McurX
MoldY := McurY
}
Itime := A_TickCount - Tlast
if (Itime < 500)
return
}
WinGetTitle title, A
pos := RegExMatch(title, "\{([^}]+)\}.*", match)
if (pos == 0)
{
gosub hide_selection_indicator
Return
}
MouseGetPos, MoldX, MoldY
expression := match1
selection_indicator_x := McurX + selection_indicator_distance
selection_indicator_y := McurY + selection_indicator_distance
Gui 2:Show, x%selection_indicator_x% y%selection_indicator_y% w%selection_indicator_size% h%selection_indicator_size% NoActivate
settimer monitor_mouse_cursor_for_selection_indicator, 10
return
start_selection_monitoring:
if w9x
Tlast := A_TickCount
SetTimer check_selection_in_title, 100
Return
autopopup:
enable_automatic_popup := !enable_automatic_popup
if (enable_automatic_popup)
gosub start_selection_monitoring
else
SetTimer check_selection_in_title, off
return
hide_selection_indicator:
Gui 2:Hide
SetTimer monitor_mouse_cursor_for_selection_indicator, off
Return
monitor_mouse_cursor_for_selection_indicator:
MouseGetPos x, y
if (x >= selection_indicator_x
and x < selection_indicator_x + selection_indicator_size
and y >= selection_indicator_y
and y < selection_indicator_y + selection_indicator_size)
{
gui_shown_automatically := true
gosub showgui
}
if (x < (selection_indicator_x - selection_indicator_hide_distance)
or x > (selection_indicator_x + selection_indicator_hide_distance)
or y < (selection_indicator_y - selection_indicator_hide_distance)
or y > (selection_indicator_y + selection_indicator_hide_distance))
gosub hide_selection_indicator
return
; MULTI-MONITOR DETECTION -----------------------------------------
PosGui(GW, GH) ; pass GUI size as parameters
{
; top-left corner and center of the GUI
; (the latter is used for positioning the mouse)
Global gui_x, gui_y, gui_cx, gui_cy
WinGetPos, AX, AY, AW, AH, A ; get active window coordinates
AcenH := AX + AW//2 ; get active window horizontal center
AcenV := AY + AH//2 ; get active window vertical center
SysGet, Tmon, 80 ; find total number of monitors
Loop, %Tmon%
{
; get working area for monitor %A_Index%
SysGet, Mon%A_Index%, MonitorWorkArea, %A_Index%
; get monitor horizontal center
Mcen%A_Index%H := Mon%A_Index%Left + (Mon%A_Index%Right - Mon%A_Index%Left) // 2
; get monitor vertical center
Mcen%A_Index%V := Mon%A_Index%Top + (Mon%A_Index%Bottom - Mon%A_Index%Top) // 2
if (AcenH <= Mon%A_Index%Right && AcenH >= Mon%A_Index%Left)
{
gui_x := Mcen%A_Index%H - GW//2
gui_y := Mcen%A_Index%V - GH//2
gui_cx := gui_x + GW//2
gui_cy := gui_y + GH//2
break
}
}
}
; sources ----------------------------------------------------------
add_sources()
{
add_source("Wikipedia"
,"http://en.wikipedia.org/wiki/Special:Search?search="
,"document.getElementById('column-one').style.display='none';"
. "document.getElementById('content').style.marginLeft=0"
, false)
add_source("Google"
,"http://www.google.com/search?ie=utf-8&oe=utf-8&hl=en&q="
,"document.getElementById('header').style.display='none';"
. "document.getElementById('tsf').style.display='none';"
. "document.getElementById('ssb').style.display='none'")
add_source("Google Define"
,"http://www.google.com/search?ie=utf-8&oe=utf-8&hl=en&q=define:"
,"document.getElementById('header').style.display='none';"
. "document.getElementById('tsf').style.display='none';"
. "document.getElementById('ssb').style.display='none'")
add_source("IMDb"
,"http://www.imdb.com/find?s=all&q="
,"document.getElementById('nb15').style.display='none'")
add_source("Youtube"
,"http://www.youtube.com/results?aq=f&search_query="
,"document.getElementById('masthead').style.display='none'")
add_source("Leo.de"
,"http://dict.leo.org/ende?lp=ende&lang=de&searchLoc=0&cmpType=relaxed&search="
,"document.getElementById('mainnavigation').style.display='none';"
. "document.getElementById('searchrow').style.display='none';"
. "document.getElementById('header').style.display='none';"
. "if(document.getElementById('subnavigation')){"
. "document.getElementById('subnavigation').style.display='none';}"
. "var tds=document.getElementsByTagName('td');"
. "for (var i = 0; i < tds.length; i++)"
. "if(tds[i].className == 'sidebar') tds[i].style.display='none'")
}
add_source(name, url, pagefix = "", suggestions = false)
{
global
sources_num++
sources%sources_num%_name := name
sources%sources_num%_url := url
sources%sources_num%_pagefix := pagefix
sources%sources_num%_suggestions := suggestions
}
get_suggestions_wikipedia(expression)
{
url := "http://en.wikipedia.org/w/api.php?action=opensearch&search=" . expression
tempfile := A_Temp . "\wikipedia_lookup_temp"
UrlDownloadToFile, %url%, % tempfile
Fileread suggestions, % tempfile
pos := RegExMatch(suggestions, "^.*\[(.+?)\].*", match)
if (pos == 0)
return ""
suggestions := match1
StringReplace, suggestions, suggestions, `"`,`", ^, All
StringTrimLeft suggestions, suggestions, 1
StringTrimRight suggestions, suggestions, 1
list =
Loop, parse, suggestions, ^
{
if (list == "")
list = %A_LoopField%
else
list = %list%|%A_LoopField%
}
return list
} |
Optional extension:
In browsers the text can simply be selected and the GUI pops up automatically.
The required javascript part can be installed as a bookmarklet, so that you activate it on a page and it can be used for quick lookup on that page (like reading a foreign text and looking up words):
| Code: | | javascript:void(z=document.body.appendChild(document.createElement('script')));void(z.language='javascript');void(z.type='text/javascript');void(z.src='http://www.autohotkey.net/~keyboardfreak/help.js');void(z.id='hoverhelp'); |
It can also be installed permanently (I use it this way). Opera can do it natively. On Firefox you can do it with GreaseMonkey and on IE with similar tools.
The feature can be enabled by setting enable_automatic_popup to true.
Last edited by keyboardfreak on Mon Mar 30, 2009 4:10 pm; edited 18 times in total |
|
| Back to top |
|
 |
SoggyDog
Joined: 02 May 2006 Posts: 783 Location: Greeley, CO
|
Posted: Tue Mar 17, 2009 3:19 pm Post subject: |
|
|
Not sure what's happening for me, but upon running this, I highlighted some text on a webpage and pressed Pause and it tried to search against the URL of the page. Then I highlighted some different text and it tried to search against the text I had just selected. Then I tried a third selection and it tried to search against the second one I tried. It's like it's always one search behind what I'm looking for. If I hit Pause twice, it catches up with the current selection. Shouldn't have to do that, though. _________________
SoggyDog
Dwarf Fortress:
"The most intriguing game I've ever played." |
|
| Back to top |
|
 |
keyboardfreak
Joined: 09 Oct 2004 Posts: 216 Location: Budapest, Hungary
|
Posted: Tue Mar 17, 2009 4:08 pm Post subject: |
|
|
| SoggyDog wrote: | | If I hit Pause twice, it catches up with the current selection. Shouldn't have to do that, though. |
It works fine for me. It simply sends a Ctrl-C and reads the clipboard variable. I think that's the standard way to get the selected text in Autohotkey. |
|
| Back to top |
|
 |
keyboardfreak
Joined: 09 Oct 2004 Posts: 216 Location: Budapest, Hungary
|
Posted: Tue Mar 17, 2009 4:11 pm Post subject: |
|
|
Added a few improvements:
Focus is put to the source dropdown box by default if the input expression is not empty. This way the desired source can be selected quickly with the cursor up/down keys or the scroll wheel.
The GUI has a title bar, so it can be moved out of the way if it obscures part of the page which you want to read while the window is open.
ESC can also be used to dismiss the window quickly. |
|
| Back to top |
|
 |
SoggyDog
Joined: 02 May 2006 Posts: 783 Location: Greeley, CO
|
Posted: Tue Mar 17, 2009 7:00 pm Post subject: |
|
|
This rev never successfully returns anything;
At least the first one returned something, even if it was one search behind.
I will look at the code at home when I have some time and try to figure out what's going on...
Could be something with my goofy box at work. _________________
SoggyDog
Dwarf Fortress:
"The most intriguing game I've ever played." |
|
| Back to top |
|
 |
keyboardfreak
Joined: 09 Oct 2004 Posts: 216 Location: Budapest, Hungary
|
Posted: Tue Mar 17, 2009 7:03 pm Post subject: |
|
|
Seems to work here. I'm running it as a standalone script.
Maybe you integrated it into a bigger script and some variables are clashing? |
|
| Back to top |
|
 |
SoggyDog
Joined: 02 May 2006 Posts: 783 Location: Greeley, CO
|
Posted: Tue Mar 17, 2009 7:24 pm Post subject: |
|
|
| keyboardfreak wrote: | Seems to work here. I'm running it as a standalone script.
Maybe you integrated it into a bigger script and some variables are clashing? |
Nope. Cut/Paste straight in to fresh new script.
As I alluded, there could be a conflict with something else on this box.
I can/will report back if it fails in my test environment at home. _________________
SoggyDog
Dwarf Fortress:
"The most intriguing game I've ever played." |
|
| Back to top |
|
 |
Drugwash
Joined: 07 Sep 2008 Posts: 921 Location: Ploiesti, RO
|
Posted: Wed Mar 18, 2009 2:07 am Post subject: |
|
|
I've changed a couple things in the hotkey section and it now appears to work more reliably, at least on my system. Here's the whole section, for convenience; I hope it works for everybody:
| Code: | pause::
dummytext := "some dummy text"
oldclipboard := clipboard
clipboard := dummytext
sendinput ^c
sleep, 500
expression := clipboard
clipboard := oldclipboard
if (expression == dummytext)
{
expression :=
return
}
gui_x := (ScreenRight - gui_width) / 2
gui_y := (ScreenBottom - gui_height) / 2
Gui, Show, x%gui_x% y%gui_y% w%gui_width% h%gui_height% , %gui_title%
mousemove % gui_width / 2, % gui_height / 2
guicontrol focus, Text
guicontrol,,Text, %expression%
sendinput {end}
if (expression <> "")
guicontrol focus, Source
if (gui_id == "")
{
top_margin = 30
IE_Init()
gui_id := WinExist(gui_title)
pweb := IE_Add(gui_id, browser_margin, browser_margin + top_margin, gui_width - (2 * browser_margin), gui_height - (2 * browser_margin) - top_margin)
}
if (expression <> "")
load_url()
settimer hidegui, %delay_to_hide%
gui_visible := true
return
|
BTW, dummytext doesn't need to be defined on every hotkey press; it can go to the autoexec section.
EDIT: would be nice if it detected multi-monitor environment and set GUI position according to active window position/monitor. Personally I have the browser on second monitor and the Lookup GUI always pops up on primary monitor. _________________ AHK tools by Drugwash |
|
| Back to top |
|
 |
SoggyDog
Joined: 02 May 2006 Posts: 783 Location: Greeley, CO
|
Posted: Wed Mar 18, 2009 3:37 am Post subject: |
|
|
@Drugwash
Thanks.
I just got on at home and started to take a look at this,
but your mods already took care of my problem
and the script is now working as expected.
Peace. _________________
SoggyDog
Dwarf Fortress:
"The most intriguing game I've ever played." |
|
| Back to top |
|
 |
keyboardfreak
Joined: 09 Oct 2004 Posts: 216 Location: Budapest, Hungary
|
Posted: Wed Mar 18, 2009 5:57 am Post subject: |
|
|
| Drugwash wrote: | | BTW, dummytext doesn't need to be defined on every hotkey press; it can go to the autoexec section. |
Yep, there is lots of room for code cleanup improvements. but I don't want to deal with them yet. I may add a a few more features first.
| Drugwash wrote: |
EDIT: would be nice if it detected multi-monitor environment and set GUI position according to active window position/monitor. Personally I have the browser on second monitor and the Lookup GUI always pops up on primary monitor. |
I don't have a multi monitor env, so I can't test it, so it's up to someone who has such a system to implement it and post the code to the thread for others. |
|
| Back to top |
|
 |
keyboardfreak
Joined: 09 Oct 2004 Posts: 216 Location: Budapest, Hungary
|
Posted: Wed Mar 18, 2009 6:00 am Post subject: |
|
|
| Drugwash wrote: | | I've changed a couple things in the hotkey section and it now appears to work more reliably, at least on my system. Here's the whole section, for convenience; I hope it works for everybody: |
Your changes also kill the feature when there is no selection detected and the window pops up with an empty edit field, so that the user can type himself some text in it.
Was it intentional? |
|
| Back to top |
|
 |
supergrass
Joined: 21 Feb 2007 Posts: 51 Location: Australia
|
Posted: Wed Mar 18, 2009 10:49 am Post subject: problem with original version |
|
|
I ran the code from the original post.
I like this idea a lot!
my problem was once an IE window was inside the gui it kept firing the lookup as I typed each letter so if I tried to lookup indian in wikipedia it would look for i then in etc. It also threw this error:
any thoughts? |
|
| Back to top |
|
 |
keyboardfreak
Joined: 09 Oct 2004 Posts: 216 Location: Budapest, Hungary
|
Posted: Wed Mar 18, 2009 11:48 am Post subject: Re: problem with original version |
|
|
| supergrass wrote: | | my problem was once an IE window was inside the gui it kept firing the lookup as I typed each letter so if I tried to lookup indian in wikipedia it would look for i then in etc. It also threw this error: |
No error here.
You should ask the guys in the Embed an Internet Explorer topic if they have an idea what kind of error it can be. |
|
| Back to top |
|
 |
Drugwash
Joined: 07 Sep 2008 Posts: 921 Location: Ploiesti, RO
|
Posted: Wed Mar 18, 2009 11:53 am Post subject: |
|
|
| keyboardfreak wrote: | | I don't have a multi monitor env, so I can't test it, so it's up to someone who has such a system to implement it and post the code to the thread for others. | OK, I'll try to do that sometime soon. | keyboardfreak wrote: | Your changes also kill the feature when there is no selection detected and the window pops up with an empty edit field, so that the user can type himself some text in it.
Was it intentional? | Well... sort of. Thing is, the GUI used to pop up empty more often than not, even when text was selected in the browser. Considering this script's aim is to look up selected text, I tried to correct this behavior.
I admit I didn't think of the possibility (or feature) to let the GUI pop up empty for manual look up, moreso when I have a handy toolbar option with multiple search engine support in SlimBrowser.
Couldn't really pinpoint the issue with not detecting/copying the selected text to the GUI, that's why I added the Sleep command which seems to do the trick; may be my slow system or something. Any feedback would be appreciated.
Will keep looking for a way to have it work correctly, manual look up included. But if you have a fix ready, please post it, I'll be happy to test here. Actually, I think simply commenting out the return command after the if condition would take care of the manual look up; haven't yet tested this. _________________ AHK tools by Drugwash
Last edited by Drugwash on Wed Mar 18, 2009 11:57 am; edited 1 time in total |
|
| Back to top |
|
 |
keyboardfreak
Joined: 09 Oct 2004 Posts: 216 Location: Budapest, Hungary
|
Posted: Wed Mar 18, 2009 11:55 am Post subject: |
|
|
I still use an older Autohotkey (1.0.47.04). Maybe you use a newer one and it may explain the differences you experience?
It shouldn't, but I'll install the newest AH sometime to try it out. |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|