"""
Buffer_Switcher.py - A Jython macro for jEdit that allows one
to switch between or close open buffers without having to use
the mouse.

Copyright (C) 2002-2004 Ollie Rutherfurd <oliver@rutherfurd.net>

$Id: Buffer_Switcher.py,v 1.6 2002/11/27 15:03:54 oliver Exp $
"""

__version__ = '$Revision: 1.6 $'[11:-2]
__author__ = 'Ollie Rutherfurd'

from java.awt import BorderLayout
from java.awt.event import ActionListener, KeyEvent, KeyListener, MouseListener
from java.util import Vector
from javax.swing import Box, BoxLayout, DefaultListCellRenderer, JButton, \
	JDialog, JLabel, JList, JPanel, JScrollPane, JTextField, ListSelectionModel
from javax.swing.border import EmptyBorder
from org.gjt.sp.jedit import Macros
from org.gjt.sp.jedit.gui import HistoryTextField
from org.gjt.sp.jedit.io import VFS


class BufferCellRenderer(DefaultListCellRenderer):
	def __init__(self):
		DefaultListCellRenderer.__init__(self)

	def getListCellRendererComponent(self, jlist, value, index, isSelected, cellHasFocus):
		DefaultListCellRenderer.getListCellRendererComponent(self, jlist, value, index, isSelected, cellHasFocus)
		self.setIcon(value.getIcon())
		color = VFS.getDefaultColorFor(value.getName())
		if color:
			self.setForeground(color)
		return self


class BufferSwitcher(JDialog,ActionListener,KeyListener,MouseListener):

	def __init__(self,view,modal=1):
		JDialog.__init__(self,view,"%d Open Buffers" % len(jEdit.getBuffers()),modal)
		self.view = view

		panel = JPanel(BorderLayout(), border=EmptyBorder(12,12,12,12))
		self.setContentPane(panel)

		bufferList =  JList(jEdit.getBuffers(), keyListener=self, mouseListener=self)
		bufferList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
		bufferList.setCellRenderer(BufferCellRenderer())
		panel.add(JScrollPane(bufferList), BorderLayout.CENTER)
		pattern = HistoryTextField("macros.buffer-switcher.py");
		pattern.setEnterAddsToHistory(1);
		pattern.addActionListener(self)
		pattern.addKeyListener(self)
		self.pattern = pattern
		topPanel = JPanel(BorderLayout(),border=EmptyBorder(2,2,2,2))
		topPanel.add(JLabel('Pattern: '), BorderLayout.WEST)
		topPanel.add(pattern, BorderLayout.CENTER)
		panel.add(topPanel, BorderLayout.NORTH)
		self.bufferList = bufferList

		buttons = JPanel(border=EmptyBorder(12,50,0,50))
		buttons.setLayout(BoxLayout(buttons, BoxLayout.X_AXIS))

		self.ok = JButton("OK",actionListener=self)
		close = JButton("Close",actionListener=self)
		self.ok.setPreferredSize(close.getPreferredSize())
		self.getRootPane().setDefaultButton(self.ok)
		buttons.add(Box.createGlue())
		buttons.add(self.ok)
		buttons.add(Box.createHorizontalStrut(6))
		buttons.add(close)
		buttons.add(Box.createGlue())
		panel.add(buttons, BorderLayout.SOUTH)

		self.addKeyListener(self)

		self.pack()
		self.bufferList.setSelectedValue(init.buffer,1)
		self.setLocationRelativeTo(self.view)
		self.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
		self.setVisible(1)

	def switchToBuffer(self):
		b = self.bufferList.getSelectedValue()
		if b is None:
			self.view.getToolkit().beep()
		else:
			self.view.editPane.setBuffer(b)

	def closeBuffer(self):
		index = self.bufferList.getSelectedIndex()
		b = self.bufferList.getSelectedValue()
		if b is None:
			self.view.getToolkit().beep()
		else:
			if jEdit.closeBuffer(self.view, b):
				self.filterBuffers()
				# in case last item was removed
				if index == self.bufferList.getModel().getSize():
					index -= 1
				self.bufferList.setSelectedIndex(index)

	def filterBuffers(self,requestFocus=1):
		import re
		p = self.pattern.getText()
		if p:
			buffers = Vector()
			#r = re.compile(p,re.IGNORECASE)
			try:
				pattern,filters = globToRE(p)
				if not filters:
					filters = [lambda b: 1]
				r = re.compile(pattern,re.IGNORECASE)
			except:
				return
			for b in jEdit.getBuffers():
				if r.search(str(b)) and len(filters) == len(filter(None,[f(b) for f in filters])):
					buffers.addElement(b)
			self.bufferList.setListData(buffers)
		else:
			self.bufferList.setListData(jEdit.getBuffers())
		self.bufferList.setSelectedValue(self.view.buffer,1)
		if self.bufferList.getSelectedIndex() == -1:
			if self.bufferList.getModel().getSize() > 0:
				self.bufferList.setSelectedIndex(0)
				if requestFocus:
					self.bufferList.requestFocus()
		elif requestFocus:
			self.bufferList.requestFocus()

	def close(self):
		self.view = None
		self.dispose()

	def actionPerformed(self,evt):
		if evt.source == self.ok:
			self.switchToBuffer()
			self.close()
		elif evt.source == self.pattern:
			import sys; sys.stderr.write(self.pattern.getText())
			self.filterBuffers()
		else:
			self.close()

	def keyPressed(self,evt):
		if evt.getKeyCode() == KeyEvent.VK_ESCAPE:
			self.close()
		elif evt.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK and \
			 evt.getKeyCode() == KeyEvent.VK_L:
			self.pattern.requestFocus()
			self.pattern.selectAll()
		elif evt.getSource() == self.pattern:
			if evt.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK:
				if evt.getKeyCode() == KeyEvent.VK_J:
					idx = self.bufferList.getSelectedIndex()
					if idx > 0:
						self.bufferList.setSelectedIndex(idx-1)
						self.bufferList.ensureIndexIsVisible(idx-1)
				elif evt.getKeyCode() == KeyEvent.VK_K:
					idx = self.bufferList.getSelectedIndex()
					if idx == -1:
						return
					if (idx + 2) < self.bufferList.getModel().getSize():
						self.bufferList.setSelectedIndex(idx+1)
						self.bufferList.ensureIndexIsVisible(idx+1)
		else:
			if evt.getKeyCode() == KeyEvent.VK_SPACE:
				self.switchToBuffer()
			elif evt.getKeyCode() == KeyEvent.VK_DELETE:
				evt.consume()
				self.closeBuffer()

	def keyReleased(self,evt):
		if evt.getSource() == self.pattern:
			if evt.getKeyCode() in (KeyEvent.VK_UP, KeyEvent.VK_DOWN):
				pass
			elif evt.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK:
				pass
			else:
				self.filterBuffers(0)
		else:
			selected = self.bufferList.getSelectedIndex()
			if selected > -1:
				self.bufferList.ensureIndexIsVisible(selected)

	def keyTyped(self,evt):
		pass

	def mouseClicked(self,evt):
		if evt.clickCount > 1:
			self.switchToBuffer()
			self.close()

	mouseEntered = mouseExited = mousePressed = mouseReleased = lambda self,evt: None


