about summary refs log tree commit diff stats
path: root/config.arg.h
blob: 781782593aad036c58b20a4fc1af448e47ef1786 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */

#define TAGS \
const char *tags[] = { "dev", "work", "net", "fnord", NULL };

#define DEFMODE			dotile		/* dofloat */
#define FLOATSYMBOL		"><>"
#define STACKPOS		StackRight	/* StackLeft */
#define BSTACKSYMBOL		"==="
#define VSTACKSYMBOL		"[]="

#define FONT			"-*-terminus-medium-*-*-*-12-*-*-*-*-*-iso10646-*"
#define NORMBGCOLOR		"#333333"
#define NORMFGCOLOR		"#dddddd"
#define SELBGCOLOR		"#333366"
#define SELFGCOLOR		"#eeeeee"
#define STATUSBGCOLOR		"#222222"
#define STATUSFGCOLOR		"#9999cc"

#define MASTER			60 /* percent */
#define MODKEY			Mod1Mask

#define KEYS \
static Key key[] = { \
	/* modifier			key		function	arguments */ \
	{ MODKEY|ShiftMask,		XK_Return,	spawn, \
		{ .cmd = "exec uxterm -bg '#111111' -fg '#eeeeee' -cr '#eeeeee' +sb -fn '"FONT"'" } }, \
	{ MODKEY,			XK_p,		spawn, \
		{ .cmd = "exe=\"$(IFS=:; for dir in $PATH; do " \
			 "for file in \"$dir\"/*; do [ -x \"$file\" ] && echo \"${file##*/}\"; done; done " \
			 "| sort -u | dmenu -font '"FONT"' -normbg '"NORMBGCOLOR"' -normfg '"NORMFGCOLOR"' " \
			 "-selbg '"SELBGCOLOR"' -selfg '"SELFGCOLOR"')\" && exec $exe" } }, \
	{ MODKEY,			XK_j,		focusnext,	{ 0 } }, \
	{ MODKEY,			XK_k,		focusprev,	{ 0 } }, \
	{ MODKEY,			XK_Return,	zoom,		{ 0 } }, \
	{ MODKEY,			XK_b,		togglestackpos,	{ 0 } }, \
	{ MODKEY,			XK_g,		resizecol,	{ .i = 20 } }, \
	{ MODKEY,			XK_s,		resizecol,	{ .i = -20 } }, \
	{ MODKEY|ShiftMask,		XK_1,		tag,		{ .i = 0 } }, \
	{ MODKEY|ShiftMask,		XK_2,		tag,		{ .i = 1 } }, \
	{ MODKEY|ShiftMask,		XK_3,		tag,		{ .i = 2 } }, \
	{ MODKEY|ShiftMask,		XK_4,		tag,		{ .i = 3 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_1,		toggletag,	{ .i = 0 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_2,		toggletag,	{ .i = 1 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_3,		toggletag,	{ .i = 2 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_4,		toggletag,	{ .i = 3 } }, \
	{ MODKEY|ShiftMask,		XK_c,		killclient,	{ 0 } }, \
	{ MODKEY,			XK_space,	togglemode,	{ 0 } }, \
	{ MODKEY,			XK_0,		viewall,	{ 0 } }, \
	{ MODKEY,			XK_1,		view,		{ .i = 0 } }, \
	{ MODKEY,			XK_2,		view,		{ .i = 1 } }, \
	{ MODKEY,			XK_3,		view,		{ .i = 2 } }, \
	{ MODKEY,			XK_4,		view,		{ .i = 3 } }, \
	{ MODKEY|ControlMask,		XK_1,		toggleview,	{ .i = 0 } }, \
	{ MODKEY|ControlMask,		XK_2,		toggleview,	{ .i = 1 } }, \
	{ MODKEY|ControlMask,		XK_3,		toggleview,	{ .i = 2 } }, \
	{ MODKEY|ControlMask,		XK_4,		toggleview,	{ .i = 3 } }, \
	{ MODKEY|ShiftMask,		XK_q,		quit,		{ 0 } }, \
};

#define RULES \
static Rule rule[] = { \
	/* class:instance:title regex	tags regex	isfloat */ \
	{ "Firefox.*",			"net",		True}, \
	{ "Gimp.*",			NULL,		True}, \
	{ "MPlayer.*",			NULL,		True}, \
	{ "Acroread.*",			NULL,		True}, \
};
: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
# -*- encoding: utf8 -*-
# Copyright (C) 2009, 2010, 2011  Roman Zimbelmann <romanz@lavabit.com>
# This software is distributed under the terms of the GNU GPL version 3.

"""The BrowserColumn widget displays the contents of a directory or file."""
import curses
import stat
from time import time

from . import Widget
from .pager import Pager
from ranger.fsobject import BAD_INFO
from ranger.ext.widestring import WideString

class BrowserColumn(Pager):
	main_column = False
	display_infostring = False
	scroll_begin = 0
	target = None
	last_redraw_time = -1
	ellipsis = { False: '~', True: '…' }

	old_dir = None
	old_cf = None

	def __init__(self, win, level):
		"""
		win = the curses window object of the BrowserView
		level = what to display?

		level >0 => previews
		level 0 => current file/directory
		level <0 => parent directories
		"""
		Pager.__init__(self, win)
		Widget.__init__(self, win)
		self.level = level

		self.settings.signal_bind('setopt.display_size_in_main_column',
				self.request_redraw, weak=True)

	def request_redraw(self):
		self.need_redraw = True

	def resize(self, y, x, hei, wid):
		Widget.resize(self, y, x, hei, wid)

	def click(self, event):
		"""Handle a MouseEvent"""
		direction = event.mouse_wheel_direction()
		if not (event.pressed(1) or event.pressed(3) or direction):
			return False

		if self.target is None:
			pass

		elif self.target.is_directory:
			if self.target.accessible and self.target.content_loaded:
				index = self.scroll_begin + event.y - self.y

				if direction:
					if self.level == -1:
						self.fm.move_parent(direction)
					else:
						return False
				elif event.pressed(1):
					if not self.main_column:
						self.fm.enter_dir(self.target.path)

					if index < len(self.target):
						self.fm.move(to=index)
				elif event.pressed(3):
					try:
						clicked_file = self.target.files[index]
						if clicked_file.is_directory:
							self.fm.enter_dir(clicked_file.path)
						elif self.level == 0:
							self.fm.env.cwd.move_to_obj(clicked_file)
							self.fm.execute_file(clicked_file)
					except:
						pass

		else:
			if self.level > 0 and not direction:
				self.fm.move(right=0)

		return True

	def execute_curses_batch(self, line, commands):
		"""
		Executes a list of "commands" which can be easily cached.

		"commands" is a list of lists.  Each element contains
		a text and an attribute.  First, the attribute will be
		set with attrset, then the text is printed.

		Example:
		execute_curses_batch(0, [["hello ", 0], ["world", curses.A_BOLD]])
		"""
		self.win.move(line, 0)
		for entry in commands:
			text, attr = entry
			self.addstr(text, attr)

	def has_preview(self):
		if self.target is None:
			return False

		if self.target.is_file:
			if not self.target.has_preview():
				return False

		if self.target.is_directory:
			if self.level > 0 and not self.settings.preview_directories:
				return False

		return True

	def poke(self):
		Widget.poke(self)
		self.target = self.env.at_level(self.level)

	def draw(self):
		"""Call either _draw_file() or _draw_directory()"""
		if self.target != self.old_dir:
			self.need_redraw = True
			self.old_dir = self.target

		if self.target:  # don't garbage collect this directory please
			self.target.use()

		if self.target and self.target.is_directory \
				and (self.level <= 0 or self.settings.preview_directories):
			if self.target.pointed_obj != self.old_cf:
				self.need_redraw = True
				self.old_cf = self.target.pointed_obj

			if self.target.load_content_if_outdated() \
			or self.target.sort_if_outdated() \
			or self.last_redraw_time < self.target.last_update_time:
				self.need_redraw = True

		if self.need_redraw:
			self.win.erase()
			if self.target is None:
				pass
			elif self.target.is_file:
				Pager.open(self)
				self._draw_file()
			elif self.target.is_directory:
				self._draw_directory()
				Widget.draw(self)
			self.need_redraw = False
			self.last_redraw_time = time()

	def _draw_file(self):
		"""Draw a preview of the file, if the settings allow it"""
		self.win.move(0, 0)
		if not self.target.accessible:
			self.addnstr("not accessible", self.wid)
			Pager.close(self)
			return

		if self.target is None or not self.target.has_preview():
			Pager.close(self)
			return

		f = self.target.get_preview_source(self.wid, self.hei)
		if f is None:
			Pager.close(self)
		else:
			self.set_source(f)
			Pager.draw(self)

	def _draw_directory(self):
		"""Draw the contents of a directory"""

		if self.level > 0 and not self.settings.preview_directories:
			return

		base_color = ['in_browser']

		self.win.move(0, 0)

		if not self.target.content_loaded:
			self.color(tuple(base_color))
			self.addnstr("...", self.wid)
			self.color_reset()
			return

		if self.main_column:
			base_color.append('main_column')

		if not self.target.accessible:
			self.color(tuple(base_color + ['error']))
			self.addnstr("not accessible", self.wid)
			self.color_reset()
			return

		if self.target.empty():
			self.color(tuple(base_color + ['empty']))
			self.addnstr("empty", self.wid)
			self.color_reset()
			return

		self._set_scroll_begin()

		copied = [f.path for f in self.env.copy]
		ellipsis = self.ellipsis[self.settings.unicode_ellipsis]

		selected_i = self.target.pointer
		for line in range(self.hei):
			i = line + self.scroll_begin
			if line > self.hei:
				break

			try:
				drawn = self.target.files[i]
			except IndexError:
				break

			tagged = self.fm.tags and drawn.realpath in self.fm.tags
			if tagged:
				tagged_marker = self.fm.tags.marker(drawn.realpath)
			else:
				tagged_marker = " "

			key = (self.wid, selected_i == i, drawn.marked, self.main_column,
					drawn.path in copied, tagged_marker, drawn.infostring)

			if key in drawn.display_data:
				self.execute_curses_batch(line, drawn.display_data[key])
				self.color_reset()
				continue

			display_data = []
			drawn.display_data[key] = display_data

			if self.display_infostring and drawn.infostring \
					and self.settings.display_size_in_main_column:
				infostring = str(drawn.infostring) + " "
			else:
				infostring = ""

			this_color = base_color + list(drawn.mimetype_tuple)
			text = drawn.basename

			space = self.wid - len(infostring)
			if self.main_column:
				space -= 2
			elif self.settings.display_tags_in_all_columns:
				space -= 1

			if i == selected_i:
				this_color.append('selected')

			if drawn.marked:
				this_color.append('marked')
				if self.main_column or self.settings.display_tags_in_all_columns:
					text = " " + text

			if tagged:
				this_color.append('tagged')

			if drawn.is_directory:
				this_color.append('directory')
			else:
				this_color.append('file')

			if drawn.stat:
				mode = drawn.stat.st_mode
				if mode & stat.S_IXUSR:
					this_color.append('executable')
				if stat.S_ISFIFO(mode):
					this_color.append('fifo')
				if stat.S_ISSOCK(mode):
					this_color.append('socket')
				if drawn.is_device:
					this_color.append('device')

			if drawn.path in copied:
				this_color.append('cut' if self.env.cut else 'copied')

			if drawn.is_link:
				this_color.append('link')
				this_color.append(drawn.exists and 'good' or 'bad')

			attr = self.settings.colorscheme.get_attr(*this_color)

			if (self.main_column or self.settings.display_tags_in_all_columns) \
					and tagged and self.wid > 2:
				this_color.append('tag_marker')
				tag_attr = self.settings.colorscheme.get_attr(*this_color)
				display_data.append([tagged_marker, tag_attr])
			else:
				text = " " + text

			wtext = WideString(text)
			if len(wtext) > space:
				wtext = wtext[:max(0, space - 1)] + ellipsis
			text = str(wtext)

			display_data.append([text, attr])

			if infostring:
				if len(text) + 1 + len(infostring) > self.wid:
					pass
				else:
					padding = self.wid - len(wtext) - len(infostring)
					if tagged and (self.main_column or \
							self.settings.display_tags_in_all_columns):
						padding -= 1
					padding = max(0, padding)
					infostring = (" " * padding) + infostring
					display_data.append([infostring, attr])
			else:
				display_data.append([" " * max(0, self.wid - len(wtext)), attr])

			self.execute_curses_batch(line, display_data)
			self.color_reset()

	def _get_scroll_begin(self):
		"""Determines scroll_begin (the position of the first displayed file)"""
		offset = self.settings.scroll_offset
		dirsize = len(self.target)
		winsize = self.hei
		halfwinsize = winsize // 2
		index = self.target.pointer or 0
		original = self.target.scroll_begin
		projected = index - original

		upper_limit = winsize - 1 - offset
		lower_limit = offset

		if original < 0:
			return 0

		if dirsize < winsize:
			return 0

		if halfwinsize < offset:
			return min( dirsize - winsize, max( 0, index - halfwinsize ))

		if original > dirsize - winsize:
			self.target.scroll_begin = dirsize - winsize
			return self._get_scroll_begin()

		if projected < upper_limit and projected > lower_limit:
			return original

		if projected > upper_limit:
			return min( dirsize - winsize,
					original + (projected - upper_limit))

		if projected < upper_limit:
			return max( 0,
					original - (lower_limit - projected))

		return original

	def _set_scroll_begin(self):
		"""Updates the scroll_begin value"""
		self.scroll_begin = self._get_scroll_begin()
		self.target.scroll_begin = self.scroll_begin

	def scroll(self, n):
		"""scroll down by n lines"""
		self.need_redraw = True
		self.target.move(down=n)
		self.target.scroll_begin += 3 * n

	def __str__(self):
		return self.__class__.__name__ + ' at level ' + str(self.level)