3 # Released under the terms of the GPLv3
8 from StringIO
import StringIO
11 import curses
.ascii
as ascii
15 from Browser
import Browser
17 defaultConfig
= """# options
19 # how many words to flash per minute
22 # "Home page", loaded if no url is passed on the command line
23 set initialUrl http://en.wikipedia.org
25 # show current location in the document?
28 # show abbreviated status line and prompts?
31 # blank between words?
34 # how many links to cycle through, and how many of the most recent should be
37 set linkDisplayEmphNum 1
39 # how many difficult "words" (defined as character strings which are long or
40 # contain nonalphabetic) to show
41 set trickyDisplayNum 3
43 # how many documents to keep in the history cache
46 # how many blanks to insert at the end of a sentence/paragraph
47 set sentenceBlankSpace 1
50 # pause at regular intervals?
52 # pause for blinkLength milliseconds out of every blinkPeriod milliseconds
55 # wait until the end of a sentence before blinking?
56 set blinkWaitSentence False
58 # speed in "skim mode"
66 bind Up changespeed 25
69 bind J changespeed -25
70 bind Down changespeed -25
73 bind L seekParagraph 1
74 bind ^D seekParagraph 1
75 bind Right seekSentence 1
76 bind h seekSentence -1
77 bind H seekParagraph -1
78 bind ^U seekParagraph 1
79 bind Left seekSentence -1
82 bind NPage seekWord 100
85 bind PPage seekWord -100
104 bind N searchAgain -1
107 bind Backspace history -1
139 # Type "g foo" at the 'Go:' prompt to search with google for "foo".
140 shortcut g http://www.google.com/search?q=%1
141 shortcut wp http://en.wikipedia.org/wiki/%1
143 # Shortcuts can be recursive;
144 shortcut wps g site:en.wikipedia.org %1
148 # and can have multiple arguments;
149 shortcut gsite g site:%1 %2
151 # %c expands to the current url;
152 shortcut gs gsite %c %1
154 # and $HOME expands to your home directory.
155 shortcut rss $HOME/.rawdog/out.html
160 # <option name> : <type>
164 'blankBetween' : bool,
167 'linkDisplayNum' : int,
168 'linkDisplayEmphNum' : int,
169 'trickyDisplayNum' : int,
171 'paraBlankSpace' : int,
172 'sentenceBlankSpace' : int,
175 'blinkWaitSentence' : bool,
179 # this list consists of the keys of optionsInfo in the order we want
180 # --help to present them
182 'wpm', 'initialUrl', 'maxCached', 'abbreviate', 'showPoint',
183 'linkDisplayNum', 'linkDisplayEmphNum', 'trickyDisplayNum',
184 'paraBlankSpace', 'sentenceBlankSpace', 'blankBetween',
185 'blink', 'blinkPeriod', 'blinkLength', 'blinkWaitSentence',
190 self
.keyBindings
= {}
191 self
.urlShortcuts
= {}
194 self
.load(StringIO(defaultConfig
), "[Default Config]")
196 filename
= os
.getenv('HOME')+'/.flinksrc'
198 file = open(filename
, 'r')
199 self
.load(file, filename
)
201 # write default config
202 open(filename
, 'w').write(defaultConfig
)
204 def processArgs(self
, argv
):
205 from getopt
import gnu_getopt
207 shortOptDict
= { "wpm": "w", "showPoint": "p",
208 "abbreviate": "a", "blink": "b" }
211 for optName
in self
.optionNames
:
212 if self
.optionsInfo
[optName
] == bool:
213 longopts
+= [optName
, "%s" % optName
.lower()]
214 longopts
+= [optName
, "no%s" % optName
.lower()]
216 longopts
.append("%s=" % optName
.lower())
218 opts
, args
= gnu_getopt(argv
, 'hw:pPaAbB', longopts
)
220 for opt
, val
in opts
:
221 if opt
in ["-h", "--help"]:
222 def helpStr(optName
):
223 short
= shortOptDict
.get(optName
, None)
224 type = self
.optionsInfo
[optName
]
227 shopts
= "-%s/%s, " % (short
, short
.upper())
229 shopts
= "-%s, " % short
233 lopts
= "--[no]%s" % optName
.lower()
235 lopts
= "--%s=N" % optName
.lower()
237 lopts
= "--%s=STRING" % optName
.lower()
238 return " "+shopts
+lopts
239 print ("Usage: " + argv
[0] +
240 " [OPTION]... [INITIAL_URL]\nOptions:\n"
242 join( [helpStr(optName
)
243 for optName
in self
.optionNames
244 if optName
!= "initialUrl"], '\n'))
247 for optName
in self
.optionNames
:
248 short
= shortOptDict
.get(optName
, None)
249 type = self
.optionsInfo
[optName
]
250 if opt
== "--"+optName
.lower() or (short
and opt
== "-"+short
):
252 self
.options
[optName
] = True
255 self
.options
[optName
] = int(val
)
257 print "--%s: expected integer value" % optName
.lower()
260 self
.options
[optName
] = val
262 elif type == bool and opt
== "--no"+optName
.lower() or (
263 short
and opt
== "-"+short
.upper()):
264 self
.options
[optName
] = False
267 self
.options
["initialUrl"] = join(args
[1:])
271 def load(self
, file, filename
):
274 def addError(details
):
275 self
.loadErrors
.append("While parsing %s, line %d : %s" %
276 (filename
, lineNumber
, details
))
280 self
.processCommand(line
, errorHandler
=addError
)
282 def processCommand(self
, command
, errorHandler
=(lambda _
: None)):
283 words
= split(command
)
285 if words
== [] or words
[0][0] == '#':
292 errorHandler("usage: set OPTION VALUE")
295 for option
in self
.optionsInfo
.keys():
296 if words
[1].lower() == option
.lower():
297 optionType
= self
.optionsInfo
[option
]
298 if optionType
== int:
300 self
.options
[option
] = int(words
[2])
302 errorHandler("expected integer value")
303 elif optionType
== bool:
304 self
.options
[option
] = (words
[2].lower() == 'true')
305 elif optionType
== str:
306 self
.options
[option
] = words
[2]
308 errorHandler("*BUG*: unknown type for option value")
310 errorHandler("Unknown option: %s" % words
[1])
312 elif command
== "bind":
314 errorHandler("usage: bind KEY [FUNCTION [ARGS...]]")
321 if len(keyName
) == 1:
324 elif len(keyName
) == 2 and keyName
[0] == '^':
326 key
= ord(ascii
.ctrl(keyName
[1]))
327 elif len(keyName
) == 3 and keyName
[1] == '-' and keyName
[0] == 'C':
329 key
= ord(ascii
.ctrl(keyName
[2]))
330 elif len(keyName
) == 3 and keyName
[1] == '-' and keyName
[0] == 'M':
332 key
= ord(ascii
.alt(keyName
[2]))
333 elif keyName
.lower() == "space":
334 # space (special case)
336 elif keyName
.lower() == "esc" or keyName
.lower() == "escape":
337 # escape (special case)
342 key
= getattr(curses
, 'KEY_'+keyName
.upper())
343 except AttributeError:
347 errorHandler("bad key description \"%s\"" % (words
[1]))
352 if self
.keyBindings
.has_key(key
):
353 del self
.keyBindings
[key
]
357 for possMethod
in Browser
.bindableMethods
.keys():
358 if words
[2].lower() == possMethod
.lower():
361 errorHandler("unknown function \"%s\"" % words
[2])
364 minArgs
= Browser
.bindableMethods
[method
][0]
365 maxArgs
= Browser
.bindableMethods
[method
][1]
367 if len(words
) - 3 < minArgs
or len(words
) - 3 > maxArgs
:
368 errorHandler("\"%s\" requires %d-%d arguments" %
369 (method
, minArgs
, maxArgs
))
373 for arg
in words
[3:]:
375 args
.append(int(arg
))
377 errorHandler("expected integer argument")
380 self
.keyBindings
[key
] = [method
, args
]
382 elif command
=="shortcut":
384 errorHandler("usage: shortcut KEYWORD URL")
386 command
= join(words
[2:])
388 while re
.search('%%%d' % i
, command
):
391 self
.urlShortcuts
[words
[1]] = [numArgs
, command
]
394 errorHandler("unknown command \"%s\"" % words
[0])