-- major tests for drawings -- We minimize assumptions about specific pixels, and try to test at the level -- of specific shapes. In particular, no tests of freehand drawings. function test_creating_drawing_saves() io.write('\ntest_creating_drawing_saves') App.screen.init{width=120, height=60} Filename = 'foo' Lines = load_array{} App.draw() -- click on button to create drawing App.run_after_mouse_click(8,Margin_top+8, 1) -- file not immediately saved App.update(0.01) check_nil(App.filesystem['foo'], 'F - test_creating_drawing_saves/early') -- wait until save App.wait_fake_time(3.1) App.update(0) -- filesystem contains drawing and an empty line of text check_eq(App.filesystem['foo'], '```lines\n```\n\n', 'F - test_creating_drawing_saves') end function test_draw_line() io.write('\ntest_draw_line') -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) Filename = 'foo' App.screen.init{width=Margin_left+300, height=300} Lines = load_array{'```lines', '```', ''} Line_width = 256 -- drawing coordinates 1:1 with pixels Current_drawing_mode = 'line' App.draw() check_eq(#Lines, 2, 'F - test_draw_line/baseline/#lines') check_eq(Lines[1].mode, 'drawing', 'F - test_draw_line/baseline/mode') check_eq(Lines[1].y, Margin_top+Drawing_padding_top, 'F - test_draw_line/baseline/y') check_eq(Lines[1].h, 128, 'F - test_draw_line/baseline/y') check_eq(#Lines[1].shapes, 0, 'F - test_draw_line/baseline/#shapes') -- draw a line App.run_after_mouse_press(Margin_left+5, Margin_top+Drawing_padding_top+6, 1) App.run_after_mouse_release(Margin_left+35, Margin_top+Drawing_padding_top+36, 1) local drawing = Lines[1] check_eq(#drawing.shapes, 1, 'F - test_draw_line/#shapes') check_eq(#drawing.points, 2, 'F - test_draw_line/#points') check_eq(drawing.shapes[1].mode, 'line', 'F - test_draw_line/shape:1') local p1 = drawing.points[drawing.shapes[1].p1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p1.x, 5, 'F - test_draw_line/p1:x') check_eq(p1.y, 6, 'F - test_draw_line/p1:y') check_eq(p2.x, 35, 'F - test_draw_line/p2:x') check_eq(p2.y, 36, 'F - test_draw_line/p2:y') -- wait until save App.wait_fake_time(3.1) App.update(0) -- The format on disk isn't perfectly stable. Table fields can be reordered. -- So just reload from disk to verify. Lines = load_from_disk(Filename) local drawing = Lines[1] check_eq(#drawing.shapes, 1, 'F - test_draw_line/save/#shapes') check_eq(#drawing.points, 2, 'F - test_draw_line/save/#points') check_eq(drawing.shapes[1].mode, 'line', 'F - test_draw_line/save/shape:1') local p1 = drawing.points[drawing.shapes[1].p1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p1.x, 5, 'F - test_draw_line/save/p1:x') check_eq(p1.y, 6, 'F - test_draw_line/save/p1:y') check_eq(p2.x, 35, 'F - test_draw_line/save/p2:x') check_eq(p2.y, 36, 'F - test_draw_line/save/p2:y') end function test_draw_horizontal_line() io.write('\ntest_draw_horizontal_line') -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) App.screen.init{width=Margin_left+300, height=300} Lines = load_array{'```lines', '```', ''} Line_width = 256 -- drawing coordinates 1:1 with pixels Current_drawing_mode = 'manhattan' App.draw() check_eq(#Lines, 2, 'F - test_draw_horizontal_line/baseline/#lines') check_eq(Lines[1].mode, 'drawing', 'F - test_draw_horizontal_line/baseline/mode') check_eq(Lines[1].y, Margin_top+Drawing_padding_top, 'F - test_draw_horizontal_line/baseline/y') check_eq(Lines[1].h, 128, 'F - test_draw_horizontal_line/baseline/y') check_eq(#Lines[1].shapes, 0, 'F - test_draw_horizontal_line/baseline/#shapes') -- draw a line that is more horizontal than vertical App.run_after_mouse_press(Margin_left+5, Margin_top+Drawing_padding_top+6, 1) App.run_after_mouse_release(Margin_left+35, Margin_top+Drawing_padding_top+26, 1) local drawing = Lines[1] check_eq(#drawing.shapes, 1, 'F - test_draw_horizontal_line/#shapes') check_eq(#drawing.points, 2, 'F - test_draw_horizontal_line/#points') check_eq(drawing.shapes[1].mode, 'manhattan', 'F - test_draw_horizontal_line/shape_mode') local p1 = drawing.points[drawing.shapes[1].p1] local p2 = drawing.points[drawing.shapes[1].p2] check_eq(p1.x, 5, 'F - test_draw_horizontal_line/p1:x') check_eq(p1.y, 6, 'F - test_draw_horizontal_line/p1:y') check_eq(p2.x, 35, 'F - test_draw_horizontal_line/p2:x') check_eq(p2.y, p1.y, 'F - test_draw_horizontal_line/p2:y') end function test_draw_circle() io.write('\ntest_draw_circle') -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) App.screen.init{width=Margin_left+300, height=300} Lines = load_array{'```lines', '```', ''} Line_width = 256 -- drawing coordinates 1:1 with pixels Current_drawing_mode = 'line' App.draw() check_eq(#Lines, 2, 'F - test_draw_circle/baseline/#lines') check_eq(Lines[1].mode, 'drawing', 'F - test_draw_circle/baseline/mode') check_eq(Lines[1].y, Margin_top+Drawing_padding_top, 'F - test_draw_circle/baseline/y') check_eq(Lines[1].h, 128, 'F - test_draw_circle/baseline/y') check_eq(#Lines[1].shapes, 0, 'F - test_draw_circle/baseline/#shapes') -- draw a circle App.mouse_move(Margin_left+4, Margin_top+Drawing_padding_top+4) -- hover on drawing App.run_after_keychord('C-o') App.run_after_mouse_press(Margin_left+35, Margin_top+Drawing_padding_top+36, 1) App.run_after_mouse_release(Margin_left+35+30, Margin_top+Drawing_padding_top+36, 1) local drawing = Lines[1] check_eq(#drawing.shapes, 1, 'F - test_draw_circle/#shapes') check_eq(#drawing.points, 1, 'F - test_draw_circle/#points') check_eq(drawing.shapes[1].mode, 'circle', 'F - test_draw_horizontal_line/shape_mode') check_eq(drawing.shapes[1].radius, 30, 'F - test_draw_circle/radius') local center = drawing.points[drawing.shapes[1].center] check_eq(center.x, 35, 'F - test_draw_circle/center:x') check_eq(center.y, 36, 'F - test_draw_circle/center:y') end function test_keys_do_not_affect_shape_when_mouse_up() io.write('\ntest_keys_do_not_affect_shape_when_mouse_up') -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) App.screen.init{width=Margin_left+300, height=300} Lines = load_array{'```lines', '```', ''} Line_width = 256 -- drawing coordinates 1:1 with pixels Current_drawing_mode = 'line' App.draw() -- hover over drawing and press 'o' without holding mouse App.mouse_move(Margin_left+4, Margin_top+Drawing_padding_top+4) -- hover on drawing App.run_after_keychord('o') -- no change to drawing mode check_eq(Current_drawing_mode, 'line', 'F - test_keys_do_not_affect_shape_when_mouse_up/drawing_mode') -- no change to text either because we didn't run the textinput event end function test_draw_circle_mid_stroke() io.write('\ntest_draw_circle_mid_stroke') -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) App.screen.init{width=Margin_left+300, height=300} Lines = load_array{'```lines', '```', ''} Line_width = 256 -- drawing coordinates 1:1 with pixels Current_drawing_mode = 'line' App.draw() check_eq(#Lines, 2, 'F - test_draw_circle_mid_stroke/baseline/#lines') check_eq(Lines[1].mode, 'drawing', 'F - test_draw_circle_mid_stroke/baseline/mode') check_eq(Lines[1].y, Margin_top+Drawing_padding_top, 'F - test_draw_circle_mid_stroke/baseline/y') check_eq(Lines[1].h, 128, 'F - test_draw_circle_mid_stroke/baseline/y') check_eq(#Lines[1].shapes, 0, 'F - test_draw_circle_mid_stroke/baseline/#shapes') -- draw a circle App.mouse_move(Margin_left+4, Margin_top+Drawing_padding_top+4) -- hover on drawing App.run_after_mouse_press(Margin_left+35, Margin_top+Drawing_padding_top+36, 1) App.run_after_keychord('o') App.run_after_mouse_release(Margin_left+35+30, Margin_top+Drawing_padding_top+36, 1) local drawing = Lines[1] check_eq(#drawing.shapes, 1, 'F - test_draw_circle_mid_stroke/#shapes') check_eq(#drawing.points, 1, 'F - test_draw_circle_mid_stroke/#points') check_eq(drawing.shapes[1].mode, 'circle', 'F - test_draw_horizontal_line/shape_mode') check_eq(drawing.shapes[1].radius, 30, 'F - test_draw_circle_mid_stroke/radius') local center = drawing.points[drawing.shapes[1].center] check_eq(center.x, 35, 'F - test_draw_circle_mid_stroke/center:x') check_eq(center.y, 36, 'F - test_draw_circle_mid_stroke/center:y') end function test_draw_arc() io.write('\ntest_draw_arc') -- display a drawing followed by a line of t
# Copyright (C) 2009, 2010 Roman Zimbelmann <romanz@lavabit.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, sys, re
from ranger.api import *
from ranger.ext.iter_tools import flatten
from ranger.ext.get_executables import get_executables
from ranger.core.runner import Context
from ranger.core.shared import FileManagerAware
class Applications(FileManagerAware):
"""
This class contains definitions on how to run programs and should
be extended in ranger.apps
The user can decide what program to run, and if he uses eg. 'vim', the
function app_vim() will be called. However, usually the user
simply wants to "start" the file without specific instructions.
In such a case, app_default() is called, where you should examine
the context and decide which program to use.
All app functions have a name starting with app_ and return a string
containing the whole command or a tuple containing a list of the
arguments. They are supplied with one argument, which is the
AppContext instance.
You should define at least app_default, app_pager and app_editor since
internal functions depend on those. Here are sample implementations:
def app_default(self, context):
if context.file.media:
if context.file.video:
# detach videos from the filemanager
context.flags += 'd'
return self.app_mplayer(context)
else:
return self.app_editor(context)
def app_pager(self, context):
return 'less', context
def app_editor(self, context):
return ('vim', context)
"""
def _meets_dependencies(self, fnc):
try:
deps = fnc.dependencies
except AttributeError:
return True
for dep in deps:
if hasattr(dep, 'dependencies') \
and not self._meets_dependencies(dep):
return False
if dep not in get_executables():
return False
return True
def either(self, context, *args):
for app in args:
try:
application_handler = getattr(self, 'app_' + app)
except AttributeError:
if app in get_executables():
return _generic_app(app, context)
continue
if self._meets_dependencies(application_handler):
return application_handler(context)
def app_self(self, context):
"""Run the file itself"""
return "./" + context.file.basename
def get(self, app):
"""Looks for an application, returns app_default if it doesn't exist"""
try:
return getattr(self, 'app_' + app)
except AttributeError:
return self.app_default
def apply(self, app, context):
if not app:
app = 'default'
try:
handler = getattr(self, 'app_' + app)
except AttributeError:
if app in get_executables():
return [app] + list(context)
handler = self.app_default
arguments = handler(context)
# flatten
if isinstance(arguments, str):
return (arguments, )
if arguments is None:
return None
result = []
for obj in arguments:
if isinstance(obj, (tuple, list, Context)):
result.extend(obj)
else:
result.append(obj)
return result
def has(self, app):
"""Returns whether an application is defined"""
return hasattr(self, 'app_' + app)
def all(self):
"""Returns a list with all application functions"""
result = set()
# go through all the classes in the mro (method resolution order)
# so subclasses will return the apps of their superclasses.
for cls in self.__class__.__mro__:
result |= set(m[4:] for m in cls.__dict__ if m.startswith('app_'))
return sorted(result)
@classmethod
def generic(cls, *args, **keywords):
flags = 'flags' in keywords and keywords['flags'] or ""
for name in args:
assert isinstance(name, str)
if not hasattr(cls, "app_" + name):
setattr(cls, "app_" + name, _generic_wrapper(name, flags=flags))
def tup(*args):
"""
This helper function creates a tuple out of the arguments.
('a', ) + tuple(some_iterator)
is equivalent to:
tup('a', *some_iterator)
"""
return args
def depends_on(*args):
args = tuple(flatten(args))
def decorator(fnc):
fnc.dependencies = args
return fnc
return decorator
def _generic_app(name, context, flags=''):
"""Use this function when no other information is given"""
context.flags += flags
return name, context
def _generic_wrapper(name, flags=''):
"""Wraps _generic_app into a method for Applications"""
assert isinstance(name, str)
return depends_on(name)(lambda self, context:
_generic_app(name, context, flags))