1/*
2 * ModeSwitcher.bsh - a BeanShell macro script for changing the current
3 * buffer's edit mode.
4 *
5 * Copyright (C) 2004-6 Nicholas O'Leary nick.oleary@gmail.com
6 *
7 * :mode=beanshell:tabSize=3:indentSize=3:maxLineLen=0:noTabs=false:
8 * :indentOnTab=true:indentOnEnter=true:folding=explicit:collapseFolds=1:
9 *
10 * {{{ License
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with the jEdit program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24 * }}}
25 *
26 * Notes:
27 *  There are two other ways to change the buffers mode:
28 *    - enter 'buffer.mode=[mode]' in the action bar
29 *    - change it in the Buffer Options dialog
30 *  Whilst both of these do the job, I wanted a way to achieve it with minimum
31 *  effort, and keypresses.
32 *  It also has the benefit of auto-completion of mode names.
33 *
34 * $Id: Mode_Switcher.bsh 21353 2012-03-14 09:46:51Z jojaba_67 $
35 */
36
37import javax.swing.border.EmptyBorder;
38
39//Localization
40final static String MainDialogTitle = jEdit.getProperty("macro.rs.BufferModeSwitcher.MainDialog.title", "Buffer Mode Switcher");
41final static String EnterBufferModeLabel = jEdit.getProperty("macro.rs.BufferModeSwitcher.EnterBufferMode.label", "Enter buffer mode:");
42final static String ChangingBufferModeLabel = jEdit.getProperty("macro.rs.BufferModeSwitcher.ChangingBufferMode.label", "Changing mode of buffer");
43final static String ToLabel = jEdit.getProperty("macro.rs.BufferModeSwitcher.To.label", "to");
44final static String ModeLabel = jEdit.getProperty("macro.rs.BufferModeSwitcher.Mode.label", "Mode");
45final static String NotFoundLabel = jEdit.getProperty("macro.rs.BufferModeSwitcher.NotFound.label", "Not found");
46
47//class ModeSwitcherTextField extends JTextField
48void modeSwitcher() {
49	JDialog dialog = new JDialog(view, MainDialogTitle, true);
50	dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
51	JPanel content = new JPanel(new BorderLayout());
52	content.setBorder(new EmptyBorder(12,12,12,12));
53	Mode[] modes = jEdit.getModes();
54	String[] names = new String[modes.length];
55	for(int i=0;i<modes.length;i++) {
56		names[i] = modes[i].getName();
57	}
58	Arrays.sort(names);
59	JTextField textfield = new JTextField() {
60		protected String[] names;
61		protected boolean shifted = false;
62		//{{{ setNames
63		public void setNames(String[] a){
64			names = a;
65		} //}}}
66		public boolean getFocusTraversalKeysEnabled(){return false;}
67		//{{{ processKeyEvent
68		protected void processKeyEvent(KeyEvent evt)
69		{
70			if (evt.getID() == KeyEvent.KEY_RELEASED) {
71				if (evt.getKeyCode() == KeyEvent.VK_SHIFT) {
72					shifted = false;
73				}
74			} else if(evt.getID() == KeyEvent.KEY_PRESSED) {
75				if (evt.getKeyCode() == KeyEvent.VK_SHIFT) {
76					shifted = true;
77				} else if(evt.getKeyCode() == KeyEvent.VK_TAB) {
78					// Get the current text
79					String txt = getText();
80					String original = txt;
81					// See if some text is selected
82					if (getSelectedText() != null)
83						txt = txt.substring(0,txt.length()-getSelectedText().length());
84					// txt represents the unhighlighted text in the box. This is used
85					// to find further matches.
86
87					// See if the current text is a known mode
88					int index = Arrays.binarySearch(names,original);
89					if (index < 0) index = 0;
90					int indexStep = 1;
91					if (shifted) indexStep = -1;
92					index+=indexStep;
93					if (index == names.length) index = 0;
94					if (index < 0) index = names.length-1;
95					int match = -1;
96					boolean foundExact = false;
97					boolean keepLooping = true;
98					// Loop through modes, starting at current+1
99					int i = index;
100					while(keepLooping) {
101						// Skip if the mode name is shorter than the current text
102						if (names[i].length()>=txt.length())
103						{
104							// If the mode matches, escape
105							if (names[i].substring(0,txt.length()).equals(txt)) {
106								match = i;
107								break;
108							}
109						}
110						// Loop the loop
111						i+=indexStep;
112						if (i == names.length) i = 0;
113						if (i < 0) i = names.length-1;
114						if (i==index) break;
115					}
116					// If a match has been found...
117					if (match >= 0) {
118						setText(names[match]);
119						setSelectionStart(txt.length());
120						setSelectionEnd(names[match].length());
121					}
122					return;
123				}
124			}
125			super.processKeyEvent(evt);
126		} //}}}
127	};
128
129	textfield.setColumns(20);
130	textfield.setNames(names);
131	Mode m = buffer.getMode();
132	// Set the inital text to the current mode, and highlight it, so a key
133	// press will clear the entry.
134	if (m != null) {
135		textfield.setText(m.getName());
136		textfield.setSelectionStart(0);
137		textfield.setSelectionEnd(m.getName().length());
138	}
139	content.add(new JLabel(EnterBufferModeLabel), BorderLayout.NORTH);
140	content.add(textfield, BorderLayout.CENTER);
141	Vector v = new Vector();
142
143	// KeyListener Interface
144	//{{{ keyPressed
145	void keyPressed(evt)
146	{
147		if(evt.getKeyCode() == KeyEvent.VK_ESCAPE)
148			dialog.dispose();
149		else if(evt.getKeyCode() == KeyEvent.VK_ENTER)
150		{
151			Mode m = jEdit.getMode(textfield.getText());
152			if (m!=null)
153			{
154				buffer.setMode(m);
155				Log.log(Log.NOTICE,
156						  BeanShell.class,
157						  ChangingBufferModeLabel+" ["+
158							  buffer.getName()+"] "+ToLabel+" ["+
159							  m.getName()+"]");
160			} else {
161				Log.log(Log.WARNING,
162						  BeanShell.class,
163						  ModeLabel+" ["+textfield.getText()+"] "+NotFoundLabel);
164			}
165			evt.consume();
166			dialog.dispose();
167		}
168	}//}}}
169	void keyReleased(evt) {}
170	void keyTyped(evt) {}
171
172	dialog.addKeyListener(this);
173	textfield.addKeyListener(this);
174	dialog.setContentPane(content);
175	dialog.pack();
176	dialog.setLocationRelativeTo(view);
177	textfield.grabFocus();
178	dialog.setVisible(true);
179}
180
181modeSwitcher();
182
183/*
184
185Macro index data (in DocBook format)
186
187   <listitem>
188      <para><filename>Mode_Switcher.bsh</filename></para>
189      Displays a modal dialog with the current buffer's mode in a text field,
190      allowing one to change the mode by typing in its name.
191      <keycap>ENTER</keycap> selects the current mode; if the text is not a
192      valid mode, the dialog still dismisses, but a warning is logged to the
193      activity log.
194      <keycap>ESACPE</keycap> closes the dialog with no further action.
195      <keycap>TAB</keycap> attempts to auto-complete the mode name. Pressing
196      <keycap>TAB</keycap> repeatedly cycles through the possible completions.
197		<keycap>SHIFT-TAB</keycap> cycles through the completions in reverse.
198      </para></abstract>
199   </listitem>
200
201*/
202