The plugin below illustrates using the new miniwindows concept in a plugin.
Miniwindows were released in version 4.34, see here for a copy:
http://www.gammon.com.au/forum/?id=8811
It captures help pages and displays them over the main output. It demonstrates using hyperlinks in a miniwindow. In this case one for "Close" to close the window, "Next" and "Previous" if there are multiple pages, and "Copy" to copy the text to the clipboard.
To use, copy between the lines and save as Aardwolf_Help.xml in your Plugins folder.
Miniwindows were released in version 4.34, see here for a copy:
http://www.gammon.com.au/forum/?id=8811
It captures help pages and displays them over the main output. It demonstrates using hyperlinks in a miniwindow. In this case one for "Close" to close the window, "Next" and "Previous" if there are multiple pages, and "Copy" to copy the text to the clipboard.
To use, copy between the lines and save as Aardwolf_Help.xml in your Plugins folder.
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<muclient>
<plugin
name="Aardwolf_Help_v2"
author="Nick Gammon"
id="01539e2770cfa90b9210867c"
language="Lua"
purpose="Shows help messages a popup window"
date_written="2008-07-28"
requires="4.34"
version="2.1"
save_state="y"
>
<description trim="y">
Redirects a help message to a mini window.
Commands:
closehelp --> dismiss help window (or click on Close hyperlink)
showhelp --> redisplay last help
</description>
</plugin>
<!-- Triggers -->
<triggers>
<trigger
enabled="y"
match="{help}"
script="help_redirect"
omit_from_output="y"
name="help_start"
sequence="100"
>
</trigger>
<trigger
enabled="y"
match="{/help}"
script="help_redirect"
omit_from_output="y"
name="help_end"
sequence="200"
>
</trigger>
<trigger
enabled="y"
match="There is no help with that keyword."
custom_colour="2"
script="no_such_help"
name="no_such_help"
sequence="5"
>
</trigger>
<trigger
enabled="n"
match="*"
script="help_redirect"
name="multi_line_help"
omit_from_output="y"
sequence="10"
>
</trigger>
</triggers>
<aliases>
<alias
name="help_alias"
script="get_help"
match="^help( .*)?$"
enabled="n"
sequence="100"
regexp="y"
ignore_case="y"
>
</alias>
<alias
name="remove_alias"
script="remove_cache"
match="remove help cache"
enabled="y"
sequence="100"
ignore_case="y"
>
</alias>
<alias
name="close_help"
script="close_help"
match="closehelp"
enabled="y"
sequence="100"
ignore_case="y"
>
</alias>
<alias
name="re_show_help"
script="re_show_help"
match="showhelp"
enabled="y"
sequence="100"
ignore_case="y"
>
</alias>
</aliases>
<!-- Script -->
<script>
<![CDATA[
HELP_EXPIRY_TIME = 60 * 60 * 24 * 2 -- time to expire help in seconds (2 days)
TOPIC_REGEXP = "[-%a%d]+"
require "commas" -- for trim function
require "serialize" -- needed to serialize table to string
require "checkplugin"
require "getlines"
text_colour = 0x000000
border_colour = 0x00D7FF
hyperlink_colour = 0xE16941
background_colour = 0xE7FFFF
button_line_height = 20
max_lines_to_show = 40
current_help_topic = nil
helps = {}
help_lookup = {}
help_time = {}
help_text_lines = {}
hyperlink_functions = {}
function OnPluginBroadcast (msg, id, name, text)
-- playing status
if id == "f5b05e8826711cdb0d141939" then
playing = text == "y"
end -- if
end
function remove_cache ()
helps = {}
help_lookup = {}
help_time = {}
ColourNote ("white", "blue", "Help topics removed from cache.")
end -- remove_cache
function Display_Line (line, text, id, colour)
local left = 5
local top = (line - 1) * font_height + 1
WindowText (win, id, text, left, top, 0, 0, colour)
end -- Display_Line
function close_help (name, line, wildcards)
WindowShow (win, false)
end -- close_help
function re_show_help (name, line, wildcards)
if #help_text_lines > 0 then
draw_help_lines (1)
else
ColourNote ("red", "", "No help recently received.")
end -- if
end -- re_show_help
function hyperlink_close ()
WindowShow (win, false)
end -- hyperlink_close
function draw_help_lines (start)
-- clear window
WindowRectOp (win, 2, 0, 0, 0, 0, background_colour)
-- DrawEdge rectangle
check (WindowRectOp (win, 1, 0, 0, 0, 0, border_colour))
check (WindowLine (win, 0, button_line_top, 0, button_line_top, border_colour, 0, 1))
local count = 0
if start < 1 then
start = 1
end -- too low
first_line_shown = start
for i = start, start + max_lines_to_show do
if i > #help_text_lines then
break
end
count = count + 1
Display_Line (count, help_text_lines [i], font_id, text_colour)
last_line_shown = i
if count >= max_lines_to_show then
break
end
end -- for
local right
local top = button_line_top + 2
WindowDeleteAllHotspots (win)
right = make_hyperlink ("Close", 10, top, hyperlink_close, "Dismiss this window")
if start > 1 then
right = make_hyperlink ("Previous", right + 10, top, hyperlink_previous, "Previous page")
else
right = right + WindowTextWidth (win, hyperlink_font_id, "Previous") + 10
end -- if
if last_line_shown < #help_text_lines then
right = make_hyperlink ("Next", right + 10, top, hyperlink_next, "Next page")
else
right = right + WindowTextWidth (win, hyperlink_font_id, "Next") + 10
end -- if
right = right + 50
right = make_hyperlink ("Copy", right + 10, top, hyperlink_copy, "Copy help to clipboard")
local old_ascent = WindowFontInfo (win, font_id, 2)
local new_ascent = WindowFontInfo (win, hyperlink_font_id, 2)
local ascent_diff = new_ascent - old_ascent
local msg = 'Type "closehelp" to dismiss.'
local msg_len = WindowTextWidth (win, font_id, msg)
WindowText (win, font_id, msg,
max_width - msg_len, top + ascent_diff, 0, 0, 0x82004B)
WindowShow (win, true)
end -- draw_help_lines
function hyperlink_next ()
draw_help_lines (last_line_shown + 1)
end -- hyperlink_next
function hyperlink_previous ()
draw_help_lines (first_line_shown - max_lines_to_show)
end -- hyperlink_next
function hyperlink_copy()
SetClipboard (table.concat (help_text_lines, "\r\n") .. "\r\n")
ColourNote ("yellow", "black", "Help for " .. current_help .. " now on Clipboard.")
end -- hyperlink_next
function mousedown (flags, hotspotid)
local f = hyperlink_functions [hotspotid]
if f then
f ()
end -- function found
end -- mousedown
function make_hyperlink (text, left, top, action, hint)
local height = WindowFontInfo (win, hyperlink_font_id, 1)
local right = left + WindowTextWidth (win, hyperlink_font_id, text)
local bottom = top + height
WindowAddHotspot(win, text,
left, top, right, bottom,
"", -- mouseover
"", -- cancelmouseover
"mousedown",
"", -- cancelmousedown
"", -- mouseup
hint,
1, 0)
WindowText (win, hyperlink_font_id, text, left, top, right, bottom, hyperlink_colour)
hyperlink_functions [text] = action
return right
end -- make_hyperlink
function show_help (topic)
if not topic then
ColourNote ("teal", "", table.concat (help_fallback, "\n"))
return
end
help_text_lines = {}
max_width = 0
for line in getlines (helps [topic]) do
table.insert (help_text_lines, line)
max_width = math.max (max_width, WindowTextWidth (win, font_id, line))
end -- for
help_line_count = #help_text_lines
lines_shown = math.min (help_line_count, max_lines_to_show)
button_line_top = lines_shown * font_height + 5
current_help = topic
-- recreate the window the correct size
WindowCreate (win,
0, 0, -- left, top (auto-positions)
max_width + 10, -- width
button_line_top + button_line_height, -- height
12, -- auto-position: center all
0, -- flags
background_colour)
draw_help_lines (1)
end -- show_help
function PrefixCheck (t, s)
for k, v in pairs (t) do
if string.match (k, "^" .. s) then -- prefix match, so "swo" matches "sword"
return k, v
end -- if name matches
end -- checking table
return nil -- not found
end -- PrefixCheck
function no_such_help (name, line, wildcards)
requested_topic = nil -- can't get help on that
current_help_topic = nil
requested_topic = nil
EnableTrigger ("multi_line_help", false) -- no more lines to go
end -- no_such_help
-- here if they type HELP <something>
function get_help (name, line, wildcards)
-- muck around finding what they want help on
-- eg. help 'hand to hand'
local topic = ""
if wildcards [1] then
topic = string.match (wildcards [1], "'(.-)'")
-- eg. help hand to hand
if not topic then
topic = trim (wildcards [1])
end -- no quotes
end -- some wildcard
-- eg. help
if topic == "" then
topic = "help"
end -- help on its own
-- lower-case for comparisons
topic = topic:lower ()
-- look for cached help
-- direct lookup? (exact match)
if help_lookup [topic] and help_time [help_lookup [topic]] and
(help_time [help_lookup [topic]] + HELP_EXPIRY_TIME) > os.time () then
show_help (help_lookup [topic])
return
end -- found direct match
-- scan for partial match
local k, v = PrefixCheck (help_lookup, topic)
if v and help_time [v] and
(help_time [v] + HELP_EXPIRY_TIME) > os.time () then
show_help (v)
return
end -- partial match
-- no match, or expired? request it
if not playing then
ColourNote ("red", "", "Not currently able to request help from Aardwolf.")
return
else
SendNoEcho ("help " .. topic)
end -- if
-- in case the help doesn't match what we asked for
requested_topic = topic
end -- get_help
-- here to add another help cross-reference
function insert_help_topic (s)
help_lookup [s:lower ()] = current_help_topic
return ""
end -- insert_help_topic
-- here when help file has fully arrived, cache it in memory
function cache_help ()
if not current_help_topic then
requested_topic = nil
ColourNote ("orange", "", table.concat (help_text, "\n"))
return
end -- no help topic arrived
-- one big block of text
helps [current_help_topic] = table.concat (help_text, "\n")
local topic = current_help_topic
-- insert quoted words
topic = string.gsub (topic, "'(.-)'", insert_help_topic)
-- insert single words
topic = string.gsub (topic, TOPIC_REGEXP, insert_help_topic)
help_time [current_help_topic] = os.time ()
-- if they typed "help mace" and got "help weapon" allow for that
if requested_topic then
-- is requested topic there now?
local k, v = PrefixCheck (help_lookup, requested_topic)
-- this is for cases like "help whip" which actually returns "help weapon"
if not v then
help_lookup [requested_topic] = current_help_topic
end -- not there
end -- wanted another word
-- don't do it again
requested_topic = nil
end -- cache_help
function capitalize (s)
return string.sub (s, 1, 1):upper () .. string.sub (s, 2):lower ()
end -- capitalize
-- help redirector
function help_redirect (name, line, wildcards, styles)
EnableTrigger ("multi_line_help", true) -- capture subsequent lines
-- should only get this on an error
if name == "help_end" then
current_help_topic = nil
requested_topic = nil
EnableTrigger ("multi_line_help", false) -- no more lines to go
return
end -- if help_end
if name == "help_start" then
current_help_topic = nil
help_text = {}
help_fallback = {}
elseif line == "{/help}" then
EnableTrigger ("multi_line_help", false) -- no more lines to go
cache_help ()
show_help (current_help_topic)
else
-- exclude tag lines
if not string.match (line, "^%{/?%a+%}$") then
line = string.gsub (line, "^{helpkeywords}", "")
local keywords = string.match (line, "^Help Keywords %: (.+)%.$")
if not current_help_topic and keywords then
current_help_topic = string.gsub (keywords, TOPIC_REGEXP, capitalize)
end -- have keywords line
if current_help_topic then
table.insert (help_text, (string.gsub (line, "\r", "")))
else
table.insert (help_fallback, (string.gsub (line, "\r", "")))
end -- have topic
end -- not a tag
end -- if
end -- function help_redirect
function OnPluginInstall ()
win = "z" .. GetPluginID ()
font_id = "fn"
hyperlink_font_id = "fh"
font_name = "Dina" -- the actual font
hyperlink_font_name = "Arial"
-- make win so I can grab the font info
WindowCreate (win,
0, 0, 1, 1, -- 1 x 1 pixel
1, -- position - irrelevant
0, -- flags
background_colour) -- background colour
check (WindowFont (win, font_id, font_name, 8, false, false, false, false, 1, 49)) -- normal
check (WindowFont (win, hyperlink_font_id, hyperlink_font_name, 9, true, false, true))
font_height = WindowFontInfo (win, font_id, 1) -- height
font_width = WindowFontInfo (win, font_id, 6) -- avg width
assert (loadstring (GetVariable ("helps") or "")) ()
assert (loadstring (GetVariable ("help_lookup") or "")) ()
assert (loadstring (GetVariable ("help_time") or "")) ()
if GetVariable ("enabled") == "false" then
ColourNote ("yellow", "", "Warning: Plugin " .. GetPluginName ().. " is currently disabled.")
check (EnablePlugin(GetPluginID (), false))
return
end -- they didn't enable us last time
OnPluginEnable () -- do initialization stuff
end -- OnPluginInstall
-- pull in telnet option handling
dofile (GetPluginInfo (GetPluginID (), 20) .. "telnet_options.lua")
function OnPluginConnect ()
TelnetOptionOn (TELOPT_HELPS)
end -- function OnPluginConnect
function OnPluginClose ()
-- if enabled
if GetPluginInfo (GetPluginID (), 17) then
TelnetOptionOff (TELOPT_HELPS)
end -- if enabled
end -- OnPluginClose
function OnPluginEnable ()
-- if we are connected when the plugin loads, it must have been reloaded whilst playing
if IsConnected () then
OnPluginConnect ()
end -- if already connected
-- see if we are playing at install time
playing = GetPluginVariable ("f5b05e8826711cdb0d141939", "playing") == "y"
end -- OnPluginEnable
function OnPluginDisable ()
TelnetOptionOff (TELOPT_HELPS)
end -- OnPluginDisable
-- save_simple is for simple tables that do not have cycles (self-reference)
-- or refer to other tables
function OnPluginSaveState ()
SetVariable ("helps",
"helps = " .. serialize.save_simple (helps))
SetVariable ("help_lookup",
"help_lookup = " .. serialize.save_simple (help_lookup))
SetVariable ("help_time",
"help_time = " .. serialize.save_simple (help_time))
SetVariable ("enabled", tostring (GetPluginInfo (GetPluginID (), 17)))
end -- function OnPluginSaveState
]]>
</script>
</muclient>