/* * This file is part of the Haven & Hearth game client. * Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and * Björn Johannessen <johannessen.bjorn@gmail.com> * * Redistribution and/or modification of this file is subject to the * terms of the GNU Lesser General Public License, version 3, as * published by the Free Software Foundation. * * This program 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. * * Other parts of this source tree adhere to other copying * rights. Please see the file `COPYING' in the root directory of the * source tree for details. * * A copy the GNU Lesser General Public License is distributed along * with the source tree of which this file is a part in the file * `doc/LPGL-3'. If it is missing for any reason, please see the Free * Software Foundation's website at <http://www.fsf.org/>, or write * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ package haven; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.Map; import java.util.TreeMap; public class Widget { public UI ui; public Coord c, sz, hsz; public Widget next, prev, child, lchild, parent; boolean focustab = false, focusctl = false, hasfocus = false, visible = true; private boolean canfocus = false, autofocus = false; public boolean canactivate = false, cancancel = false; Widget focused; public Resource cursor = null; public Object tooltip = null; public boolean canhastrash = true; private Widget prevtt; public boolean isui = true; static Map<String, WidgetFactory> types = new TreeMap<String, WidgetFactory>(); static Class<?>[] barda = {Img.class, TextEntry.class, MapView.class, FlowerMenu.class, Window.class, Button.class, Inventory.class, Item.class, Listbox.class, Makewindow.class, Chatwindow.class, Textlog.class, Equipory.class, IButton.class, Cal.class, Avaview.class, NpcChat.class, Label.class, Progress.class, VMeter.class, Partyview.class, MenuGrid.class, SlenHud.class, HWindow.class, CheckBox.class, Logwindow.class, MapMod.class, ISBox.class, ComMeter.class, Fightview.class, IMeter.class, GiveButton.class, Charlist.class, ComWin.class, CharWnd.class, BuddyWnd.class, ChatHW.class, Speedget.class, Bufflist.class}; static { addtype("cnt", new WidgetFactory() { public Widget create(Coord c, Widget parent, Object[] args) { return(new Widget(c, (Coord)args[0], parent)); } }); } public static void initbardas() { try { for(Class<?> c : barda) Class.forName(c.getName(), true, c.getClassLoader()); } catch(ClassNotFoundException e) { throw(new Error(e)); } } public static void addtype(String name, WidgetFactory fct) { synchronized(types) { types.put(name, fct); } } public static WidgetFactory gettype(String name) { synchronized(types) { return(types.get(name)); } } public Widget(UI ui, Coord c, Coord sz) { this.ui = ui; this.c = c; this.sz = sz; } public Widget(Coord c, Coord sz, Widget parent) { synchronized(parent.ui) { this.ui = parent.ui; this.c = c; this.sz = sz; this.parent = parent; link(); } } public void link() { synchronized(ui) { if(parent.lchild != null) parent.lchild.next = this; if(parent.child == null) parent.child = this; this.prev = parent.lchild; parent.lchild = this; } } public void unlink() { synchronized(ui) { if(next != null) next.prev = prev; if(prev != null) prev.next = next; if(parent.child == this) parent.child = next; if(parent.lchild == this) parent.lchild = prev; next = null; prev = null; } } public Coord xlate(Coord c, boolean in) { return(c); } public Coord rootpos() { if(parent == null) return(new Coord(0, 0)); return(xlate(parent.rootpos().add(c), true)); } public boolean hasparent(Widget w2) { for(Widget w = this; w != null; w = w.parent) { if(w == w2) return(true); } return(false); } public void gotfocus() { if(focusctl && (focused != null)) { focused.hasfocus = true; focused.gotfocus(); } } public void destroy() { if(canfocus) setcanfocus(false); } public void lostfocus() { if(focusctl && (focused != null)) { focused.hasfocus = false; focused.lostfocus(); } } public void setfocus(Widget w) { if(focusctl) { if(w != focused) { Widget last = focused; focused = w; if(last != null) last.hasfocus = false; w.hasfocus = true; if(last != null) last.lostfocus(); w.gotfocus(); if((ui != null) && ui.rwidgets.containsKey(w)) wdgmsg("focus", ui.rwidgets.get(w)); } if(parent != null) parent.setfocus(this); } else { parent.setfocus(w); } } public void setcanfocus(boolean canfocus) { this.autofocus = this.canfocus = canfocus; if(parent != null) { if(canfocus) { parent.newfocusable(this); } else { parent.delfocusable(this); } } } public void newfocusable(Widget w) { if(focusctl) { if(focused == null) setfocus(w); } else { parent.newfocusable(w); } } public void delfocusable(Widget w) { if(focusctl) { if(focused == w) findfocus(); } else { parent.delfocusable(w); } } private void findfocus() { /* XXX: Might need to check subwidgets recursively */ focused = null; for(Widget w = lchild; w != null; w = w.prev) { if(w.autofocus) { focused = w; focused.hasfocus = true; w.gotfocus(); break; } } } public void setfocusctl(boolean focusctl) { if(this.focusctl = focusctl) { findfocus(); setcanfocus(true); } } public void setfocustab(boolean focustab) { if(focustab && !focusctl) setfocusctl(true); this.focustab = focustab; } public void uimsg(String msg, Object... args) { if(msg == "tabfocus") { setfocustab(((Integer)args[0] != 0)); } else if(msg == "act") { canactivate = (Integer)args[0] != 0; } else if(msg == "cancel") { cancancel = (Integer)args[0] != 0; } else if(msg == "autofocus") { autofocus = (Integer)args[0] != 0; } else if(msg == "focus") { Widget w = ui.widgets.get((Integer)args[0]); if(w != null) { if(w.canfocus) setfocus(w); } } else if(msg == "curs") { if(args.length == 0) cursor = null; else cursor = Resource.load((String)args[0], (Integer)args[1]); } else { System.err.println("Unhandled widget message: " + msg); } } public void binded() { } public void wdgmsg(String msg, Object... args) { wdgmsg(this, msg, args); } public void wdgmsg(Widget sender, String msg, Object... args) { if(parent == null) ui.wdgmsg(sender, msg, args); else parent.wdgmsg(sender, msg, args); } public void draw(GOut g) { Widget next; for(Widget wdg = child; wdg != null; wdg = next) { next = wdg.next; if(!wdg.visible||(!ui.root.visible&&wdg.isui)) continue; Coord cc = xlate(wdg.c, true); wdg.draw(g.reclip(cc, wdg.sz)); } } public boolean mousedown(Coord c, int button) { for(Widget wdg = lchild; wdg != null; wdg = wdg.prev) { if(!wdg.visible) continue; Coord cc = xlate(wdg.c, true); if(c.isect(cc, (wdg.hsz == null)?wdg.sz:wdg.hsz)) { if(wdg.mousedown(c.add(cc.inv()), button)) { return(true); } } } return(false); } public boolean mouseup(Coord c, int button) { for(Widget wdg = lchild; wdg != null; wdg = wdg.prev) { if(!wdg.visible) continue; Coord cc = xlate(wdg.c, true); if(c.isect(cc, (wdg.hsz == null)?wdg.sz:wdg.hsz)) { if(wdg.mouseup(c.add(cc.inv()), button)) { return(true); } } } return(false); } public boolean mousewheel(Coord c, int amount) { for(Widget wdg = lchild; wdg != null; wdg = wdg.prev) { if(!wdg.visible) continue; Coord cc = xlate(wdg.c, true); if(c.isect(cc, (wdg.hsz == null)?wdg.sz:wdg.hsz)) { if(wdg.mousewheel(c.add(cc.inv()), amount)) { return(true); } } } return(false); } public void mousemove(Coord c) { for(Widget wdg = lchild; wdg != null; wdg = wdg.prev) { if(!wdg.visible) continue; Coord cc = xlate(wdg.c, true); wdg.mousemove(c.add(cc.inv())); } } public boolean globtype(char key, KeyEvent ev) { for(Widget wdg = child; wdg != null; wdg = wdg.next) { if(wdg.globtype(key, ev)) return(true); } return(false); } public boolean type(char key, KeyEvent ev) { if(canactivate) { if(key == 10) { wdgmsg("activate"); return(true); } } if(cancancel) { if(key == 27) { wdgmsg("cancel"); return(true); } } if(focusctl) { if(focused != null) { if(focused.type(key, ev)) return(true); if(focustab) { if(key == '\t') { Widget f = focused; while(true) { if((ev.getModifiers() & InputEvent.SHIFT_MASK) == 0) f = (f.next == null)?child:f.next; else f = (f.prev == null)?lchild:f.prev; if(f.canfocus) break; } setfocus(f); return(true); } else { return(false); } } else { return(false); } } else { return(false); } } else { for(Widget wdg = child; wdg != null; wdg = wdg.next) { if(wdg.type(key, ev)) return(true); } return(false); } } public boolean keydown(KeyEvent ev) { if(focusctl) { if(focused != null) { if(focused.keydown(ev)) return(true); return(false); } else { return(false); } } else { for(Widget wdg = child; wdg != null; wdg = wdg.next) { if(wdg.keydown(ev)) return(true); } } return(false); } public boolean keyup(KeyEvent ev) { if(focusctl) { if(focused != null) { if(focused.keyup(ev)) return(true); return(false); } else { return(false); } } else { for(Widget wdg = child; wdg != null; wdg = wdg.next) { if(wdg.keyup(ev)) return(true); } } return(false); } public void raise() { synchronized(ui) { unlink(); link(); } } @Deprecated public <T extends Widget> T findchild(Class<T> cl) { for(Widget wdg = child; wdg != null; wdg = wdg.next) { if(cl.isInstance(wdg)) return(cl.cast(wdg)); T ret = wdg.findchild(cl); if(ret != null) return(ret); } return(null); } public Resource getcurs(Coord c) { Resource ret; for(Widget wdg = lchild; wdg != null; wdg = wdg.prev) { if(!wdg.visible) continue; Coord cc = xlate(wdg.c, true); if(c.isect(cc, wdg.sz)) { if((ret = wdg.getcurs(c.add(cc.inv()))) != null) return(ret); } } return(cursor); } public Object tooltip(Coord c, boolean again) { if(tooltip != null) { prevtt = null; return(tooltip); } for(Widget wdg = lchild; wdg != null; wdg = wdg.prev) { if(!wdg.visible) continue; Coord cc = xlate(wdg.c, true); if(c.isect(cc, wdg.sz)) { Object ret = wdg.tooltip(c.add(cc.inv()), again && (wdg == prevtt)); if(ret != null) { prevtt = wdg; return(ret); } } } prevtt = null; return(null); } public void hide() { visible = false; } public void show() { visible = true; } }