/*
This file is part of leafdigital leafChat.
leafChat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
leafChat 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with leafChat. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012 Samuel Marshall.
*/
package com.leafdigital.ircui;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import com.leafdigital.irc.api.*;
import com.leafdigital.prefs.api.*;
import com.leafdigital.ui.api.*;
import com.leafdigital.ui.api.TreeBox.Item;
import leafchat.core.api.*;
/**
* Preferences page containing character encoding options.
*/
@UIHandler({"encoding", "addencodingoverride"})
public class EncodingPage implements TreeBox.SingleSelectionHandler
{
private PluginContext context;
private Page p;
/** Dropdown: incoming encoding. */
public Dropdown incomingUI;
/** Dropdown: outgoing encoding. */
public Dropdown outgoingUI;
/** Checkbox: default to UTF-8. */
public CheckBox utf8UI;
/** Table: list of overrides. */
public Table overridesUI;
/** Button: Remove override. */
public Button removeUI;
/** Button: Edit override. */
public Button editUI;
private Dialog addDialog;
/** Tab panel: tabs in Add dialog */
public TabPanel addTabsUI;
/** Button: Add button in dialog */
public Button addAddUI;
/** Editbox: Nickname for override. */
public EditBox addNickUI;
/** Editbox: Username for override. */
public EditBox addUserUI;
/** Editbox: Hostname for override. */
public EditBox addHostUI;
/** Editbox: Channel name for override. */
public EditBox addChannelUI;
/** Dropdown: Incoming encoding for override. */
public Dropdown addIncomingUI;
/** Dropdown: Outgoing encoding for override. */
public Dropdown addOutgoingUI;
/** Checkbox: UTF-8 default for override. */
public CheckBox addUTF8UI;
/** Treebox: Server for override */
public TreeBox addServerTreeUI;
private boolean addEditMode;
private String addEditType,addEditName;
private PreferencesGroup addEditGroup;
EncodingPage(PluginContext pc)
{
this.context=pc;
UI ui=pc.getSingle(UI.class);
p = ui.createPage("encoding", this);
fillDropdown(incomingUI,false);
fillDropdown(outgoingUI,true);
Preferences p=pc.getSingle(Preferences.class);
PreferencesGroup encoding=getMainPrefs();
incomingUI.setSelected(encoding.get(IRCPrefs.PREF_ENCODING,IRCPrefs.PREFDEFAULT_ENCODING));
outgoingUI.setSelected(encoding.get(IRCPrefs.PREF_OUTGOING,IRCPrefs.PREFDEFAULT_OUTGOING));
utf8UI.setChecked(p.toBoolean(encoding.get(IRCPrefs.PREF_UTF8,IRCPrefs.PREFDEFAULT_UTF8)));
fillOverrides();
}
private void fillDropdown(Dropdown d,boolean outgoing)
{
for(String charset : Charset.availableCharsets().keySet())
{
if(outgoing)
{
try
{
"frog".getBytes(charset);
}
catch(UnsupportedOperationException e)
{
continue;
}
catch(UnsupportedEncodingException e)
{
continue;
}
}
d.addValue(charset,charset);
}
}
Page getPage()
{
return p;
}
/**
* Action: User clicks add override button.
*/
@UIAction
public void actionAdd()
{
openAddDialog(false);
}
private void openAddDialog(boolean edit)
{
UI ui=context.getSingle(UI.class);
addDialog = ui.createDialog("addencodingoverride", this);
addEditMode=edit;
Preferences prefs=context.getSingle(Preferences.class);
PreferencesGroup encoding=getMainPrefs();
// Fill and set up encoding selection
fillDropdown(addIncomingUI,false);
fillDropdown(addOutgoingUI,true);
// Fill server list
PreferencesGroup pg=prefs.
getGroup("Plugin_com.leafdigital.irc.IRCPlugin").getChild("servers");
rootItem = new PrefsServerItem(pg, null, context);
addServerTreeUI.setHandler(this);
// Set up defaults or existing values
if(edit)
{
int index=overridesUI.getSelectedIndex();
String context=overridesUI.getString(index,COL_CONTEXT);
addEditName=overridesUI.getString(index,COL_NAME);
addEditType=context;
addEditGroup = null;
if(context.equals(CONTEXT_CHANNEL))
{
PreferencesGroup[] chans=encoding.getChild(IRCPrefs.PREFGROUP_BYCHAN).getAnon();
for(int i=0;i<chans.length;i++)
{
if(chans[i].get(IRCPrefs.PREF_BYCHAN_NAME).equals(addEditName))
{
addEditGroup=chans[i];
addIncomingUI.setSelected(addEditGroup.get(IRCPrefs.PREF_ENCODING));
addOutgoingUI.setSelected(addEditGroup.get(IRCPrefs.PREF_OUTGOING));
addUTF8UI.setChecked(pg.getPreferences().toBoolean(addEditGroup.get(IRCPrefs.PREF_UTF8)));
addTabsUI.display("addChannelPage");
addChannelUI.setValue(addEditName);
break;
}
}
}
else if(context.equals(CONTEXT_USER))
{
PreferencesGroup[] users=encoding.getChild(IRCPrefs.PREFGROUP_BYUSER).getAnon();
for(int i=0;i<users.length;i++)
{
if(users[i].get(IRCPrefs.PREF_BYUSER_MASK).equals(addEditName))
{
addEditGroup=users[i];
addIncomingUI.setSelected(addEditGroup.get(IRCPrefs.PREF_ENCODING));
addOutgoingUI.setSelected(addEditGroup.get(IRCPrefs.PREF_OUTGOING));
addUTF8UI.setChecked(pg.getPreferences().toBoolean(addEditGroup.get(IRCPrefs.PREF_UTF8)));
addTabsUI.display("addUserPage");
IRCUserAddress mask=new IRCUserAddress(addEditName,true);
addNickUI.setValue(mask.getNick());
addUserUI.setValue(mask.getUser());
addHostUI.setValue(mask.getHost());
break;
}
}
}
else
{
initEditFromServerOverride(
prefs.getGroup("Plugin_com.leafdigital.irc.IRCPlugin").getChild("servers"),
context.equals(CONTEXT_NETWORK),addEditName);
}
if(addEditGroup == null)
{
throw new BugException("Failed to identify preferences group for edit (" +
addEditType + ":" + addEditName + ")");
}
addAddUI.setLabel("Save changes");
}
else
{
addIncomingUI.setSelected(incomingUI.getSelected());
addOutgoingUI.setSelected(outgoingUI.getSelected());
addUTF8UI.setChecked(utf8UI.isChecked());
selectedServer=null;
}
addDialog.show(p.getOwner());
}
private boolean isEditSame()
{
String
incoming=(String)addIncomingUI.getSelected(),
outgoing=(String)addOutgoingUI.getSelected(),
utf8=addEditGroup.getPreferences().fromBoolean(addUTF8UI.isChecked());
if(!(addEditGroup.get(IRCPrefs.PREF_ENCODING).equals(incoming) &&
addEditGroup.get(IRCPrefs.PREF_OUTGOING).equals(outgoing) &&
addEditGroup.get(IRCPrefs.PREF_UTF8).equals(utf8)))
return false;
if(addEditType.equals(CONTEXT_CHANNEL))
{
return
addTabsUI.getDisplayed().equals("addChannelPage") &&
addChannelUI.getValue().equals(addEditName);
}
else if(addEditType.equals(CONTEXT_USER))
{
IRCUserAddress mask=new IRCUserAddress(
addNickUI.getValue(),addUserUI.getValue(),addHostUI.getValue());
return
addTabsUI.getDisplayed().equals("addUserPage") &&
mask.toString().equals(addEditName);
}
else
{
boolean network=addEditGroup.get(IRCPrefs.PREF_HOST,null)==null;
String selectedName=network ? addEditGroup.get(IRCPrefs.PREF_NETWORK) :
addEditGroup.get(IRCPrefs.PREF_HOST);
return
addTabsUI.getDisplayed().equals("addServerPage") &&
selectedName.equals(addEditName);
}
}
/**
* Action: User clicks edit override button.
*/
@UIAction
public void actionEdit()
{
openAddDialog(true);
}
/**
* Action: User clicks remove override button.
*/
@UIAction
public void actionRemove()
{
Preferences prefs=context.getSingle(Preferences.class);
PreferencesGroup encoding=getMainPrefs();
int index=overridesUI.getSelectedIndex();
String context=overridesUI.getString(index,COL_CONTEXT);
String name=overridesUI.getString(index,COL_NAME);
if(context.equals(CONTEXT_CHANNEL))
{
PreferencesGroup[] chans=encoding.getChild(IRCPrefs.PREFGROUP_BYCHAN).getAnon();
for(int i=0;i<chans.length;i++)
{
if(chans[i].get(IRCPrefs.PREF_BYCHAN_NAME).equals(name))
chans[i].remove();
}
}
else if(context.equals(CONTEXT_USER))
{
PreferencesGroup[] users=encoding.getChild(IRCPrefs.PREFGROUP_BYUSER).getAnon();
for(int i=0;i<users.length;i++)
{
if(users[i].get(IRCPrefs.PREF_BYUSER_MASK).equals(name))
users[i].remove();
}
}
else
{
removeServerOverride(
prefs.getGroup("Plugin_com.leafdigital.irc.IRCPlugin").getChild("servers"),
context.equals(CONTEXT_NETWORK),name);
}
fillOverrides();
}
private void initEditFromServerOverride(PreferencesGroup pg,boolean network,String name)
{
// Check this server
if(
(network && name.equals(pg.get(IRCPrefs.PREF_NETWORK,null))) ||
(!network && name.equals(pg.get(IRCPrefs.PREF_HOST,null)))
)
{
addIncomingUI.setSelected(pg.get(IRCPrefs.PREF_ENCODING));
addOutgoingUI.setSelected(pg.get(IRCPrefs.PREF_OUTGOING));
addUTF8UI.setChecked(pg.getPreferences().toBoolean(pg.get(IRCPrefs.PREF_UTF8)));
addServerTreeUI.select(rootItem.find(pg));
addTabsUI.display("addServerPage");
addEditGroup=pg;
return;
}
// Check all child servers
PreferencesGroup[] children=pg.getAnon();
for(int i=0;i<children.length;i++)
{
initEditFromServerOverride(children[i],network,name);
}
}
private void removeServerOverride(PreferencesGroup pg,boolean network,String name)
{
// Check this server
if(
(network && name.equals(pg.get(IRCPrefs.PREF_NETWORK,null))) ||
(!network && name.equals(pg.get(IRCPrefs.PREF_HOST,null)))
)
{
pg.unset(IRCPrefs.PREF_ENCODING);
pg.unset(IRCPrefs.PREF_UTF8);
pg.unset(IRCPrefs.PREF_OUTGOING);
}
// Check all child servers
PreferencesGroup[] children=pg.getAnon();
for(int i=0;i<children.length;i++)
{
removeServerOverride(children[i],network,name);
}
}
/**
* Action: User selects an override from table.
*/
@UIAction
public void selectOverrides()
{
boolean gotSomething=overridesUI.getSelectedIndex()!=Table.NONE;
removeUI.setEnabled(gotSomething);
editUI.setEnabled(gotSomething);
}
/**
* Action: Add dialog losed.
*/
@UIAction
public void closedAdd()
{
addDialog=null;
}
/**
* Action: focus nickname field.
*/
@UIAction
public void focusAddNick()
{
if(addNickUI.getValue().equals("*")) addNickUI.selectAll();
}
/**
* Action: focus username field.
*/
@UIAction
public void focusAddUser()
{
if(addUserUI.getValue().equals("*")) addUserUI.selectAll();
}
/**
* Action: focus hostname field.
*/
@UIAction
public void focusAddHost()
{
if(addHostUI.getValue().equals("*")) addHostUI.selectAll();
}
private final static String SAFECHARACTERS="[^!@ ]+";
/** Check whether add button should now be enabled */
@UIAction
public void changeAdd()
{
String selectedTab=addTabsUI.getDisplayed();
boolean canAdd=false;
if(selectedTab.equals("addServerPage"))
{
canAdd=selectedServer!=null;
}
else if(selectedTab.equals("addChannelPage"))
{
String value=addChannelUI.getValue();
canAdd=value.matches("[^A-Za-z0-9].+");
addChannelUI.setFlag((canAdd || value.equals(""))?EditBox.FLAG_NORMAL : EditBox.FLAG_ERROR);
}
else if(selectedTab.equals("addUserPage"))
{
canAdd=true;
if(addNickUI.getValue().matches(SAFECHARACTERS) )
{
addNickUI.setFlag(EditBox.FLAG_NORMAL);
}
else
{
addNickUI.setFlag(EditBox.FLAG_ERROR);
canAdd=false;
}
if(addUserUI.getValue().matches(SAFECHARACTERS))
{
addUserUI.setFlag(EditBox.FLAG_NORMAL);
}
else
{
addUserUI.setFlag(EditBox.FLAG_ERROR);
canAdd=false;
}
if(addHostUI.getValue().matches(SAFECHARACTERS))
{
addHostUI.setFlag(EditBox.FLAG_NORMAL);
}
else
{
addHostUI.setFlag(EditBox.FLAG_ERROR);
canAdd=false;
}
if(addNickUI.getValue().equals("*") &&
addUserUI.getValue().equals("*") &&
addHostUI.getValue().equals("*"))
{
canAdd=false;
}
}
if(addEditMode && canAdd) canAdd=!isEditSame();
addAddUI.setEnabled(canAdd);
}
/**
* Action: Cancel add dialog.
*/
@UIAction
public void actionAddCancel()
{
addDialog.close();
}
/**
* Action: Confirm add (add button in dialog).
*/
@UIAction
public void actionAddAdd()
{
// Get rid of old one if editing
if(addEditMode) actionRemove();
// Prepare preferences
Preferences p=context.getSingle(Preferences.class);
PreferencesGroup encoding=getMainPrefs();
// Get encoding details
String
incoming=(String)addIncomingUI.getSelected(),
outgoing=(String)addOutgoingUI.getSelected();
String utf8=p.fromBoolean(addUTF8UI.isChecked());
String selectedTab=addTabsUI.getDisplayed();
if(selectedTab.equals("addServerPage"))
{
selectedServer.set(IRCPrefs.PREF_ENCODING,incoming);
selectedServer.set(IRCPrefs.PREF_OUTGOING,outgoing);
selectedServer.set(IRCPrefs.PREF_UTF8,utf8);
}
else if(selectedTab.equals("addChannelPage"))
{
PreferencesGroup pg=encoding.getChild(IRCPrefs.PREFGROUP_BYCHAN).addAnon();
pg.set(IRCPrefs.PREF_BYCHAN_NAME,addChannelUI.getValue());
pg.set(IRCPrefs.PREF_BYCHAN_ENCODING,incoming);
pg.set(IRCPrefs.PREF_BYCHAN_OUTGOING,outgoing);
pg.set(IRCPrefs.PREF_BYCHAN_UTF8,utf8);
}
else if(selectedTab.equals("addUserPage"))
{
PreferencesGroup pg=encoding.getChild(IRCPrefs.PREFGROUP_BYUSER).addAnon();
pg.set(IRCPrefs.PREF_BYUSER_MASK,addNickUI.getValue()+"!"+addUserUI.getValue()+"@"+addHostUI.getValue());
pg.set(IRCPrefs.PREF_BYUSER_ENCODING,incoming);
pg.set(IRCPrefs.PREF_BYUSER_OUTGOING,outgoing);
pg.set(IRCPrefs.PREF_BYUSER_UTF8,utf8);
}
addDialog.close();
fillOverrides();
}
private final static int COL_CONTEXT=0,COL_NAME=1,COL_RECEIVED=2,COL_UTF8=3,COL_SENT=4;
private final static String CONTEXT_NETWORK="Net",CONTEXT_SERVER="Server",CONTEXT_CHANNEL="Chan",CONTEXT_USER="User";
private void fillServerOverrides(PreferencesGroup pg)
{
Preferences p=context.getSingle(Preferences.class);
// Check this server
String serverEncoding=pg.get(IRCPrefs.PREF_ENCODING,null);
if(serverEncoding!=null)
{
int index=overridesUI.addItem();
boolean network=pg.get(IRCPrefs.PREF_HOST,null)==null;
overridesUI.setString(index,COL_CONTEXT,network ? CONTEXT_NETWORK : CONTEXT_SERVER);
overridesUI.setString(index,COL_NAME,network ? pg.get(IRCPrefs.PREF_NETWORK) : pg.get(IRCPrefs.PREF_HOST));
overridesUI.setString(index,COL_RECEIVED,serverEncoding);
overridesUI.setBoolean(index,COL_UTF8,p.toBoolean(pg.get(IRCPrefs.PREF_UTF8)));
overridesUI.setString(index,COL_SENT,pg.get(IRCPrefs.PREF_OUTGOING));
}
// Check all child servers
PreferencesGroup[] children=pg.getAnon();
for(int i=0;i<children.length;i++)
{
fillServerOverrides(children[i]);
}
}
/** Update the overrides table */
private void fillOverrides()
{
Preferences p=context.getSingle(Preferences.class);
PreferencesGroup encoding=getMainPrefs();
overridesUI.clear();
fillServerOverrides(p.getGroup("Plugin_com.leafdigital.irc.IRCPlugin").getChild("servers"));
PreferencesGroup[] users=encoding.getChild(IRCPrefs.PREFGROUP_BYUSER).getAnon();
for(int i=0;i<users.length;i++)
{
int index=overridesUI.addItem();
overridesUI.setString(index,COL_CONTEXT,CONTEXT_USER);
overridesUI.setString(index,COL_NAME,users[i].get(IRCPrefs.PREF_BYUSER_MASK));
overridesUI.setString(index,COL_RECEIVED,users[i].get(IRCPrefs.PREF_BYUSER_ENCODING));
overridesUI.setBoolean(index,COL_UTF8,p.toBoolean(users[i].get(IRCPrefs.PREF_BYUSER_UTF8)));
overridesUI.setString(index,COL_SENT,users[i].get(IRCPrefs.PREF_BYUSER_OUTGOING));
}
PreferencesGroup[] chans=encoding.getChild(IRCPrefs.PREFGROUP_BYCHAN).getAnon();
for(int i=0;i<chans.length;i++)
{
int index=overridesUI.addItem();
overridesUI.setString(index,COL_CONTEXT,CONTEXT_CHANNEL);
overridesUI.setString(index,COL_NAME,chans[i].get(IRCPrefs.PREF_BYCHAN_NAME));
overridesUI.setString(index,COL_RECEIVED,chans[i].get(IRCPrefs.PREF_BYCHAN_ENCODING));
overridesUI.setBoolean(index,COL_UTF8,p.toBoolean(chans[i].get(IRCPrefs.PREF_BYCHAN_UTF8)));
overridesUI.setString(index,COL_SENT,chans[i].get(IRCPrefs.PREF_BYCHAN_OUTGOING));
}
}
private PreferencesGroup getMainPrefs()
{
Preferences p=context.getSingle(Preferences.class);
return p.getGroup(
p.getPluginOwner("com.leafdigital.irc.IRCPlugin")).
getChild(IRCPrefs.PREFGROUP_ENCODING);
}
/**
* Action: Change incoming encoding dropdown.
*/
@UIAction
public void selectionChangeIncoming()
{
getMainPrefs().set(IRCPrefs.PREF_ENCODING,
(String)incomingUI.getSelected(),IRCPrefs.PREFDEFAULT_ENCODING);
}
/**
* Action: Change default UTF-8 dropdown.
*/
@UIAction
public void changeUTF8()
{
Preferences p=context.getSingle(Preferences.class);
getMainPrefs().set(IRCPrefs.PREF_UTF8,
p.fromBoolean(utf8UI.isChecked()),IRCPrefs.PREFDEFAULT_UTF8);
}
/**
* Action: Change outgoing encoding dropdown.
*/
@UIAction
public void selectionChangeOutgoing()
{
getMainPrefs().set(IRCPrefs.PREF_OUTGOING,
(String)outgoingUI.getSelected(),IRCPrefs.PREFDEFAULT_OUTGOING);
}
PreferencesGroup selectedServer;
@Override
public void selected(Item i)
{
if(i==null)
selectedServer=null;
else
selectedServer=((PrefsServerItem)i).getGroup();
changeAdd();
}
private PrefsServerItem rootItem;
@Override
public Item getRoot()
{
return rootItem;
}
@Override
public boolean isRootDisplayed()
{
return false;
}
}