package org.droiddraw; import org.droiddraw.gui.Preferences; import org.droiddraw.gui.PropertiesPanel; import org.droiddraw.gui.Viewer; import org.droiddraw.property.Property; import org.droiddraw.property.StringProperty; import org.droiddraw.util.ArrayHandler; import org.droiddraw.util.ColorHandler; import org.droiddraw.util.StringHandler; import org.droiddraw.widget.AbstractWidget; import org.droiddraw.widget.Button; import org.droiddraw.widget.CheckBox; import org.droiddraw.widget.Layout; import org.droiddraw.widget.Widget; import org.xml.sax.SAXException; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URL; import java.util.Hashtable; import java.util.StringTokenizer; import java.util.Vector; import javax.imageio.ImageIO; import javax.swing.JOptionPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.undo.UndoManager; import javax.swing.undo.UndoableEdit; import javax.xml.parsers.ParserConfigurationException; public class AndroidEditor { public static enum ScreenMode {QVGA_LANDSCAPE, QVGA_PORTRAIT, HVGA_LANDSCAPE, HVGA_PORTRAIT, WVGA_LANDSCAPE, WVGA_PORTRAIT} public static int MAJOR_VERSION = 0; public static int MINOR_VERSION = 11; Layout layout; Widget selected; Viewer viewer; ScreenMode screen; int sx, sy; PropertiesPanel pp; File stringFile = null; Hashtable<String, String> strings; File colorFile = null; Hashtable<String, Color> colors; File arrayFile = null; Hashtable<String, Vector<String>> arrays; Preferences prefs; UndoManager undo; Vector<ChangeListener> changeListeners; boolean changed; File drawable_dir; URLOpener opener; String theme; public static int OFFSET_X = 0; public static int OFFSET_Y = 48; private static AndroidEditor inst; private AndroidEditor() { this(ScreenMode.HVGA_PORTRAIT); } public boolean isLatestVersion() { try { URL u = new URL("http://www.droiddraw.org/version.txt"); BufferedReader in = new BufferedReader(new InputStreamReader(u.openStream())); String line = in.readLine(); StringTokenizer tok = new StringTokenizer(line); int major = Integer.parseInt(tok.nextToken()); int minor = Integer.parseInt(tok.nextToken()); return MAJOR_VERSION > major || (MAJOR_VERSION == major && MINOR_VERSION >= minor); } catch (MalformedURLException ex) { return true; } catch (IOException ex) { return true; } catch (Exception ex) { return true; } } private AndroidEditor(ScreenMode mode) { setScreenMode(mode); this.pp = new PropertiesPanel(false); this.colors = new Hashtable<String, Color>(); this.strings = new Hashtable<String, String>(); this.arrays = new Hashtable<String, Vector<String>>(); this.undo = new UndoManager(); this.changeListeners = new Vector<ChangeListener>(); colors.put("black", Color.black); colors.put("darkgray", Color.darkGray); colors.put("gray", Color.gray); colors.put("lightgray", Color.lightGray); colors.put("red", Color.red); colors.put("green", Color.green); colors.put("blue",Color.blue); colors.put("yellow", Color.yellow); colors.put("cyan", Color.cyan); colors.put("magenta", Color.magenta); colors.put("white", Color.white); this.changed = false; } public void queueUndoRecord(UndoableEdit edit) { this.undo.addEdit(edit); setChanged(true); } public void undo() { if (undo.canUndo()) { undo.undo(); setChanged(true); } } public void redo() { if (undo.canRedo()) { undo.redo(); setChanged(true); } } public boolean isChanged() { return changed; } public void setChanged(boolean changed) { this.changed = changed; if (changed) { for (ChangeListener cl : changeListeners) { cl.stateChanged(new ChangeEvent(layout)); } } } public void addChangeListener(ChangeListener cl) { this.changeListeners.add(cl); } public PropertiesPanel getPropertiesPanel() { return pp; } public void setDrawableDirectory(File dir) { this.drawable_dir = dir; } public File getDrawableDirectory() { return this.drawable_dir; } public BufferedImage findDrawable(String src) { if (this.getDrawableDirectory() == null) { return null; } int ix = src.indexOf("/"); String file = src.substring(ix+1); //System.out.println("Looking for: "+file); File f = new File(this.getDrawableDirectory(), file+".png"); if (!f.exists()) { //try { // System.out.println(f.getCanonicalPath()+" doesn't exist!"); //} catch (IOException ex) { // ex.printStackTrace(); //} f = new File(this.getDrawableDirectory(), file+".bmp"); } if (!f.exists()) { f = new File(this.getDrawableDirectory(), file+".jpg"); } if (!f.exists()) { return null; } try { //System.out.println("Reading in: "+f.getCanonicalPath()); return ImageIO.read(f); } catch (IOException ex) { error(ex); } return null; } public Hashtable<String, String> getStrings() { return strings; } public void setStrings(Hashtable<String, String> strings) { this.strings = strings; } public File getStringFile() { return stringFile; } public void setStrings(File f) { try { setStrings(StringHandler.load(new FileInputStream(f))); stringFile = f; } catch (IOException ex) { error(ex); } catch (SAXException ex) { error(ex); } catch (ParserConfigurationException ex) { error(ex); } } public File getColorFile() { return colorFile; } public void setColors(File f) { try { setColors(ColorHandler.load(new FileInputStream(f))); colorFile = f; } catch (IOException ex) { error(ex); } catch (SAXException ex) { error(ex); } catch (ParserConfigurationException ex) { error(ex); } } public Hashtable<String, Color> getColors() { return colors; } public void error(String message) { JOptionPane.showMessageDialog(viewer, message, "Error", JOptionPane.WARNING_MESSAGE); } public void error(Exception ex) { error(ex.getMessage()); ex.printStackTrace(); } public void message(String title, String message) { JOptionPane.showMessageDialog(viewer, message, title, JOptionPane.INFORMATION_MESSAGE); } public void setColors(Hashtable<String, Color> colors) { for (String key : colors.keySet()) { colors.put(key, colors.get(key)); } } public static AndroidEditor instance() { if (inst == null) inst = new AndroidEditor(); return inst; } public ScreenMode getScreenMode() { return screen; } public void setScreenMode(ScreenMode mode) { this.screen = mode; if (screen == ScreenMode.QVGA_LANDSCAPE) { sx = 320; sy = 240; } else if (screen == ScreenMode.QVGA_PORTRAIT) { sx = 240; sy = 320; } else if (screen == ScreenMode.HVGA_LANDSCAPE) { sx = 480; sy = 320; } else if (screen == ScreenMode.HVGA_PORTRAIT) { sx = 320; sy = 480; } else if (screen == ScreenMode.WVGA_LANDSCAPE) { sx = 854; sy = 480; } else if (screen == ScreenMode.WVGA_PORTRAIT) { sx = 480; sy = 854; } else if (screen == ScreenMode.WVGA_LANDSCAPE) { sx = 854; sy = 480; } else if (screen == ScreenMode.WVGA_PORTRAIT) { sx = 480; sy = 854; } if (this.getLayout() != null) { this.getLayout().apply(); for (Widget w : this.getLayout().getWidgets()) { w.apply(); } this.getLayout().repositionAllWidgets(); } } public int getScreenX() { return sx; } public int getScreenY() { return sy; } public void setViewer(Viewer v) { this.viewer = v; this.pp.setViewer(v); } public void setIdsFromLabels() { setIdsFromLabels(layout); } public void setIdsFromLabels(Layout l) { for (Widget w : l.getWidgets()) { if (w instanceof Layout) { setIdsFromLabels((Layout)w); } else { Property p = w.getPropertyByAttName("android:text"); if (p != null) { ((AbstractWidget)w).setId("@+id/"+((StringProperty)p).getStringValue()); } } } } public void setLayout(Layout l) { setLayout(l, true); } public void setLayout(Layout l, boolean fill) { if (fill) { l.setPropertyByAttName("android:layout_width", "fill_parent"); l.setPropertyByAttName("android:layout_height", "fill_parent"); } if (this.layout != null) { Vector<Widget> widgets = layout.getWidgets(); for (Widget w : widgets) { l.addWidget(w); } this.layout.removeAllWidgets(); } this.layout = l; if (l.getPropertyByAttName("xmlns:android") == null) { l.addProperty(new StringProperty("xmlns", "xmlns:android", "http://schemas.android.com/apk/res/android", false)); } if (selected == null) { pp.setProperties(l.getProperties(), l); } } public Layout getLayout() { return layout; } public Widget getSelected() { return selected; } public void select(Widget w) { if (w == layout) { selected = null; pp.setProperties(layout.getProperties(), w); } else { selected = w; } if (w != null) { pp.setProperties(w.getProperties(), w); } pp.validate(); pp.repaint(); } public void removeWidget(Widget w) { if (w != null) { w.getParent().removeWidget(w); if (selected == w) { selected = null; } changed = true; } } public void removeAllWidgets() { layout.removeAllWidgets(); selected = null; changed = true; } public Vector<Layout> findLayouts(int x, int y) { return findLayout(layout, x, y); } protected Vector<Layout> findLayout(Layout l, int x, int y) { Vector<Layout> res = new Vector<Layout>(); if (l.clickedOn(x, y)) { for (Widget w : l.getWidgets()) { if (w instanceof Layout) { Vector<Layout> tmp = findLayout((Layout)w, x, y); for (Layout lt : tmp) { res.add(lt); } } } res.add(l); } return res; } public Vector<Widget> findWidgets(int x, int y) { return findWidgets(layout, x, y); } public Vector<Widget> findWidgets(Layout l, int x, int y) { Vector<Widget> res = new Vector<Widget>(); for (Widget w : l.getWidgets()) { if (w.clickedOn(x, y)) { if (w instanceof Layout) { Vector<Widget> tmp = findWidgets((Layout)w, x, y); for (Widget wt : tmp) res.add(wt); } res.add(w); } } return res; } public void generateSource(PrintWriter pw, String pkg) { pw.println("package "+pkg+";"); pw.println("public class DroidDrawSetup {"); pw.println("public static void setup(Context c) {"); generateSource(layout, pw, pkg); pw.println("}"); pw.println("}"); } public void generateSource(Layout l, PrintWriter pw, String pkg) { for (Widget w : l.getWidgets()) { generateSource(w, pw, pkg); } } public void generateSource(Widget w, PrintWriter pw, String pkg) { if (w instanceof Layout) { generateSource((Layout)w, pw, pkg); } else { if (w instanceof Button) { StringProperty onClick = (StringProperty)w.getPropertyByAttName("droiddraw:onClickListener"); if (onClick != null && onClick.getStringValue() != null && onClick.getStringValue().length() > 0) { String id = w.getId(); int ix = id.indexOf("/"); id = id.substring(ix+1); pw.println("View b"+id+" = context.findViewById(R.ids."+id+";"); pw.println("b"+id+".setOnClickListener(new "+onClick.getStringValue()+"());"); } } } } public void generate(PrintWriter pw) { pw.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); generateWidget(layout, pw); pw.flush(); } @SuppressWarnings("unchecked") protected void generateWidget(Widget w, PrintWriter pw) { pw.println("<"+w.getTagName()); Vector<Property> props = (Vector<Property>)w.getProperties().clone(); if (w != layout) w.getParent().addOutputProperties(w, props); for (Property prop : props) { if (prop.getValue() != null && prop.getValue().toString().length() > 0 && !prop.isDefault()) { // Work around an android bug... *sigh* if (w instanceof CheckBox && prop.getAtttributeName().equals("android:padding")) continue; pw.println(prop.getAtttributeName()+"=\""+prop.getValue()+"\""); } } pw.println(">"); if (w instanceof Layout) { for (Widget wt : ((Layout)w).getWidgets()) { generateWidget(wt, pw); } } pw.println("</"+w.getTagName()+">"); } public void setURLOpener(URLOpener open) { this.opener = open; } public URLOpener getURLOpener() { return opener; } public File getArrayFile() { return arrayFile; } public void setArrayFile(File arrayFile) { this.arrayFile = arrayFile; } public Hashtable<String, Vector<String>> getArrays() { return arrays; } public void setArrays(Hashtable<String, Vector<String>> arrays) { this.arrays = arrays; } public void setArrays(File f) { setArrayFile(f); try { setArrays(ArrayHandler.load(new FileInputStream(f))); } catch (IOException ex) { error(ex); } catch (SAXException ex) { error(ex); } catch (ParserConfigurationException ex) { error(ex); } } public String getTheme() { return theme; } public void setTheme(String theme) { this.theme = theme; } public Preferences getPreferences() { if (prefs == null) { prefs = new Preferences(); try { prefs.load(); } catch (SecurityException ex) {} } return prefs; } }