1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import sys
18 import os
19 import traceback
20 import __builtin__
21
22 from libxyz.core.utils import ustring
23 from libxyz.core.utils import is_func
24 from libxyz.core.plugins import Namespace
25 from libxyz.core import FSRule
26
27 from libxyz.ui.colors import Palette
28 from libxyz.ui import Shortcut
29
30 from skin import Skin
31
32 import libxyz.exceptions as ex
35 """
36 Ensure the class has been instantiated
37 """
38
39 def wrap(cls, *args, **kwargs):
40 if cls._instance is None:
41 error(_(u"Class must be instantiated first!"))
42 else:
43 return func(cls, *args, **kwargs)
44
45 wrap.__doc__ = func.__doc__
46
47 return wrap
48
49
50
51 -def error(msg, trace=True):
52 if trace and hasattr(__builtin__, "xyzlog"):
53 xyzlog.debug(ustring(traceback.format_exc()))
54 raise ex.DSLError(_(u"DSL Error: %s") % msg)
55
56
57
58 -class XYZ(object):
59 """
60 XYZ DSL implementation object
61 """
62
63 api = ["let",
64 "val",
65 "section",
66 "unlet",
67 "load",
68 "bind",
69 "exec_file",
70 "kbd",
71 "action",
72 "macro",
73 "call",
74 "env",
75 "shell",
76 "alias",
77 "plugins_on",
78 "plugins_off",
79 "plugin_conf",
80 "icmd",
81 "prefix",
82 "help",
83 "vfs",
84 "vfs_path",
85 "hook",
86 "unhook",
87 "fsrule",
88 "palette",
89 "skin"
90 ]
91
92 EVENT_CONF_UPDATE = u"event:conf_update"
93
94 macros = {}
95
96 _instance = None
97 _env = {}
98
114
115
116
117 @classmethod
119 cls.macros["ACT_CWD"] = lambda: cls.xyz.pm.from_load(":sys:panel",
120 "cwd")()
121 cls.macros["INACT_CWD"] = lambda: cls.xyz.pm.from_load(":sys:panel",
122 "cwd")(False)
123
124 cls.macros["ACT_PATH"] = lambda: \
125 cls.xyz.pm.from_load(":sys:panel",
126 "get_selected")().path
127
128 cls.macros["INACT_PATH"] = lambda: \
129 cls.xyz.pm.from_load(":sys:panel",
130 "get_selected"
131 )(False).path
132 cls.macros["ACT_BASE"] = lambda: \
133 os.path.dirname(cls.macros["ACT_CWD"]())
134
135 cls.macros["INACT_BASE"] = lambda: \
136 os.path.dirname(cls.macros["INACT_CWD"]())
137
138 cls.macros["ACT_TAGGED"] = lambda: [x.full_path for x in
139 cls.xyz.pm.from_load(
140 ":sys:panel",
141 "get_tagged")()]
142 cls.macros["INACT_TAGGED"] = lambda: [x.full_path for x in
143 cls.xyz.pm.from_load(
144 ":sys:panel",
145 "get_tagged")(False)]
146
147 cls.macros["ACT_UNTAGGED"] = lambda: [x.full_path for x in
148 cls.xyz.pm.from_load(
149 ":sys:panel",
150 "get_untagged")()]
151 cls.macros["INACT_UNTAGGED"] = lambda: [x.full_path for x in
152 cls.xyz.pm.from_load(
153 ":sys:panel",
154 "get_untagged")(False)]
155
156
157
158 @classmethod
161
162
163
164 @classmethod
165 @instantiated
166 - def let(cls, var, val, sect=u"local"):
167 """
168 Set variable.
169 Variable will be available in xyz.conf[section][varname]
170 If section is not provided - local will be used
171 """
172
173 _conf = cls.xyz.conf
174
175 if sect not in _conf:
176 _conf[sect] = {}
177
178 if var in _conf[sect] and isinstance(_conf[sect][var], dict) and \
179 isinstance(val, dict):
180
181 _conf[sect][var].update(val)
182 else:
183 cls.xyz.conf[sect][var] = val
184
185 cls.xyz.hm.dispatch(cls.EVENT_CONF_UPDATE, var, val, sect)
186
187
188
189 @classmethod
190 @instantiated
191 - def val(cls, var, sect=u"local"):
192 """
193 Return variable value or None if undefined
194 """
195
196 try:
197 return cls.xyz.conf[sect][var]
198 except Exception:
199 return None
200
201
202
203 @classmethod
204 @instantiated
206 """
207 Return whole configuration section contents as a dictionary or None
208 if undefined
209 """
210
211 try:
212 return cls.xyz.conf[sect]
213 except Exception:
214 return None
215
216
217
218 @classmethod
219 @instantiated
220 - def unlet(cls, var, sect=u"local"):
221 """
222 Unset variable
223 """
224
225 if var in cls.xyz.conf[sect]:
226 del(cls.xyz.conf[sect])
227
228
229
230 @classmethod
231 @instantiated
232 - def load(cls, plugin):
233 """
234 Load method[s] from plugin
235 """
236
237 try:
238 cls.xyz.km.load(plugin)
239 except Exception, e:
240 error(_(u"Unable to load plugin %s: %s") %
241 (plugin, unicode(e)))
242
243
244
245 @classmethod
246 @instantiated
247 - def bind(cls, method, shortcut, context="DEFAULT"):
248 """
249 Bind method to shortcut
250 """
251
252 try:
253 cls.xyz.km.bind(method, shortcut, context=context)
254 except Exception, e:
255 error(_(u"Unable to bind shortcut %s: %s") % (str(shortcut),
256 unicode(e)))
257
258
259
260 @classmethod
261 @instantiated
262 - def kbd(cls, *args):
263 """
264 Create keyboard shortcut
265 """
266
267 return Shortcut(sc=list(args))
268
269
270
271 @classmethod
272 @instantiated
274 """
275 Execute DSL in file
276 """
277
278 f = None
279
280 try:
281 f = open(filename)
282 cls.execute(f.read())
283 except Exception, e:
284 error(_(u"Unable to execute file: %s") % unicode(e))
285
286 if f:
287 f.close()
288
289
290
291 @classmethod
292 @instantiated
294 """
295 Set up an action to be taken upon pressing action key on file
296 """
297
298 try:
299 cls.xyz.am.register(rule, fn)
300 except Exception, e:
301 error(_(u"Unable to register action: %s") % unicode(e))
302
303
304
305 @classmethod
306 @instantiated
307 - def macro(cls, macroname):
308 """
309 Expand macro name.
310
311 Available macros:
312 * ACT_CWD -- Working directory in active panel
313 * INACT_CWD -- Working directory in inactive panel
314 * ACT_PATH -- Full selected object path in active panel
315 * INACT_PATH -- Full selected object path in inactive panel
316 * ACT_BASE -- Parent directory in active panel
317 * INACT_BASE -- Parent directory in inactive panel
318 * ACT_TAGGED -- List of tagged files in active panel
319 * INACT_TAGGED -- List of tagged files in inactive panel
320 * ACT_UNTAGGED -- List of not tagged files in active panel
321 * INACT_UNTAGGED -- List of not tagged files in inactive panel
322 """
323
324 if macroname in cls.macros:
325 try:
326 return cls.macros[macroname]()
327 except Exception, e:
328 xyzlog.warning(_(u"Unable to expand macro %s: %s") %
329 (ustring(macroname), unicode(e)))
330
331 return macroname
332
333
334
335 @classmethod
336 @instantiated
337 - def call(cls, method, *args, **kwargs):
338 """
339 Call plugin method
340 """
341
342 try:
343 p = Namespace(method)
344 m = cls.xyz.pm.from_load(p.pfull, p.method)
345 return m(*args, **kwargs)
346 except Exception, e:
347 error(_(u"Unable to execute method %s: %s") %
348 (method, unicode(e)))
349
350
351
352 @classmethod
353 @instantiated
354 - def env(cls, var, default=None):
355 """
356 Return environment variable or default if is not set
357 """
358
359 return os.getenv(var, default)
360
361
362
363 @classmethod
364 @instantiated
365 - def shell(cls, cmd, *args, **kwargs):
366 """
367 Execute command via :core:shell plugin
368 Optional boolean argument 'current' can be provided to indicate
369 that cmd is to be run from current directory.
370 Optional boolean argument 'bg' can be provided to indicate that cmd
371 must be executed in background
372 Optional boolean argument 'reload' can be provided to indicate
373 that panel content should/should not be reloaded after execution
374 Optional boolean argument 'wait' can be provided to indicate
375 that shell should/should not wait for user input after command executed
376 The wait flag has higher priority than :core:shell's `wait`
377 configuration flag.
378 """
379
380 if kwargs.get("current", False):
381 cmd = "./%s" % cmd
382
383 if kwargs.get("bg", False):
384 bg = ["&"]
385 else:
386 bg = []
387
388 reloadp = kwargs.get("reload", True)
389 wait = kwargs.get("wait", None)
390
391 try:
392 exef = cls.xyz.pm.from_load(":core:shell", "execute")
393 escapef = cls.xyz.pm.from_load(":sys:cmd", "escape")
394 reloadf = cls.xyz.pm.from_load(":sys:panel", "reload_all")
395 exef(" ".join([escapef(cmd, True)] +
396 [escapef(a, True) for a in args] + bg), wait=wait)
397 if reloadp:
398 reloadf()
399 except Exception, e:
400 error(_(u"Error in DSL shell execution: %s") % unicode(e))
401
402
403
404 @classmethod
405 @instantiated
406 - def alias(cls, alias, replace):
407 """
408 Set an alias which will be expanded in command line before execution
409 @param replace: Either string or function
410 """
411
412 return cls.let(alias, replace, sect="aliases")
413
414
415
416 @classmethod
417 @instantiated
418 - def icmd(cls, command, obj):
419 """
420 Set an internal command.
421 """
422
423 if not is_func(obj):
424 error(_(u"Invalid object type: %s. Function expected") %
425 type(obj), trace=False)
426
427 return cls.let(command, obj, sect="commands")
428
429
430
431 @classmethod
432 @instantiated
434 """
435 Enable plugin[s]
436 """
437
438 for plugin in plugins:
439 cls.let("plugins", {plugin: "ENABLE"}, sect="xyz")
440
441
442
443 @classmethod
444 @instantiated
446 """
447 Disable plugin[s]
448 """
449
450 for plugin in plugins:
451 cls.let("plugins", {plugin: "DISABLE"}, sect="xyz")
452
453
454
455 @classmethod
456 @instantiated
458 """
459 Configure plugin.
460
461 @param plugin: Plugin name
462 @param opts: dict {var1: val1, var2: var2,..}
463 """
464
465 if not isinstance(opts, dict):
466 error(_(u"Invalid opts type: %s. Dict instance expected")
467 % type(opts))
468
469 return cls.let(plugin, opts, sect="plugins")
470
471
472
473 @classmethod
474 @instantiated
481
482
483
484 @classmethod
485 @instantiated
486 - def help(cls, obj=None):
487 """
488 Help
489 """
490
491 fmt = lambda o: "%s\t%s" % (o, getattr(cls, o).__doc__)
492
493 if obj is not None and obj not in cls.api:
494 error(_(u"Invalid function %s") % obj)
495
496 if obj:
497 objs = [obj]
498 else:
499 objs = cls.api
500
501 return "\n".join([fmt(x) for x in objs]).replace("\t", " ")
502
503
504
505 @classmethod
506 @instantiated
507 - def vfs(cls, prefix, vfsclass):
508 """
509 Set prefix and VFSObject class for VFS dispatching
510 """
511
512 try:
513 return cls.xyz.vfs.register(prefix, vfsclass)
514 except Exception, e:
515 error(_(u"Error setting VFS prefix: %s") % unicode(e))
516
517
518
519 @classmethod
520 @instantiated
522 """
523 Construct path using provided VFS driver
524 """
525
526 return path + "#vfs-%s#" % driver
527
528
529
530 @classmethod
531 @instantiated
532 - def hook(cls, event, proc):
533 """
534 Register a new hook.
535 Event is an event string and proc is a procedure to be called
536 """
537
538 try:
539 return cls.xyz.hm.register(event, proc)
540 except Exception, e:
541 error(_(u"Error registering new hook: %s") % unicode(e))
542
543
544
545 @classmethod
546 @instantiated
548 """
549 Remove all hooks for the event
550 """
551
552 return cls.xyz.hm.clear(event)
553
554
555
556 @classmethod
557 @instantiated
559 """
560 Return libxyz.core.FSRule instance
561 """
562
563 try:
564 return FSRule(rule)
565 except Exception, e:
566 error(_(u"Error parsing FSRule: %s") % unicode(e))
567
568
569
570 @classmethod
571 @instantiated
573 """
574 Create internal palette object
575
576 @param config: Dictionary of form:
577 {
578 'foreground': COLOR,
579 'background': COLOR,
580 'fg_attributes': [ATTR],
581 'mono': [ATTR],
582 'foreground_high': HG_COLOR,
583 'background_high': HG_COLOR
584 }
585 """
586
587 try:
588 return Palette(None, *Palette.convert(config))
589 except Exception, e:
590 error(_(u"Error creating Palette instance: %s") % unicode(e))
591
592
593
594 @classmethod
595 @instantiated
596 - def skin(cls, **kwargs):
597 """
598 Make and register new skin
599 """
600
601 try:
602 cls.xyz.sm.add(Skin(**kwargs))
603 except Exception, e:
604 error(_(u"Error creating Skin instance: %s") % unicode(e))
605
606
607
608 @classmethod
609 @instantiated
611 """
612 Execute DSL statements
613 @param source: Either string or open file-object or code object
614 """
615
616 try:
617 exec source in cls._env.copy()
618 except Exception, e:
619 error(_(u"Error in DSL execution: %s") % unicode(e))
620
621
622
623 @classmethod
624 @instantiated
626 """
627 Return copy of global dsl environment
628 """
629
630 return cls._env.copy()
631
632
633
634
635 module = sys.modules[__name__]
636
637 __all__ = ["XYZ"]
638
639 for f in XYZ.api:
640 setattr(module, f, getattr(XYZ, f))
641 __all__.append(f)
642