Flua string parsing


Forum: Programming and Scripting
Topic: Flua string parsing
started by: mikshaw

Posted by mikshaw on Oct. 17 2005,17:23
I've always had trouble with string parsing....it's one of those things that seems like it would be simple, but i never quite get the syntax right.

I'm building a flua gui for the Fluxbox keys file, and having trouble separating the parts of a line.  I've tried various things, and the closer i get to what seems to work, the more unstable the script becomes (getting segmentation faults).

My current parser function (a big mess right now):
Code Sample
function get_line()
-- read from the selected keys file line
if display.value > 0 then
-- all keys
keys_string = gsub(display:text(display.value),":.*","")
--key_string = keys_string
if strfind(strlower(keys_string),"none ") then
mod_choice.value = 0
--new_key_string = gsub(keys_string,"NONE ","")
--else
--new_key_string = keys_string
--key_string = gsub(key_string,"NONE ","")
end

for modnum = 1,9 do
if strfind(keys_string,"Mod"..modnum.." ") then
mod_choice.value = modnum
--key_string = gsub(key_string,"Mod"..modnum,"")
--modnum = modnum+1
end
end

if strfind(strlower(keys_string)," shift ") then
shift.value = 1
--key_string = gsub(key_string,"Shift","")
else shift.value = 0 end
if strfind(strlower(keys_string)," control ") then
ctrl.value = 1
--key_string = gsub(key_string,"Control","")
else ctrl.value = 0 end
-- just the key with no modifiers
--key_string = gsub(keys_string,"* ","")
-- string including just the fluxbox and shell commands
com_string = gsub(display:text(display.value),".*:","")
if strfind(strlower(com_string),"execcommand ") then sh_command.value = gsub(com_string,"ExecCommand ","") else sh_command.value = "" end
--print("com_string="..com_string)
end
--key_string = strfind(keys_string,".* ",-1)
--print("key_string="..key_string)
end

Some of the comments are comments, and some are failed or in-progress tests.  I apologize if it's confusing....it's confusing to me too =o)

Essentially, this is what I'm trying...
Load a keys file into a hold_browser. When the user clicks a line, the contents of that line are loaded into a series of input fields and pulldown menus.  I've had little trouble grabbing Mod#, Shift, Control, and (partially) the command.  I haven't figured out yet how to load the flux_command into the chooser menu, but that's a future hurdle.  The big problem i'm having is grabbing the key without modifiers...at least in a way that doesn't crash.  It would be really nice if i could figure out a way to emulate awk '{print <num>}'

A sample keys file can look something like this:

Mod1 Tab :NextWorkspace
mod1 Shift TAB :prevworkspace  
NONE shift Control f1 :execCommand aterm -e poop
none control SHIFT F12 :ExecCommand aterm -e su -c "blah -five"

