package org.bbssh.ui.screens.macros;
import java.io.IOException;
import java.util.Vector;
import net.rim.device.api.i18n.ResourceBundle;
import net.rim.device.api.i18n.ResourceBundleFamily;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Keypad;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.BasicEditField;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.ListField;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.component.SeparatorField;
import net.rim.device.api.ui.component.Status;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;
import org.bbssh.i18n.BBSSHResource;
import org.bbssh.keybinding.PersistableCommandFactory;
import org.bbssh.model.KeyBindingManager;
import org.bbssh.model.Macro;
import org.bbssh.model.MacroManager;
import org.bbssh.platform.PlatformServicesProvider;
import org.bbssh.ui.components.BitmapButtonField;
import org.bbssh.ui.components.OKCancelControl;
import org.bbssh.ui.components.VectorListFieldCallback;
import org.bbssh.ui.components.keybinding.CommandBindingPopup;
import org.bbssh.ui.components.keybinding.KeybindState;
import org.bbssh.util.Tools;
public class MacroEditorScreen extends MainScreen implements FieldChangeListener, BBSSHResource {
ResourceBundleFamily res = ResourceBundle.getBundle(BBSSHResource.BUNDLE_ID, BBSSHResource.BUNDLE_NAME);
private boolean saved = false;
private CommandBindingPopup popup = new CommandBindingPopup(true);
private Macro macro;
private BasicEditField nameField;
private MacroPartList partsList;
private boolean editing;
VectorListFieldCallback cb;
// Even though this is a standard screen, we're still going to append "ok cancel"
// control: easier fro touchscreen users...
OKCancelControl okCancel;
private BitmapButtonField moveUp, moveDown;
private BitmapButtonField deletePart, editPart;
private BitmapButtonField newPart;
boolean ignoreNextListChange;
// shortcuts: alt up/down - move up down
// del - delete
// c - add/create
static class MacroPartList extends ListField {
protected void paint(Graphics graphics) {
super.paint(graphics);
// always draw as if we have focus even when we don't.
drawFocus(graphics, true);
}
protected void drawFocus(Graphics graphics, boolean on) {
// XYRect rect = new XYRect();
// // getFocusRect(rect);
// int idx = getSelectedIndex();
if (isFocus()) {
// If we're drawing focus, there's a fair chance that our selection
// has changed. Since the control doesn't provide any selection change notification
// we'll use this as the closest substitute.
fieldChangeNotify(1);
}
super.drawFocus(graphics, on);
}
}
// Alt+nav moves selection up/down.
protected boolean navigationMovement(int dx, int dy, int status, int time) {
if ((status & Keypad.KEY_ALT) > 0) {
int idx = partsList.getSelectedIndex();
if (idx > -1) {
if (dy > 0) {
moveDown(idx);
} else if (dy < 0) {
moveUp(idx);
}
}
}
return super.navigationMovement(dx, dy, status, time);
}
MenuItem itemDuplicate = new MenuItem(res, MENU_DUPLICATE, 10000, 10) {
public void run() {
int idx = partsList.getSelectedIndex();
if (idx == -1)
return;
Vector commands = macro.getCommandVector();
PersistableCommandFactory pf = (PersistableCommandFactory) commands.elementAt(idx);
commands.insertElementAt(new PersistableCommandFactory(pf), idx + 1);
partsList.setSize(commands.size(), idx + 1);
enableControlsForSelection(true);
};
};
MenuItem itemMoveDown = new MenuItem(res, MENU_MOVEDOWN, 20000, 10) {
public void run() {
moveDown(partsList.getSelectedIndex());
};
};
MenuItem itemMoveUp = new MenuItem(res, MENU_MOVEUP, 20000, 10) {
public void run() {
moveUp(partsList.getSelectedIndex());
};
};
MenuItem itemDelete = new MenuItem(res, MENU_DELETE_ACTION, 10000, 10) {
public void run() {
deleteAction(partsList.getSelectedIndex());
};
};
MenuItem itemEdit = new MenuItem(res, MENU_EDIT_ACTION, 10000, 10) {
public void run() {
editAction(partsList.getSelectedIndex());
};
};
MenuItem itemAddAction = new MenuItem(res, MENU_ADD_ACTION, 10000, 10) {
public void run() {
add(partsList.getSelectedIndex());
};
};
private void deleteAction(int index) {
macro.getCommandVector().removeElementAt(index);
enableControlsForSelection(true);
}
private void add(int idx) {
Vector commands = macro.getCommandVector();
KeybindState state = new KeybindState(-1, "Add Macro Action", null, null);
popup.setKeybindState(state);
UiApplication.getUiApplication().pushModalScreen(popup);
if (popup.isChangeSaved()) {
PersistableCommandFactory data = new PersistableCommandFactory(0, state.command.getId(), state.parameter);
if (idx == -1) {
commands.addElement(data);
idx = commands.size() - 1;
} else {
commands.insertElementAt(data, idx + 1);
}
partsList.setSize(commands.size(), idx);
}
enableControlsForSelection(true);
}
private void editAction(int idx) {
Vector commands = macro.getCommandVector();
PersistableCommandFactory pf = (PersistableCommandFactory) commands.elementAt(partsList.getSelectedIndex());
KeybindState state = new KeybindState(-1, "Edit Macro Action",
KeyBindingManager.getInstance().getExecutableCommandById(pf.getExecutableCommandId()),
pf.getParam());
popup.setKeybindState(state);
UiApplication.getUiApplication().pushModalScreen(popup);
if (popup.isChangeSaved()) {
state.changed = true;
commands.insertElementAt(new PersistableCommandFactory(0, state.command.getId(), state.parameter), idx);
commands.removeElementAt(idx + 1);
}
enableControlsForSelection(true);
}
private void moveUp(int index) {
if (index > 0) {
Tools.swapVectorElements(macro.getCommandVector(), index, index - 1);
enableControlsForSelection(true);
}
}
private void moveDown(int index) {
if (index < macro.getCommandVector().size() - 1) {
Tools.swapVectorElements(macro.getCommandVector(), index, index + 1);
enableControlsForSelection(true);
}
}
protected void makeMenu(Menu menu, int instance) {
int idx = partsList.getSelectedIndex();
menu.add(itemAddAction);
if (idx > -1) {
if (idx > 0) {
menu.add(itemMoveUp);
}
if (idx < macro.getCommandVector().size() - 1) {
menu.add(itemMoveDown);
}
menu.add(itemDuplicate);
menu.add(itemDelete);
menu.add(itemEdit);
menu.setDefault(itemEdit);
} else {
menu.setDefault(itemAddAction);
}
if (instance != Menu.INSTANCE_CONTEXT) {
super.makeMenu(menu, instance);
}
}
public MacroEditorScreen(Macro macro) {
super(DEFAULT_CLOSE | DEFAULT_MENU);
editing = true;
this.macro = macro;
// instructions
setTitle(res.getString(MACRO_TITLE_EDIT) + macro.getName());
nameField = new BasicEditField(res.getString(MACRO_EDIT_NAME), "", 32,
BasicEditField.NO_NEWLINE);
nameField.setText(macro.getName());
// @todo -- we need to copy our original values - what if the user doens't want to save
// changes but modifiers the vector or command parameters?
// @todo -- use new Macro(Macro m) constructor here - and update the backingmacro store
// only after changes are saved. Only problm here is that we ALSO need to preserve GUID
// Next, a list of commands in this macro
partsList = new MacroPartList();
Vector commands = macro.getCommandVector();
cb = new VectorListFieldCallback(commands);
partsList.setCallback(cb);
partsList.setSize(commands.size());
// @todo add(instructionFIeld)
add(nameField);
add(new SeparatorField());
add(partsList);
if (PlatformServicesProvider.getInstance().hasTouchscreen()) {
moveUp = new BitmapButtonField("arrowup");
moveDown = new BitmapButtonField("arrowdown");
deletePart = new BitmapButtonField("minus");
editPart = new BitmapButtonField("write");
newPart = new BitmapButtonField("plus");
moveUp.setEditable(false);
moveDown.setEditable(false);
deletePart.setEditable(false);
editPart.setEditable(false);
HorizontalFieldManager moveButtons = new HorizontalFieldManager();
moveButtons.add(newPart);
moveButtons.add(editPart);
moveButtons.add(deletePart);
moveButtons.add(moveUp);
moveButtons.add(moveDown);
add(moveButtons);
// These get set last so that we don't trigger notifications during
// our setup above.
moveDown.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
moveDown(partsList.getSelectedIndex());
}
});
deletePart.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
deleteAction(partsList.getSelectedIndex());
}
});
moveUp.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
moveUp(partsList.getSelectedIndex());
}
});
editPart.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
editAction(partsList.getSelectedIndex());
}
});
newPart.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
add(partsList.getSelectedIndex());
}
});
}
partsList.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
if (ignoreNextListChange) {
ignoreNextListChange = false;
} else {
enableControlsForSelection(false);
}
}
});
okCancel = new OKCancelControl();
add(okCancel);
okCancel.setChangeListener(this);
}
public MacroEditorScreen() {
this(new Macro());
editing = false;
setTitle(res, MACRO_TITLE_NEW);
}
public boolean isDataValid() {
String name = nameField.getText().trim();
if (name.length() == 0) {
Status.show(res.getString(MACRO_EDIT_MSG_NO_NAME));
nameField.setFocus();
return false;
}
if (macro.getCommandVector().size() == 0) {
Status.show(res.getString(MACRO_EDIT_MSG_NO_CONTENT));
return false;
}
if (!editing || !macro.getName().equals(name)) {
// if this is a new macro, or if we've changed the name of an existing macro
// make sure that we don't conflict
if (MacroManager.getInstance().getMacro(name) != null) {
Status.show(res.getString(MACRO_EDIT_MSG_NAME_IN_USE));
nameField.setText(macro.getName());
nameField.setFocus();
return false;
}
}
try {
save();
} catch (IOException e) {
}
return true;
}
/*
* (non-Javadoc)
* @see net.rim.device.api.ui.FieldChangeListener#fieldChanged(net.rim.device.api.ui.Field, int)
*/
public void fieldChanged(Field field, int context) {
// Logger.debug("MacroEditorScreen: fieldChange: " + field + " context: " + context);
if (field == okCancel) {
if (context == OKCancelControl.CONTEXT_OK_PRESS) {
if (!isDataValid()) {
return;
}
} else {
if (isDirty()) {
if (Dialog.ask(Dialog.D_YES_NO, res.getString(MACRO_EDIT_CONFIRM_CANCEL)) == Dialog.NO) {
return;
}
}
}
try {
save();
} catch (IOException e) {
}
close();
}
}
private void enableControlsForSelection(boolean invalidateList) {
int size = macro.getCommandVector().size();
if (partsList.getSize() != size) {
partsList.setSize(size);
// ignore the insert event.
ignoreNextListChange = true;
}
int sel = partsList.getSelectedIndex();
// Logger.debug("MacroEditorScreen: list selection = " + sel);
boolean validSelection = sel > -1;
if (PlatformServicesProvider.getInstance().hasTouchscreen()) {
moveUp.setEditable(sel > 0);
moveDown.setEditable(validSelection && sel < (partsList.getSize() - 1));
deletePart.setEditable(validSelection);
editPart.setEditable(validSelection);
}
if (invalidateList) {
partsList.invalidate();
}
}
/**
* Get the macro that was created by this dialog.
*
* @return the edited and validated macro, as long as "show" returns true
*/
public Macro getMacro() {
return this.macro;
}
/*
* (non-Javadoc)
* @see net.rim.device.api.ui.Screen#save()
*/
public void save() throws java.io.IOException {
macro.setName(nameField.getText());
// Really all of our other stuff is done...
saved = true;
}
public boolean isSaved() {
return this.saved;
};
}