def globToRE(pattern):
	import re
	filters = {
		('$d'): lambda b: b.isDirty(),
		('$D'): lambda b: not b.isDirty(),
		('$n'): lambda b: b.isNewFile(),
		('$N'): lambda b: not b.isNewFile(),
		('$u'): lambda b: b.isUntitled(),
		('$U'): lambda b: not b.isUntitled(),
		('$ro'): lambda b: b.isReadOnly(),
		('$RO'): lambda b: not b.isReadOnly(),
	}
	op = re.compile(r'(\$(?:[DdNnUu]|ro|RO))')
	ops = op.findall(pattern)		# extract all escapes
	pattern = op.sub('',pattern)	# remove escapes
	exp = []
	backslash = 0
	state = []
	for i in range(len(pattern)):
		c = pattern[i]
		if backslash:
			exp.append('\\'); exp.append(c)
			backslash = 0
		elif c == '\\':
			backslash = 1
		elif c in ('.','+','(',')'):
			exp.append('\\'); exp.append(c)
		elif c == '*':
			exp.append('.*')
		elif c == '|':
			if backslash:
				exp.append('\\|')
			else:
				exp.append(c)
		elif c == '{':
			exp.append('(')
			if (i+1) != len(pattern) and pattern[i+1] == '!':
				exp.append('?')
				state.append('NEG')
			else:
				state.append('GROUP')
		elif c == ',':
			if state and state[-1] == 'GROUP':
				exp.append('|')
			else:
				exp.append(',')
		elif c == '}':
			if state:
				exp.append(')')
				if state.pop() == 'NEG':
					exp.append('.*')
			else:
				exp.append('}')
		else:
			exp.append(c)
	return (''.join(exp),[filters[op] for op in ops if filters.has_key(op)])


if __name__ in ('__main__', 'main'):

	if jEdit.getProperty('macro.py_buffer_switcher.display-help', '1') == '1':

		Macros.message(init.view, """Help for Buffer Switcher macro:

DELETE: closes selected buffer.
ENTER: switches to selected buffer and closes dialog.
ESCAPE: closes dialog.
SPACE: switches to selected buffer.
C+L: moves focus to pattern field.

$d/$D: Show dirty/non-dirty buffers
$n/$N: Show new/non-new buffers
$u/$U: Show untitled/non-untitled buffers
$ro/$RO: Show read-only/non-read-only buffers

Examples: 

  Show dirty java buffers: $d*.java
  Show untitled buffers: $u
  Show dirty, untitled buffers: $d$u

Note: This message will only appear once.""")

		jEdit.setProperty('macro.py_buffer_switcher.display-help','0')

	bs = BufferSwitcher(init.view)

# :indentSize=4:lineSeparator=\n:noTabs=false:tabSize=4:

