/** * Warlock, the open-source cross-platform game client * * Copyright 2008, Warlock LLC, and individual contributors as indicated * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package cc.warlock.rcp.prefs; import java.util.ArrayList; import java.util.Collection; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.jface.bindings.keys.KeyStroke; import org.eclipse.jface.bindings.keys.SWTKeySupport; import org.eclipse.jface.fieldassist.IContentProposal; import org.eclipse.jface.fieldassist.IContentProposalProvider; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchPropertyPage; import org.eclipse.ui.part.PageBook; import cc.warlock.core.client.IWarlockClient; import cc.warlock.core.client.settings.internal.ClientSettings; import cc.warlock.core.client.settings.macro.CommandMacroHandler; import cc.warlock.core.client.settings.macro.IMacro; import cc.warlock.core.client.settings.macro.IMacroCommand; import cc.warlock.core.client.settings.macro.IMacroHandler; import cc.warlock.core.client.settings.macro.IMacroProvider; import cc.warlock.core.client.settings.macro.internal.Macro; import cc.warlock.rcp.ui.ContentAssistCellEditor; import cc.warlock.rcp.ui.KeyStrokeCellEditor; import cc.warlock.rcp.ui.KeyStrokeText; import cc.warlock.rcp.ui.WarlockSharedImages; import cc.warlock.rcp.ui.KeyStrokeText.KeyStrokeLockListener; /** * * @author Marshall Culpepper */ public class MacrosPreferencePage extends PreferencePageUtils implements IWorkbenchPropertyPage { public static final String PAGE_ID = "cc.warlock.rcp.prefs.macros"; protected static String COLUMN_COMMAND = "command"; protected static String COLUMN_KEY = "key"; protected TableViewer macroTableView; protected IWarlockClient client; protected ClientSettings settings; protected ArrayList<Macro> macros = new ArrayList<Macro>(); protected Button addMacroButton; protected Button removeMacroButton; protected Button clearMacrosButton; protected Button defaultMacrosButton; protected Macro selectedMacro; protected PageBook filterBook; protected Text commandText; protected KeyStrokeText keyComboText; protected Menu filterMenu; protected boolean filterByCommand = true; @Override protected Control createContents(Composite parent) { Composite main = new Composite(parent, SWT.NONE); main.setLayout(new GridLayout(2, false)); Composite filterComposite = new Composite(main, SWT.NONE); GridLayout layout = new GridLayout(3, false); layout.marginWidth = layout.marginHeight = 0; filterComposite.setLayout(layout); GridData data = new GridData(SWT.FILL, SWT.FILL, true, false); // data.horizontalSpan = 2; filterComposite.setLayoutData(data); new Label(filterComposite, SWT.NONE).setText("Filter: "); filterBook = new PageBook(filterComposite, SWT.NONE); filterBook.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); commandText = new Text(filterBook, SWT.BORDER); commandText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { macroTableView.refresh(); } }); keyComboText = new KeyStrokeText(filterBook, SWT.BORDER); keyComboText.addKeyStrokeLockListener(new KeyStrokeLockListener() { public void keyStrokeLocked() { macroTableView.refresh(); } public void keyStrokeUnlocked() { macroTableView.refresh(); } }); filterBook.showPage(commandText); Button filterButton = new Button(filterComposite, SWT.ARROW | SWT.DOWN); filterMenu = new Menu(filterButton); filterButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { filterMenu.setVisible(true); } }); MenuItem filterByCommand = new MenuItem(filterMenu, SWT.RADIO); filterByCommand.setText("Filter by command"); filterByCommand.setSelection(true); filterByCommand.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { MacrosPreferencePage.this.filterByCommand = true; filterBook.showPage(commandText); macroTableView.refresh(); } }); MenuItem filterByKeyCombo= new MenuItem(filterMenu, SWT.RADIO); filterByKeyCombo.setText("Filter by key combo"); filterByKeyCombo.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { MacrosPreferencePage.this.filterByCommand = false; filterBook.showPage(keyComboText.getText()); macroTableView.refresh(); } }); filterButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, false)); new Label(main, SWT.NONE); macroTableView = new TableViewer(main, SWT.SINGLE | SWT.BORDER | SWT.V_SCROLL); TableViewerColumn commandColumn = new TableViewerColumn(macroTableView, SWT.NONE, 0); commandColumn.getColumn().setText("Command"); commandColumn.getColumn().setWidth(225); commandColumn.setEditingSupport(new CommandEditingSupport(macroTableView)); TableViewerColumn keyColumn = new TableViewerColumn(macroTableView, SWT.NONE, 1); keyColumn.getColumn().setText("Key Combination"); keyColumn.getColumn().setWidth(125); keyColumn.setEditingSupport(new KeyStrokeEditingSupport(macroTableView)); macroTableView.setUseHashlookup(true); macroTableView.setColumnProperties(new String[] {COLUMN_COMMAND, COLUMN_KEY}); macroTableView.setContentProvider(new ArrayContentProvider()); macroTableView.setLabelProvider(new LabelProvider()); macroTableView.addFilter(new ViewerFilter() { public boolean select(Viewer viewer, Object parentElement, Object element) { IMacro macro = (IMacro) element; Collection<IMacroHandler> handlers = macro.getHandlers(); if (handlers.size() == 1) { IMacroHandler handler = (IMacroHandler) handlers.toArray()[0]; if (handler instanceof CommandMacroHandler) { return true; } } return false; } }); macroTableView.addFilter(new MacroFilter()); macroTableView.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { if (macroTableView.getSelection().isEmpty()) { removeMacroButton.setEnabled(false); } else { selectedMacro = (Macro) ((IStructuredSelection)macroTableView.getSelection()).getFirstElement(); removeMacroButton.setEnabled(true); } } }); for (IMacro macro : settings.getAllMacros()) { if (macro instanceof Macro) { macros.add(new Macro((Macro)macro)); } } macroTableView.setInput(macros); macroTableView.getTable().setHeaderVisible(true); int listHeight = macroTableView.getTable().getItemHeight() * 8; Rectangle trim = macroTableView.getTable().computeTrim(0, 0, 0, listHeight); data = new GridData(GridData.FILL, GridData.FILL, true, true); data.heightHint = trim.height; macroTableView.getTable().setLayoutData(data); Composite macroButtons = new Composite(main, SWT.NONE); macroButtons.setLayout(new GridLayout(1, true)); macroButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); addMacroButton = new Button(macroButtons, SWT.PUSH); addMacroButton.setText("Add Macro"); addMacroButton.setImage(WarlockSharedImages.getImage(WarlockSharedImages.IMG_ADD)); addMacroButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { addMacroSelected(); } }); addMacroButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); removeMacroButton = new Button(macroButtons, SWT.PUSH); removeMacroButton.setText("Remove Macro"); removeMacroButton.setImage(WarlockSharedImages.getImage(WarlockSharedImages.IMG_REMOVE)); removeMacroButton.setEnabled(false); removeMacroButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { removeMacroSelected(); } }); removeMacroButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); Label filler = new Label(macroButtons, SWT.NONE); filler.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); clearMacrosButton = new Button(macroButtons, SWT.PUSH); clearMacrosButton.setText("Clear Macros"); clearMacrosButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { clearMacros(); } }); clearMacrosButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); defaultMacrosButton = new Button(macroButtons, SWT.PUSH); defaultMacrosButton.setText("Reset to Defaults"); defaultMacrosButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { setupDefaultMacros(); } }); defaultMacrosButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); return main; } protected class MacroFilter extends ViewerFilter { public boolean select(Viewer viewer, Object parentElement, Object element) { IMacro macro = (IMacro)element; if (filterByCommand) { String command = getCommandMacroHandler(macro).getCommand(); if (command.equals("")) { return true; } return command.toLowerCase().contains(commandText.getText().toLowerCase()); } else { KeyStroke stroke = keyComboText.getKeyStroke(); if (stroke != null && stroke.getNaturalKey() != KeyStroke.NO_KEY) { return (stroke.getModifierKeys() == macro.getModifiers() && stroke.getNaturalKey() == macro.getKeyCode()); } return true; } } } protected ArrayList<Macro> addedMacros = new ArrayList<Macro>(); protected void addMacroSelected () { Macro macro = new Macro(settings.getMacroConfigurationProvider(), 0); macro.addHandler(new CommandMacroHandler("")); addedMacros.add(macro); macros.add(macro); macroTableView.add(macro); macroTableView.editElement(macro, 0); } protected ArrayList<Macro> removedMacros = new ArrayList<Macro>(); protected void removeMacroSelected () { addedMacros.remove(selectedMacro); if (macros.remove(selectedMacro)) { removedMacros.add(selectedMacro); } macroTableView.remove(selectedMacro); } protected void setupDefaultMacros() { // There probably is a better place to put this. clearMacros(); createRawMacro("\\xxml toggle containers\\r",99,65536); createRawMacro("\\xxml toggle dialogs\\r",100,65536); createRawMacro("{ExportDialog}",101,327680); createRawMacro("{HighlightsDialog}",104,327680); createRawMacro("{ImportDialog}",105,327680); createRawMacro("{ToggleLinks}",108,65536); createRawMacro("{ToggleMusic}",109,65536); createRawMacro("{ToggleImages}",105,65536); createRawMacro("{ToggleSounds}",115,65536); createRawMacro("{MacrosDialog}",109,327680); createRawMacro("{ChooseSkin}",115,327680); createRawMacro("{VariablesDialog}",118,65536); createRawMacro("{MacroSet}0",48,65536); createRawMacro("{MacroSet}1",49,65536); createRawMacro("{MacroSet}2",50,65536); createRawMacro("{MacroSet}3",51,65536); createRawMacro("{MacroSet}4",52,65536); createRawMacro("{MacroSet}5",53,65536); createRawMacro("{MacroSet}6",54,65536); createRawMacro("{MacroSet}7",55,65536); createRawMacro("{MacroSet}8",56,65536); createRawMacro("{MacroSet}9",57,65536); createRawMacro("{Restart}",16777230,262144); createRawMacro("\\xretreat\\r",114,262144); createRawMacro("\\xlook\\r",16777259,0); createRawMacro("\\xhealth\\r",16777263,0); createRawMacro("\\xnotoriety\\r",16777258,0); createRawMacro("\\xmana\\r",16777261,0); createRawMacro("\\xup\\r",16777262,0); createRawMacro("\\xdown\\r",16777264,0); createRawMacro("\\xsw\\r",16777265,0); createRawMacro("\\xs\\r",16777266,0); createRawMacro("\\xse\\r",16777267,0); createRawMacro("\\xw\\r",16777268,0); createRawMacro("\\xout\\r",16777269,0); createRawMacro("\\xe\\r",16777270,0); createRawMacro("\\xnw\\r",16777271,0); createRawMacro("\\xn\\r",16777272,0); createRawMacro("\\xne\\r",16777273,0); createRawMacro("{PageUp}",16777221,0); createRawMacro("{PageDown}",16777222,0); createRawMacro("{LineUp}",16777221,131072); createRawMacro("{LineDown}",16777222,131072); createRawMacro("{HistoryPrev}",16777217,0); createRawMacro("{HistoryNext}",16777218,0); createRawMacro("{RepeatLast}",13,262144); createRawMacro("{RepeatSecondToLast}",13,65536); createRawMacro("{ReturnOrRepeatLast}",16777296,0); createRawMacro("{RepeatLast}",16777296,262144); createRawMacro("{RepeatSecondToLast}",16777296,65536); createRawMacro("{CycleWindows}",9,0); createRawMacro("{BufferTop}",16777221,262144); createRawMacro("{BufferBottom}",16777222,262144); createRawMacro("{BufferTop}",16777223,262144); createRawMacro("{BufferBottom}",16777224,262144); createRawMacro("{CycleWindowsReverse}",9,131072); createRawMacro("\\xassess\\r",97,262144); createRawMacro("{copy}",99,262144); createRawMacro("{cut}",120,262144); createRawMacro("{paste}",118,262144); createRawMacro("{PauseScript}",27,131072); createRawMacro("peer down\\r",16777264,262144); createRawMacro("\\xpath focus damage\\r",16777265,131072); createRawMacro("\\xaft to port\\r",16777265,65536); createRawMacro("peer sw\\r",16777265,262144); createRawMacro("\\xpath focus quick\\r",16777266,131072); createRawMacro("\\xaft\\r",16777266,65536); createRawMacro("peer s\\r",16777266,262144); createRawMacro("\\xpath focus ease\\r",16777267,131072); createRawMacro("\\xaft to starboard\\r",16777267,65536); createRawMacro("peer se\\r",16777267,262144); createRawMacro("\\xport\\r",16777268,65536); createRawMacro("peer w\\r",16777268,262144); createRawMacro("peer out\\r",16777269,262144); createRawMacro("\\xstarboard\\r",16777270,65536); createRawMacro("peer e\\r",16777270,262144); createRawMacro("\\xforward to port\\r",16777271,65536); createRawMacro("peer nw\\r",16777271,262144); createRawMacro("\\xforward\\r",16777272,65536); createRawMacro("peer n\\r",16777272,262144); createRawMacro("\\xforward to starboard\\r",16777273,65536); createRawMacro("peer ne\\r",16777273,262144); createRawMacro("peer up\\r",16777262,262144); createRawMacro("\\xpath sense\\r",16777263,131072); createRawMacro("\\xpath check\\r",16777258,131072); createRawMacro("\\xdemeanor neutral\\r",101,65536); createRawMacro("\\xdemeanor reserved\\r",119,65536); createRawMacro("\\xdemeanor cold\\r",113,65536); createRawMacro("\\xget \\?\\rput \\? in my %container\\r",116,262144); createRawMacro("\\xremove my %helmet\\rput my %helmet in my %container\\r\\premove my %gloves\\rput my %gloves in my %container\\r",16777235,0); createRawMacro("\\xopen my \\?\\ropen my %gpouch\\r\\pfill my %gpouch with my \\?\\r\\pclose my %gpouch\\rclose my \\?\\r",16777228,0); createRawMacro("\\xopen my %gpouch\\rappr my %gpouch\\r\\p\\p\\p\\p\\p\\p\\p\\p\\p\\xclose my %gpouch\\r",16777230,0); createRawMacro("\\xopen my \\?\\rl in my \\?\\r\\pclose my \\?\\r",16777231,0); createRawMacro("\\xget my %helmet\\rwear my %helmet\\r\\pget my %gloves\\rwear my %gloves\\r",16777234,0); createRawMacro("\\xdemeanor friendly\\r",114,65536); createRawMacro("\\xdemeanor warm\\r",116,65536); createRawMacro("\\xpath focus power\\r",16777268,131072); createRawMacro("\\xremove my %shield\\rstow my %shield\\r",16777235,262144); createRawMacro("\\xget my %shield\\rwear my %shield\\r",16777234,262144); } // Mostly just to support setupDefaultMacros protected void createRawMacro(String cmd, int keycode, int keymod) { Macro macro = new Macro(settings.getMacroConfigurationProvider(), 0); macro.setModifiers(keymod); macro.setKeyCode(keycode); macro.addHandler(new CommandMacroHandler(cmd)); addedMacros.add(macro); macros.add(macro); macroTableView.add(macro); } protected void clearMacros() { // Clear out all exiting macros. Table macroTable = macroTableView.getTable(); if(macroTable != null) macroTable.clearAll(); addedMacros.clear(); for (Macro currentMacro: macros) { // System Macros have no handlers, and shouldn't be removed. if (!currentMacro.getHandlers().isEmpty()) { macroTableView.remove(currentMacro); removedMacros.add(currentMacro); } } } protected CommandMacroHandler getCommandMacroHandler (IMacro macro) { return (CommandMacroHandler)macro.getHandlers().toArray()[0]; } protected class LabelProvider implements ITableLabelProvider { public void addListener(ILabelProviderListener listener) {} public void dispose() {} public Image getColumnImage(Object element, int columnIndex) { return null; } public String getColumnText(Object element, int columnIndex) { IMacro macro = (IMacro) element; if (columnIndex == 0) { return getCommandMacroHandler(macro).getCommand(); } else { return SWTKeySupport.getKeyFormatterForPlatform().format(KeyStroke.getInstance(macro.getModifiers(), macro.getKeyCode())); } } public boolean isLabelProperty(Object element, String property) { return true; } public void removeListener(ILabelProviderListener listener) {} } protected class CommandEditingSupport extends EditingSupport implements IContentProposalProvider { protected ContentAssistCellEditor editor; public CommandEditingSupport (TableViewer viewer) { super(viewer); editor = new ContentAssistCellEditor(macroTableView.getTable(), SWT.SINGLE, new char[] { '{', '$', '\\' }, this); } protected boolean canEdit(Object element) { return true; } protected CellEditor getCellEditor(Object element) { return editor; } protected Object getValue(Object element) { return getCommandMacroHandler((IMacro)element).getCommand(); } protected void setValue(Object element, Object value) { getCommandMacroHandler((IMacro)element).setCommand((String)value); ((Macro)element).setNeedsUpdate(true); macroTableView.update(element, null); } protected class MacroCommandContentProposal implements IContentProposal { protected IMacroCommand command; protected String contents; protected int position; public MacroCommandContentProposal (IMacroCommand command, String contents, int position) { this.command = command; this.contents = contents; this.position = position; } public String getContent() { String content = "{" + this.command.getIdentifier() + "}"; int leftBracketIndex = contents.substring(0, position).lastIndexOf('{'); String text = contents.substring(0, leftBracketIndex) + content + contents.substring(position); return text; } public int getCursorPosition() { return getContent().length(); } public String getDescription() { return command.getDescription(); } public String getLabel() { return "{"+command.getIdentifier()+"}"; } } public IContentProposal[] getProposals(String contents, int position) { ArrayList<IContentProposal> proposals = new ArrayList<IContentProposal>(); int lastRightBracket = contents.substring(0, position).lastIndexOf('{'); if (lastRightBracket >= 0) { String commandSubstr = contents.substring(lastRightBracket+1, position).toLowerCase(); for (IMacroCommand command : settings.getAllMacroCommands()) { if (command.getIdentifier().toLowerCase().startsWith(commandSubstr)) { proposals.add(new MacroCommandContentProposal( command, contents.substring(0, contents.length()-1), position-1)); } } } return proposals.toArray(new IContentProposal[proposals.size()]); } } protected class KeyStrokeEditingSupport extends EditingSupport { protected KeyStrokeCellEditor editor; public KeyStrokeEditingSupport (TableViewer viewer) { super(viewer); this.editor = new KeyStrokeCellEditor(macroTableView.getTable(), SWT.SINGLE); } protected boolean canEdit(Object element) { return true; } protected CellEditor getCellEditor(Object element) { return editor; } protected Object getValue(Object element) { Macro macro = (Macro) element; return KeyStroke.getInstance(macro.getModifiers(), macro.getKeyCode()); } protected void setValue(Object element, Object value) { Macro macro = (Macro) element; KeyStroke stroke = (KeyStroke) value; macro.setModifiers(stroke.getModifierKeys()); macro.setKeyCode(stroke.getNaturalKey()); macroTableView.update(macro, null); } } @Override public void setElement(IAdaptable element) { client = (IWarlockClient)element.getAdapter(IWarlockClient.class); settings = (ClientSettings)client.getClientSettings(); } @Override public boolean performOk() { for (Macro macro : macros) { if (macro.needsUpdate() && !addedMacros.contains(macro)) { IMacroProvider provider = (IMacroProvider) macro.getProvider(); provider.replaceMacro(macro.getOriginalMacro(), macro); } } for (Macro macro : removedMacros) { IMacroProvider provider = (IMacroProvider) macro.getProvider(); provider.removeMacro(macro.getOriginalMacro()); } for (Macro macro : addedMacros) { settings.getMacroConfigurationProvider().addMacro(macro); } return true; } }