/*
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 2011 Samuel Marshall.
*/
package com.leafdigital.ui;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.*;
import java.util.*;
import javax.swing.JComponent;
import org.w3c.dom.*;
import textlayout.StaticLayout;
import textlayout.stylesheet.*;
import util.PlatformUtils;
import util.xml.XML;
import com.leafdigital.ui.api.*;
import com.leafdigital.ui.api.TextView.ActionHandler;
import com.leafdigital.ui.api.Label;
import leafchat.core.api.*;
/** A wrappable label */
public class LabelImp extends JComponent implements BaseGroup
{
private ActionStaticLayout sl;
private static StyleContext smallContext;
private String text="";
private boolean small=false;
private String widthGroup, baseGroup;
private int topOffset=0;
private int macIndent = 0;
/** Map of actions from URL -> ActionHandler */
private Map<String, ActionHandler> actions =
new HashMap<String, ActionHandler>();
private Set<BaseGroup> getWidthGroup()
{
return ((InternalWidgetOwner)li.getOwner()).getArbitraryGroup("width\n"+widthGroup);
}
/** Constructor */
public LabelImp()
{
setLayout(null);
sl=new ActionStaticLayout("",-1);
add(sl);
// Add default URL action
li.setAction("url",new TextView.ActionHandler()
{
@Override
public void action(Element e,MouseEvent me)
{
String sURL=XML.getText(e);
if(!(sURL.startsWith("http://") || sURL.startsWith("https://")))
sURL="http://"+sURL;
try
{
PlatformUtils.showBrowser(new URL(sURL));
}
catch(MalformedURLException e1)
{
ErrorMsg.report("Not a valid URL",e1);
}
catch(IOException e1)
{
ErrorMsg.report(e1.getMessage(),e1.getCause());
}
return;
}
});
}
@Override
public void setBounds(int x,int y,int width,int height)
{
super.setBounds(x,y,width,height);
relayout();
}
private void relayout()
{
sl.setBounds(macIndent, topOffset, getWidth()-macIndent, getHeight()-topOffset);
}
@Override
public int getBaseline()
{
return sl.getFirstBaseline();
}
@Override
public InternalWidgetOwner getInternalWidgetOwner()
{
return (InternalWidgetOwner)li.getOwner();
}
@Override
public void setTopOffset(int topOffset)
{
if(this.topOffset==topOffset) return;
this.topOffset=topOffset;
relayout();
}
Label getInterface() { return li; }
LabelInterface li=new LabelInterface();
class LabelInterface extends BasicWidget implements Label, InternalWidget
{
private int iMinWidth=-1;
@Override
public int getContentType() { return CONTENT_NONE; }
@Override
public String getText()
{
return text;
}
@Override
public void setText(final String text)
{
LabelImp.this.text=text;
UISingleton.runInSwing(new Runnable()
{
@Override
public void run()
{
try
{
StyleContext sc=TextViewImp.getDefaultStyle(getUI(),false);
if(small)
{
if(smallContext==null)
{
smallContext=new StyleContext(sc);
smallContext.addStylesheet(new Stylesheet("output { font-size:0.85f }"));
}
sc=smallContext;
}
sl.setText(text,sc);
if(baseGroup!=null)
BaseGroup.Updater.updateGroup(LabelImp.this,baseGroup);
relayout();
}
catch(Exception e)
{
ErrorMsg.report("Unexpected error setting text",e);
}
}
});
}
@Override
public void setDefaultWidth(int width)
{
sl.setPreferredWidth(width);
}
@Override
public JComponent getJComponent()
{
return LabelImp.this;
}
@Override
public int getPreferredWidth()
{
int inner=getInnerPreferredWidth();
if(widthGroup==null) return inner;
Set<BaseGroup> s = getWidthGroup();
synchronized(s)
{
// Need to convert to array or we get commodification
LabelImp[] labels=s.toArray(new LabelImp[s.size()]);
for(int i=0;i<labels.length;i++)
{
inner=Math.max(inner,
((LabelInterface)labels[i].getInterface()).getInnerPreferredWidth());
}
}
return inner;
}
private int getInnerPreferredWidth()
{
int preferred = sl.getPreferredWidth();
if(iMinWidth!=-1)
{
preferred=Math.max(iMinWidth,preferred);
}
preferred += macIndent;
return preferred;
}
@Override
public int getPreferredHeight(int width)
{
int h=sl.getPreferredHeight(width);
h+=topOffset;
return h;
}
@Override
public void addXMLChild(String slotName, Widget child)
{
throw new BugException("Labels cannot contain children");
}
@Override
public void setMinWidth(int width)
{
iMinWidth=width;
}
@Override
public void setSmall(boolean small)
{
if(LabelImp.this.small==small) return;
LabelImp.this.small=small;
setText(text);
}
@Override
public void setWidthGroup(String group)
{
LabelImp.this.widthGroup=group;
Set<BaseGroup> s = getWidthGroup();
synchronized(s)
{
s.add(LabelImp.this);
}
}
@Override
public void setBaseGroup(String group)
{
if(baseGroup!=null)
{
BaseGroup.Updater.removeFromGroup(LabelImp.this,baseGroup);
baseGroup=null;
}
if(group!=null)
{
baseGroup=group;
BaseGroup.Updater.addToGroup(LabelImp.this,baseGroup);
}
}
@Override
public void setAction(String tag,ActionHandler ah)
{
actions.put(tag,ah);
}
@Override
public void setMacIndent(boolean macIndent)
{
setMacIndent(macIndent ? SupportsMacIndent.TYPE_EDIT_LEGACY
: SupportsMacIndent.TYPE_NONE);
}
@Override
public void setMacIndent(String macIndent)
{
int newValue = UISingleton.getMacIndent(macIndent);
if(newValue != LabelImp.this.macIndent)
{
LabelImp.this.macIndent = newValue;
setText(text);
}
}
}
// Debugging
@Override
protected void paintChildren(Graphics g)
{
super.paintChildren(g);
BaseGroup.Debug.paint(g,this,topOffset);
}
private class ActionStaticLayout extends StaticLayout
{
/**
* @param text
* @param preferredWidth
*/
public ActionStaticLayout(String text,int preferredWidth)
{
super(text,preferredWidth);
}
@Override
protected boolean isAction(Node n,int iCharacter)
{
while(n!=null)
{
if((n instanceof Element) && actions.containsKey(n.getNodeName())) return true;
n=n.getParentNode();
}
return false;
}
@Override
protected void doAction(Node n,int iCharacter,MouseEvent me)
{
while(n!=null)
{
if(n instanceof Element)
{
TextView.ActionHandler ah=actions.get(n.getNodeName());
if(ah!=null)
{
try
{
ah.action((Element)n,me);
}
catch(Throwable t)
{
ErrorMsg.report("Error carrying out text view action", t);
}
return;
}
}
n=n.getParentNode();
}
}
}
}