There is no set number of fields, no defined order of fields, and case is arbitrary.  I can deal with the case by using strlower(), and i can break up the string into pieces by breaking it down in stages (cutting key_string multiple times).  However, both of these processes seem to break flua after a few clicks.  I don't know if flua has difficulty with stopping what it's doing when the user clicks a new line (or if it doesn't stop at all?), or if i'm just a sloppy scripter.  Regardless, i've tried to slow down on the clicks and it still crashes, so my guess is that flua just doesn't like to set the same variable several times in a short time, or maybe I'm overlapping variables so nothing works.
While typing this I'm forming some ideas, but i'd still llove some input.  This is the most complicated task i've had so far (and it seemed so simple at the beginning).

Maybe I need to set initial variables, and return to them as soon as the user clicks anything?
Should I use a new variable each time a string is broken into a smaller part?

Thank you for any ideas.

For added clarity (or maybe added confusion), here is the complete script as it is now.  Its functionality is limited...it loads a keys file, and has rudimentary line-editing capability.  It does not write anything external...that feature will not be added until everything else works.  There are various print commands included, which i've been using to debug.
Code Sample
-- DEFAULTS
keysfile = getenv("HOME").."/.fluxbox/keys"
ww = 420        -- window width
wh = 300        -- window height
bw = 80        -- button width
bh = 20        -- button height
bvs = bh+5    -- button vertical spacing
bhs = bw+5    -- button horizontal spacing
Fl_Widget.initializers = {textfont = 15, labelfont = 15, labelcolor = 0}
Fl_Input_.initializers = {when = When.changed}
Fl_Button.initializers = {box = Boxtype.thin_up, down_box = Boxtype.thin_down}
Fl_Round_Button.initializers = {box = Boxtype.none, down_box=Boxtype.round_down, type = Buttontype.radio, align = Align.bottom}
Fl_Window.initializers = {box = Boxtype.thin_down, color = 15}

function set_line()
-- modify the keys file line
-- a=mod# b=Control c=Shift d=key e=fluxcommand f=shellcommand
if display.value > 0 then
a = mod_choice.text
if ctrl.value == 1 then b = " Control" else b = "" end
if shift.value == 1 then c = " Shift" else c = "" end
d = " "..key.value
e = " :"..fb_command.text
if fb_command.text == "ExecCommand" then f = " "..sh_command.value else f = "" end
display:set_text(display.value,a..b..c..d..e..f)
end
end

function get_line()
-- read from the selected keys file line
if display.value > 0 then
-- all keys
keys_string = gsub(display:text(display.value),":.*","")
--key_string = keys_string
if strfind(strlower(keys_string),"none ") then
mod_choice.value = 0
--new_key_string = gsub(keys_string,"NONE ","")
--else
--new_key_string = keys_string
--key_string = gsub(key_string,"NONE ","")
end

for modnum = 1,9 do
if strfind(keys_string,"Mod"..modnum.." ") then
mod_choice.value = modnum
--key_string = gsub(key_string,"Mod"..modnum,"")
--modnum = modnum+1
end
end

if strfind(strlower(keys_string)," shift ") then
shift.value = 1
--key_string = gsub(key_string,"Shift","")
else shift.value = 0 end
if strfind(strlower(keys_string)," control ") then
ctrl.value = 1
--key_string = gsub(key_string,"Control","")
else ctrl.value = 0 end
-- just the key with no modifiers
--key_string = gsub(keys_string,"* ","")
-- string including just the fluxbox and shell commands
com_string = gsub(display:text(display.value),".*:","")
if strfind(strlower(com_string),"execcommand ") then sh_command.value = gsub(com_string,"ExecCommand ","") else sh_command.value = "" end
--print("com_string="..com_string)
end
--key_string = strfind(keys_string,".* ",-1)
--print("key_string="..key_string)
end

-- MAIN WINDOW
w_main = Window{ww,wh, "fluxkeys.flua"}
-- MENU
mm = Menu_Bar{0,0,ww,25;textfont=9}

file_open = Menu_Entry{"&File/&Open..."}
function file_open:callback()
keysfile = fl_file_chooser("Open Keys File","keys*",keysfile)
display:load(keysfile)
display.value=1
line_num.value=display.value
get_line()
end

mm:add(file_open)

file_save = Menu_Entry{"&File/&Save"}
mm:add(file_save)

help = Menu_Entry{"&File/&Help"}
function help:callback()
w_help:show()
end
mm:add(help)

file_quit = Menu_Entry{"&File/&Quit"}
function file_quit:callback()
  exit(0)
end
mm:add(file_quit)

edit_new = Menu_Entry{"&Edit/&New Key"}
function edit_new:callback()
display:insert(display.value+1,"Mod1 Shift F12 :Reconfigure")
display.value=display.value+1
line_num.value=display.value
end
mm:add(edit_new)

edit_remove = Menu_Entry{"&Edit/&Remove Key"}
function edit_remove:callback()
display:remove(display.value)
end
mm:add(edit_remove)
-- MENU END

line_num = Int_Input{360,0,60,25,"line:"}
function line_num:callback()
if line_num.value > "" then display.value = line_num.value end
end

-- The main text field, containing the keys file itself
display = Hold_Browser{10,30,ww-20,200}
function display:callback()
line_num.value = display.value
get_line()
--debug
--print(display:text(display.value))
end

-- CHOOSE MODIFIERS
mod_choice = Choice{10,240,64,20}
mod_choice:add(Menu_Entry{"NONE"})
mod_choice:add(Menu_Entry{"Mod1"})
mod_choice:add(Menu_Entry{"Mod2"})
mod_choice:add(Menu_Entry{"Mod3"})
mod_choice:add(Menu_Entry{"Mod4"})
mod_choice:add(Menu_Entry{"Mod5"})
mod_choice:add(Menu_Entry{"Mod6"})
mod_choice:add(Menu_Entry{"Mod7"})
mod_choice:add(Menu_Entry{"Mod8"})
mod_choice:add(Menu_Entry{"Mod9"})
mod_choice.value = 0
ctrl = Round_Button{7,260,64,20;type=Buttontype.toggle}
ctrl_label = Box{10,260,20,20,"Control";align=Align.right}
shift = Round_Button{7,275,64,20;type=Buttontype.toggle}
shift_label = Box{10,275,20,20,"Shift";align=Align.right}

-- CHOOSE KEY
key = Input{80,240,80,20}

-- CHOOSE FLUXBOX COMMAND
fb_command = Choice{170,240,240,20}
fb_command:add(Menu_Entry{"ExecCommand"})
fb_command:add(Menu_Entry{"Reconfigure"})
fb_command:add(Menu_Entry{"Workspace1"})
fb_command:add(Menu_Entry{"Workspace2"})
fb_command:add(Menu_Entry{"Workspace3"})
fb_command:add(Menu_Entry{"Workspace4"})
fb_command.value = 0
sh_command = Input{170,265,240,20}


function mod_choice:callback()
--print(mod_choice.text)
set_line()
end
function fb_command:callback()
if fb_command.text == "ExecCommand" then sh_command:show() else sh_command:hide() end
--print(fb_command.text)
set_line()
end
--mod_choice.callback()
--fb_command.callback()

w_main:end_layout()


-- HELP WINDOW
w_help = Window{ww,wh, "fluxkeys help"}
author = Box{0,wh-bh,ww,20, "Lua FLTK script by mikshaw 2005"}
w_help:end_layout()

w_main:show()

Posted by mikshaw on Oct. 17 2005,19:54
I decided to start rewriting the get_line function from scratch.  So far no segfault, although i still have to figure out how to separate "F7" from "Mod1 Shift F7 :*" or None Control Shift F7 :*".

Also put the fb_command lines into a table so i don't have a huge number of redundant "fb_command:add" commands in there.

New get_line function, without the sigle-key detection.
"fluxbox_commands" is the comma-separated table of fluxbox commands, "BLANK", "ExecCommand", "Restart", "Workspace1", etc...
Eventually i hope to make fluxbox version-specific tables.
Code Sample
function get_line()
-- find parts of selected keysfile line

-- find Mod<num> or "None"
for modnum = 1,9 do
if strfind(strlower(display:text(display.value)),"mod"..modnum.." ") then
mod_choice.value = modnum
break
else mod_choice.value = 0
end
end

-- find "Control"
if strfind(strlower(display:text(display.value))," control ") then
ctrl.value = 1
else
ctrl.value = 0
end

-- find "Shift"
if strfind(strlower(display:text(display.value))," shift ") then
shift.value = 1
else
shift.value = 0
end

-- find "ExecCommand"
if strfind(strlower(display:text(display.value)),":execcommand ") then
fb_command.value = 1
sh_command.value = gsub(display:text(display.value),".*:-ommand ","")
sh_command:show()
else
-- else find other fluxbox commands
sh_command:hide()
for find_flux = 1,num_fb_commands do
if strfind(strlower(display:text(display.value)),":"..strlower(fluxbox_commands[find_flux])) then
print(fluxbox_commands[find_flux])
fb_command.value = find_flux
break
else fb_command.value = 0
end
end
end
--print(display:text(display.value))

end

Posted by clacker on Oct. 17 2005,20:46
mikshaw, this looks like a great project.  I don't have the big answers for you, but here are some of my thoughts:

You want to parse a line to find out what components are there so for each component, you can create a check box (like for shift and mod8), a pull down menu item (like for  :prevworkspace or :execcommand), and in input box for things like the execcommand string.  What I would do is parse the strings into two parts: before the command and after.  Is that OK to do or not?  I thought that the keys and modifers came before the :command.

I used the sherlock holmes method to find the key: eleminate everything else, and whatever is left must be the key!

EDIT: modified code to separate out key.

Code Sample
-- test for mikshaw

-- make some test strings

st1 = "Mod1 Tab :NextWorkspace"
st2 = "mod1 Shift TAB :prevworkspace"
st3 = "NONE shift Control f1 :execCommand aterm -e poop"
st4 = 'none control SHIFT F12 :ExecCommand aterm -e su -c "blah -five"'

-- make the parsing function
function parseMyLine(mystring)
  frontstring = strsub( mystring, strfind(mystring, "^[^\:]*"))
  backstring = strsub( mystring, strfind(mystring, ":.*"))

  if strfind( strlower(frontstring), "^%s*none") then cb_none = "true" else cb_none = "false" end
  if strfind( strlower(frontstring), "control") then cb_control = "true" else cb_control = "false" end
  if strfind( strlower(frontstring), "shift") then cb_shift = "true" else cb_shift = "false" end

  if strfind( strlower(frontstring), "mod%d") then
     tempModString = strsub( strlower(frontstring), strfind( strlower(frontstring), "mod%d*"))
     cb_modifier = strsub( tempModString, strfind( tempModString, "%d"))
  else
     cb_modifier = "'no value'"
  end

--strip out everything but the key
  frontstring = gsub(frontstring,"[nN][oO][Nn][Ee]","")
  frontstring = gsub(frontstring,"[Cc][Oo][Nn][Tt][Rr][Oo][Ll]","")
  frontstring = gsub(frontstring,"[Ss][Hh][Ii][Ff][Tt]","")
  frontstring = gsub(frontstring,"[Mm][Oo][Dd]%d*","")
  cb_key = gsub(frontstring, "%s%s*","")

  list_command = strsub( strlower(backstring), strfind(strlower(backstring), ":[^%s]*"))
  list_commandargs = gsub(strlower(backstring), ":[^%s]*", "")

  print("PARSING: ".. mystring)
  print("frontstring = " .. frontstring)
  print("backstring = " .. backstring)
  print("\tnone was " .. cb_none)
  print("\tcontrol was " .. cb_control)
  print("\tshift was " .. cb_shift)
  print("\tMOD was " .. cb_modifier)
  print("\tkey was " .. cb_key)
  print("\tfluxbox command was was " .. list_command)
  print("\tfluxbox command args were " .. list_commandargs)
  print()
end

parseMyLine(st1)
parseMyLine(st2)
parseMyLine(st3)
parseMyLine(st4)

Posted by mikshaw on Oct. 17 2005,21:06
I had orignally split the string into the two parts separated by the colon, and ideally i want to put it back in just in case the shell command contains " shift " or " control " (the spaces are useful, by the way...I notice you omitted them in your script). The biggest problem was arising from creating substrings out of substrings i think....i'm guessing that it was inthe middle of parsing wheni tried to get it to start over with a different line?  I still don't know for sure.  Anyway, my current function seems to do exactly what i need without crashing...EXCEPT for grabbing just the single key without modifiers.  I can't see where that is in your script? Thank you for your work...could you please help me to understand it? =o)

I'm beginning to think that a new table for keys might work, in the same way that the fluxbox_commands table is used.  Rather than type in the name of the key, the user would select it from a (very) long list. The problem with that, other than the fact that the list would contain over 100 entries, is that different keyboards have different keys, and it also wouldn't allow the user to enter keycodes instead of key names.

Posted by clacker on Oct. 17 2005,21:31
I retro-actively added the part the separates out the string for the key to my previous, just because I'm devious.  There are three main string functions I think we are both using: strfind, strsub, and gsub.  Strfind looks for a match and return either nil if it finds nothing or a pair of numbers.  strsub returns a substring based on the numbers returned by strfind.  gsub replaces a matched pattern in a string with another pattern.

I would try matching a pattern, say shift.  If that matches, I set some switch.  Then i use gsub to remove that string from the test string.  Once all of the modifiers have been removed from the test string (and the spaces) whatever is left is the key itself.

What I did is a little harder to explain.  It all regular expression stuff.  You can look < at the manual > to see more.

frontstring = strsub( mystring, strfind(mystring, "^[^\:]*"))
          matches from the start of a string ( "^ ) and includes everything from there on except for a : character ( [^:]* )

backstring = strsub( mystring, strfind(mystring, ":.*"))
          matches from the first : and includes the rest of the string.

Posted by clacker on Oct. 17 2005,23:42
OK, here's what I got.  It gets the key (even tab) and it sets the check boxes.  You're on your own setting the menu to the correct command.

Code Sample
-- test for mikshaw

-- make some test strings

st1 = "Mod1 Tab :NextWorkspace"
st2 = "mod1 Shift TAB :prevworkspace"
st3 = "NONE shift Control f12 :execCommand aterm -e poop"
st4 = 'none control SHIFT F12 :ExecCommand aterm -e su -c "blah -five"'


-- set up windows and buttons

w = Window{20,100,300,200;label = "Fluxbox Keys Test Window"}

b_none = Check_Button{20,20,60,20;label = "None"}
b_shift = Check_Button{20,50,60,20;label = "Shift"}
b_control = Check_Button{20,80,60,20;label = "Control"}
i_mod = Input{60,110,60,20;label = "Mod"}
i_key = Input{140,40,50,20;label = "Key"}
o_string = Output{40,170,250,20;label = "string"}

m_command = Choice{20,140,200,20}
m_command:add(Menu_Entry{":ExecCommand"})
m_command:add(Menu_Entry{":NextWorkspace"})
m_command:add(Menu_Entry{":PrevWorkspace"})

w:end_layout()

-- make the parsing function
function parseMyLine(mystring)
 o_string.value = mystring
 frontstring = strsub( mystring, strfind(mystring, "^[^\:]*"))
 backstring = strsub( mystring, strfind(mystring, ":.*"))

 if strfind( strlower(frontstring), "^%s*none") then b_none.value = 1 else b_none.value = 0 end
 if strfind( strlower(frontstring), "control") then b_control.value = 1 else b_control.value = 0 end
 if strfind( strlower(frontstring), "shift") then b_shift.value = 1 else b_shift.value = 0 end

 if strfind( strlower(frontstring), "mod%d") then
    tempModString = strsub( strlower(frontstring), strfind( strlower(frontstring), "mod%d*"))
    i_mod.value = strsub( tempModString, strfind( tempModString, "%d"))
 else
    i_mod.value = ""
 end

--strip out everything but the key
 frontstring = gsub(frontstring,"[nN][oO][Nn][Ee]","")
 frontstring = gsub(frontstring,"[Cc][Oo][Nn][Tt][Rr][Oo][Ll]","")
 frontstring = gsub(frontstring,"[Ss][Hh][Ii][Ff][Tt]","")
 frontstring = gsub(frontstring,"[Mm][Oo][Dd]%d*","")
 i_key.value = gsub(frontstring, "%s%s*","")

 list_command = strsub( strlower(backstring), strfind(strlower(backstring), ":[^%s]*"))
 list_commandargs = gsub(strlower(backstring), ":[^%s]*", "")

end

parseMyLine(st1)
parseMyLine(st2)
--parseMyLine(st3)
--parseMyLine(st4)

w:show()

Posted by mikshaw on Oct. 17 2005,23:47
I got it working the way i was hoping with just a couple of lines:
-- cut off the command:
the_keystring = gsub(display:text(display.value),"%s:.*","")
-- cut off everything but the last field of the string (emulates awk '{print $NF}'):
key.value = gsub(the_keystring,".*%s","")

My problem in getting this working before was that i was leaving the space at the end of the keystring, so trying to cut out a string of characters with a space at the end would result in cutting the whole string.

Now all I need to do is add the "save" function and it'll be ready for the first release.  I know i pretty much ignored your script entirely, but thanks for trying to help =o)

UPDATE: got a useable script going.  It will probably need some debugging, but it does what i need it to do so far.
< http://dsl-mirror.vectori.net/tools/mikshaw/ >

Posted by mikshaw on Oct. 18 2005,05:25
crap...couldn't upload it for some reason.....

Code Sample
#!/bin/flua


------------------------------------------------------------------------
-- fluxkeys.flua
-- gui frontend to fluxbox keys file
-- mikshaw 2005

-- Changelog YYYY-MM-DD
-- 2005-10-16 Begin
-- 2005-10-17 First release
-- 2005-10-18 Fixed recognition of Tab-separated items
--            Fixed crash when clicking Edit buttons with no file loaded
--            Removed unused Initializers
--            Fixed focus issue when using arrows to scroll
--            Display line now doesn't change until you press the button
--            Fixed "Cancel" bug
------------------------------------------------------------------------

-- DEFAULTS
default_keysfile = getenv("HOME").."/.fluxbox/keys"
ww = 420        -- window width
wh = 300        -- window height
Fl_Widget.initializers = {textfont = 15, labelfont = 15, labelcolor = 0}
Fl_Round_Button.initializers = {box = Boxtype.none, down_box=Boxtype.round_down, type = Buttontype.radio, align = Align.bottom}
Fl_Window.initializers = {box = Boxtype.thin_down, color = 15}
Fl_Input_.initializers = {when = When.changed}

fluxbox_commands = {
"BLANK", "ExecCommand", "Reconfigure", "Restart", "Quit", "NextWindow", "PrevWindow",
"NextWorkspace", "PrevWorkspace", "Workspace1", "Workspace2", "Workspace3", "Workspace4",
"Workspace 1", "Workspace 2", "Workspace 3", "Workspace 4",
"SendToNextWorkspace", "SendToPrevWorkspace", "SendToWorkspace 1", "SendToWorkspace 2",
"SendToWorkspace 3", "SendToWorkspace 4", "NextGroup", "PrevGroup", "Minimize", "Maximize",
"Shade", "Stick", "Fullscreen", "ToggleDecor", "Raise", "Lower", "RaiseLayer", "LowerLayer",
"Close", "KillWindow", "WindowMenu", "WorkspaceMenu", "RootMenu", "ReloadStyle", "CommandDialog",
"WorkspaceNameDialog", "SetResourceValueDialog", "Deiconify last", "Deiconify lastworkspace",
"Deiconify all", "Deiconify allworkspace", "ShowDesktop", "ArrangeWindows",
"AttachClient", "DetachClient", "FirstTab", "LastTab", "NextTab", "PrevTab",
"MoveTabNext", "MoveTabPrev", "ToggleTab"
}
-- get total number of fluxbox commands
num_fb_commands = getn(fluxbox_commands)

function set_line()
-- modify the keys file line
-- a=mod# b=Control c=Shift d=key e=fluxcommand f=shellcommand
if display.value > 0 and not strfind(display:text(display.value),"^#") then
a = mod_choice.text
if ctrl.value == 1 then b = " Control" else b = "" end
if shift.value == 1 then c = " Shift" else c = "" end
d = " "..key.value
if fb_command.value == 0 then e = "" else e = " :"..fb_command.text end
if fb_command.text == "ExecCommand" then f = " "..sh_command.value else f = "" end
display:set_text(display.value,a..b..c..d..e..f)
end
end

function get_line()
-- find parts of selected keysfile line
-- string of just the keys
the_keystring = gsub(display:text(display.value),"%s*%s:.*","")
-- find Mod<num> or "None"
for modnum = 1,9 do
if strfind(strlower(the_keystring),"mod"..modnum.." ") then
mod_choice.value = modnum
break
else mod_choice.value = 0
end
end
-- find "Control"
if strfind(strlower(the_keystring),"control ") then
ctrl.value = 1
else
ctrl.value = 0
end
-- find "Shift"
if strfind(strlower(the_keystring),"shift ") then
shift.value = 1
else
shift.value = 0
end
-- find single key
if not strfind(display:text(display.value),"^#") then
key.value = gsub(the_keystring,".*%s","")
else key.value = "" end
-- find "ExecCommand"
if strfind(strlower(display:text(display.value)),":execcommand ") then
fb_command.value = 1
sh_command.value = gsub(display:text(display.value),".*:-ommand%s*%s","")
sh_command:show()
else
-- else find other fluxbox commands
sh_command:hide()
sh_command.value=""
for find_flux = 1,num_fb_commands do
if strfind(strlower(display:text(display.value)),":"..strlower(fluxbox_commands[find_flux])) then
fb_command.value = find_flux-1
break
else fb_command.value = 0
end
end
end
key:take_focus()
end

-- MAIN WINDOW
w_main = Window{ww,wh, "fluxkeys.flua"}
-- MENU
mm = Menu_Bar{0,0,ww,25;textfont=9}
file_open = Menu_Entry{"&File/&Open..."}
function file_open:callback()
keysfile = fl_file_chooser("Open Keys File","keys*",default_keysfile)
if not keysfile then
keysfile = default_keysfile
else
display:load(keysfile)
display.value=1
line_num.value=display.value
get_line()
end
end
file_save = Menu_Entry{"&File/&Save"}
function file_save:callback()
if display.value > 0 then
writeto(keysfile)
write(display:text(1))
start = 2
while start <= display.size do
write("\n"..display:text(start))
start = start+1
end
fl_message(keysfile.." written")
writeto()
end
end
help = Menu_Entry{"&File/&Help"}
function help:callback()
w_help:show()
end
file_quit = Menu_Entry{"&File/&Quit"}
function file_quit:callback()
  exit(0)
end
edit_new = Menu_Entry{"&Edit/&New Key"}
function edit_new:callback()
if display.value > 0 then
display:insert(display.value+1,display:text(display.value))
display.value=display.value+1
line_num.value=display.value
get_line()
end
end
edit_remove = Menu_Entry{"&Edit/&Remove Key"}
function edit_remove:callback()
if display.value > 0 then
temp_line = display.value
display:remove(display.value)
display.value = temp_line
get_line()
end
end
mm:add(file_open)
mm:add(file_save)
mm:add(help)
mm:add(file_quit)
mm:add(edit_new)
mm:add(edit_remove)
-- MENU END

line_num = Int_Input{360,0,60,25,"line:"}
function line_num:callback()
if line_num.value > "" then display.value = line_num.value end
end

-- The main text field, containing the keys file itself
display = Hold_Browser{10,30,ww-20,200}
function display:callback()
line_num.value = display.value
if display.value > 0 then get_line() end
end

-- CHOOSE MODIFIERS (Mod<num>, Ctrl, Shift)
mod_choice = Choice{10,240,64,20}
mod_choice:add(Menu_Entry{"NONE"})
for modline = 1,9 do
mod_choice:add(Menu_Entry{"Mod"..modline})
end
mod_choice.value = 0
ctrl = Round_Button{7,260,64,20;type=Buttontype.toggle}
ctrl_label = Box{10,260,20,20,"Control";align=Align.right}
shift = Round_Button{7,275,64,20;type=Buttontype.toggle}
shift_label = Box{10,275,20,20,"Shift";align=Align.right}

-- CHOOSE KEY
key = Input{80,240,80,20}

-- MAKE THE CHANGE
commit = Button{80,265,80,20,"APPLY";labelfont=9}
function commit:callback()
set_line()
end

-- CHOOSE FLUXBOX COMMAND
-- shell command
-- hidden unless "ExecCommand" is selected
sh_command = Input{170,265,240,20}
fb_command = Choice{170,240,240,20}
for commline = 1,num_fb_commands do
fb_command:add(Menu_Entry{fluxbox_commands[commline]})
end
fb_command.value = 0
function fb_command:callback()
if fb_command.text == "ExecCommand" then
sh_command:show()
else sh_command.value="" end
end
w_main:end_layout()


-- HELP WINDOW
w_help = Window{ww,wh, "fluxkeys help"}
w_help_text = Box{10,10,0,wh-20,"\n"..
"fluxkeys.flua : version one.one : released 18 Oct, 2005 : mikshaw\n\n"..
"This script  might help you in creating  or  editing your Fluxbox\n"..
"key bindings file. Hopefully the usage is pretty simple...use the\n"..
"\"File -> Open\" menu to load your keys file, highlight a line, and\n"..
"use the  buttons and input fields to modify the line.   The first\n"..
"button and the toggles below it are used  to choose your modifier\n"..
"keys, the text field next to that is for entering the name of the\n"..
"main key to press,  and the inputs  on the right are the command.\n"..
"The APPLY button sends all your changes to the selected key line.\n"..
"The \"Edit\" menu allows you to add and remove lines from the file,\n"..
"and \"File -> Save\" will save your changes.  You can select a line\n"..
"in three ways:  1) click on the line,  2) type a line number into\n"..
"the \"line:\" box, or 3) use arrows to scroll.\n"
;align=Align.right}
w_help:end_layout()
w_main:show()

Posted by clacker on Oct. 18 2005,12:01
mikshaw, that is one awsume flua example.  Absolutely fantastic.  I had no idea flua could look and act so much like a real application.

The only thing I saw so far was when you use the file dialog to open a file, but then hit cancel you get an error from gsub.

Very nice work.

Posted by mikshaw on Oct. 18 2005,14:33
Thanks...I think i fixed the cancel bug, although it's not thoroughly tested.

I don't understand why Flua is so underappreciated....it's really an amazingly simple way to build some pretty decent interfaces, while remaining tiny and open. It's really annoying to search for tools online only to find so many based on KDE, Gtk2, and Java....even the simplest tools like this keys editor. Why make a 10k application a hundred times larger (and depend on even more) just for a prettier set of widgets?
I'm hoping that eventually I can learn Lua well enough to not struggle so much with every project.  Also hoping that the Flua project is not dead =o(

Posted by clacker on Oct. 18 2005,16:37
What's really missing from flua is decent documentation and a few kick-ass examples (like yours was).  My beef with it is it's so hard to find how to do things that after a while you assume it's a crappy program, which your example proves it's not.

I don't think flua's dead, it just smells funny.

Posted by mikshaw on Oct. 18 2005,21:42
I was messing around for days using Lua5 syntax, and wondering why so many things didn't work.  Even after reading through the lua4 manual and the flua docs a lot of stuff still doesn't make sense to me (I usually have both open for reference while scripting).  The flua widget reference is extremely vague, and the example files are for the most part either incomplete or so simple that they don't provide much help.  Personally I've learned much more from reading Robert's scripts than from reading those examples.
Powered by Ikonboard 3.1.2a
Ikonboard © 2001 Jarvis Entertainment Group, Inc.