2013/05/16

The Setting of Dynamic Editors

In the interface of editing properties, editor can be extended as you can customize two Editors A and B, in which the value of B changes with that of A.

The method is quite simple that an ElementPropertyChangeListener is to be added to TDataBox and that when property A has been changed and thus the value the value in the DataBox is changed the Editor of property B will load that value.
public void propertyChange(PropertyChangeEvent evt) {
    String pro = TWaverUtil.getPropertyName(evt);
    Element element = (Element)evt.getSource();
    if("A".equals(pro)){
        CustomUtil.ref = CustomUtil.getBRefs((Integer)element.getUserProperty("A"));
    }
}
public static String[] getBRefs(int v){
    switch(v){
    case 1:
    case 2:
        return REF1;
    default:
        return REF2;
    }
}
public Component getTableCellEditorComponent(JTable table, Object value,
        boolean isSelected, int row, int column) {
    JComboBox comboBox = (JComboBox)this.getComponent();
    comboBox.removeAllItems();   
    if(CustomUtil.ref!=null){
        for(int i=0;i<CustomUtil.ref.length;i++){
            comboBox.addItem(CustomUtil.ref[i]);
        }
    }
   
    return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}



2013/04/15

Add the Function “AutoComplete” in JTextField(2)

In the article Add the Function “AutoComplete” in JTextField,we receive many feedback. One is: “Can you help me, make a search like in facebook? every search there are pictures and text”。 It's not very hard to do this, we can just set the JComboBox's Renderer, By default the Renderer of JComboBox is a JLabel, and the JLabel itself has a function "setIcon", so we can set the Icon to the JLabel, the Code is like this:

cbInput.setRenderer(new DefaultListCellRenderer(){
 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
 if(value != null){
 this.setText(value.toString());
 String url = value.toString().replaceAll(" ", "_") + ".png";
 ImageIcon icon = getImageIcon(url, IMAGE_ICON_CACHE);
 if(icon != null){
 this.setIcon(icon);
 }
 }
 return this;
 }
 });


In the "getImageIcon" method ,we cache the images, so it no need create a new ImageIcon every time where the UI is Refreshed, which can improve the response time.


public static ImageIcon getImageIcon(String url,Map<String, ImageIcon> IMAGE_ICON_CACHE) {
 if (url == null) {
 return null;
 }
 if (IMAGE_ICON_CACHE.get(url) == null) {
 ImageIcon image = null;
 InputStream in =ResourceAgent.class.getResourceAsStream(url);
 if (in != null) {
 try {
 byte buffer[] = new byte[in.available()];
 for (int i = 0, n = in.available(); i < n; i++) {
 buffer[i] = (byte) in.read();
 }
 Toolkit toolkit = Toolkit.getDefaultToolkit();
 Image img = toolkit.createImage(buffer);
 image = new ImageIcon(img);
 in.close();
 } catch (IOException ex) {
 ex.printStackTrace();
 return null;
 }
 }
 if (image == null) {
 if (ClassLoader.getSystemResource(url) != null) {
 image = new ImageIcon(ClassLoader.getSystemResource(url));
 } else {
 image = new ImageIcon(url);
 }
 }
 if (image == null) {
 System.err.println("can't load image '" + url + "'");
 } else {
 IMAGE_ICON_CACHE.put(url, image);
 }
 }
 return (ImageIcon) IMAGE_ICON_CACHE.get(url);
 }
Finally, it's like this:
The full code file:
package demo; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import demo.images.*; public class Test { public static void main(String[] args) throws Exception { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); JFrame frame = new JFrame(); frame.setTitle("Auto Completion Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setBounds(200, 200, 500, 400); ArrayList<String> items = new ArrayList<String>(); // Locale[] locales = Locale.getAvailableLocales(); // for (int i = 0; i < locales.length; i++) { // String item = locales[i].getDisplayName(); // items.add(item); // } items.add("China"); items.add("China Hongkong"); items.add("China Taiwan"); items.add("USA"); items.add("Japan"); JTextField txtInput = new JTextField(); setupAutoComplete(txtInput, items); txtInput.setColumns(30); frame.getContentPane().setLayout(new FlowLayout()); frame.getContentPane().add(txtInput, BorderLayout.NORTH); frame.setVisible(true); } private static boolean isAdjusting(JComboBox cbInput) { if (cbInput.getClientProperty("is_adjusting") instanceof Boolean) { return (Boolean) cbInput.getClientProperty("is_adjusting"); } return false; } private static void setAdjusting(JComboBox cbInput, boolean adjusting) { cbInput.putClientProperty("is_adjusting", adjusting); } public static ImageIcon getImageIcon(String url,Map<String, ImageIcon> IMAGE_ICON_CACHE) { if (url == null) { return null; } if (IMAGE_ICON_CACHE.get(url) == null) { ImageIcon image = null; InputStream in =ResourceAgent.class.getResourceAsStream(url); if (in != null) { try { byte buffer[] = new byte[in.available()]; for (int i = 0, n = in.available(); i < n; i++) { buffer[i] = (byte) in.read(); } Toolkit toolkit = Toolkit.getDefaultToolkit(); Image img = toolkit.createImage(buffer); image = new ImageIcon(img); in.close(); } catch (IOException ex) { ex.printStackTrace(); return null; } } if (image == null) { if (ClassLoader.getSystemResource(url) != null) { image = new ImageIcon(ClassLoader.getSystemResource(url)); } else { image = new ImageIcon(url); } } if (image == null) { System.err.println("can't load image '" + url + "'"); } else { IMAGE_ICON_CACHE.put(url, image); } } return (ImageIcon) IMAGE_ICON_CACHE.get(url); } public static void setupAutoComplete(final JTextField txtInput, final ArrayList<String> items) { final DefaultComboBoxModel model = new DefaultComboBoxModel(); final Map<String,ImageIcon> IMAGE_ICON_CACHE = new HashMap<String, ImageIcon>(); final JComboBox cbInput = new JComboBox(model) { public Dimension getPreferredSize() { return new Dimension(super.getPreferredSize().width, 0); } }; setAdjusting(cbInput, false); for (String item : items) { model.addElement(item); } cbInput.setSelectedItem(null); cbInput.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (!isAdjusting(cbInput)) { if (cbInput.getSelectedItem() != null) { txtInput.setText(cbInput.getSelectedItem().toString()); } } } }); cbInput.setRenderer(new DefaultListCellRenderer(){ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if(value != null){ this.setText(value.toString()); String url = value.toString().replaceAll(" ", "_") + ".png"; ImageIcon icon = getImageIcon(url, IMAGE_ICON_CACHE); if(icon != null){ this.setIcon(icon); } } return this; } }); txtInput.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { setAdjusting(cbInput, true); if (e.getKeyCode() == KeyEvent.VK_SPACE) { if (cbInput.isPopupVisible()) { e.setKeyCode(KeyEvent.VK_ENTER); } } if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) { e.setSource(cbInput); cbInput.dispatchEvent(e); if (e.getKeyCode() == KeyEvent.VK_ENTER) { txtInput.setText(cbInput.getSelectedItem().toString()); cbInput.setPopupVisible(false); } } if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { cbInput.setPopupVisible(false); } setAdjusting(cbInput, false); } }); txtInput.getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { updateList(); } public void removeUpdate(DocumentEvent e) { updateList(); } public void changedUpdate(DocumentEvent e) { updateList(); } private void updateList() { setAdjusting(cbInput, true); model.removeAllElements(); String input = txtInput.getText(); if (!input.isEmpty()) { for (String item : items) { if (item.toLowerCase().startsWith(input.toLowerCase())) { model.addElement(item); } } } cbInput.hidePopup(); cbInput.setPopupVisible(model.getSize() > 0); setAdjusting(cbInput, false); } }); txtInput.setLayout(new BorderLayout()); txtInput.add(cbInput, BorderLayout.SOUTH); } }
The ResourceAgent class:

package demo.images;


public class ResourceAgent {
}

and at the some folder, put the icons related to the text show on the list. 



2013/03/28

To Customize Interactions in TWaver

Recently, there comes a fairly demanding requirement that a node dragged from the toolbar will be inserted in the link under the mouse, during which that the the section of the link to be inserted by the node will protrude over its original height.
Nodes can be inserted in to a link through the deletion of the original one and the addition of two new ones, but the protruding part is troublesome, which, however can be realized with the help of ShapeLink in TWaverFlex.
Method:
  1. Listen to the event "DragEvent.DRAG_OVER" in network and judge whether there is Link under the mouse by the method "network#getElementsByDisplayObject". If there is, set this link as transparent(link.setStyle(Styles.LINK_ALPHA, 0) and then add a ShapeLink; if there is not, set it as transparent, restore it and delete ShapeLink. Please refer to the method "updateMark" or "removeMark" for detailed codes:
    private function updateMark(link:Link, data:Object, point:Point):void {
        if(!network.elementBox.contains(markLink)){
            markLink.fromNode = link.fromNode;
            markLink.toNode = link.toNode;
            markLink.setStyle(Styles.SHAPELINK_TYPE, Consts.SHAPELINK_TYPE_QUADTO);
            markLink.setStyle(Styles.LINK_WIDTH, link.getStyle(Styles.LINK_WIDTH));
            markLink.setStyle(Styles.LINK_COLOR, link.getStyle(Styles.LINK_COLOR));
            markLink.setStyle(Styles.LINK_ALPHA, link.getStyle(Styles.LINK_ALPHA));
           
            network.elementBox.add(markLink);
            lastLink = link;
            link.setStyle(Styles.LINK_ALPHA, 0);
        }
       
        var points:Collection = new Collection();
       
        var fromCenter:Point = markLink.fromNode.centerLocation;
        var toCenter:Point = markLink.toNode.centerLocation;
        var radius:Number = Math.sqrt(data.width*data.width + data.height*data.height)/2;
        var crossPoints:Object = getCrossPoints(fromCenter, toCenter, point, radius*2);
        if (crossPoints){
            if(fromCenter.x&lt;toCenter.x){
                points.addItem(crossPoints.p2);
                points.addItem(crossPoints.p2);
            }else{
                points.addItem(crossPoints.p1);
                points.addItem(crossPoints.p1);
            }
           
            var crossPoint:Point = new Point(crossPoints.p1.x + (crossPoints.p2.x-crossPoints.p1.x)/2,
                crossPoints.p1.y + (crossPoints.p2.y-crossPoints.p1.y)/2);
            var controlPoints:Object = getCrossPoints(crossPoint, point, point, radius*3);
            if(controlPoints){
                if(crossPoint.y &gt;= point.y){
                    points.addItem(controlPoints.p2);
                }else{
                    points.addItem(controlPoints.p1);
                }
            }else{
                if(point.y&gt;=fromCenter.y){
                    point.y = point.y - radius * 3;
                }else{
                    point.y = point.y + radius * 3;
                }
                points.addItem(point);
            }
            if(fromCenter.x&lt;toCenter.x){
                points.addItem(crossPoints.p1);
                points.addItem(crossPoints.p1);
            }else{
                points.addItem(crossPoints.p2);
                points.addItem(crossPoints.p2);
            }
        }else{
            points.addItem(fromCenter);
        }
        markLink.points = points;
    }

    private function removeMark(moving:Boolean):void {
        if(network.elementBox.contains(markLink)){
            var fromNode:Node = markLink.fromNode;
            var toNode:Node = markLink.toNode;
            network.elementBox.remove(markLink);
            if(lastLink &amp;&amp; moving){
                lastLink.setStyle(Styles.LINK_ALPHA, markLink.getStyle(Styles.LINK_ALPHA));
                lastLink = null;
            }
        }
    }

  2. The key is how to make sure which points among ShapeLink will protrude. The solution is to calculate the intersection of the semi-circle and line. Please refer to the method "getCrossPoints" for detailed codes: 
    private function getCrossPoints(p1:Point, p2:Point, circleCenter:Point, radius:Number):Object{
        var line_p1_x:Number = p1.x;
        var line_p1_y:Number = p1.y;
        var line_p2_x:Number = p2.x;
        var line_p2_y:Number = p2.y;
        var circle_center_x:Number = circleCenter.x;
        var circle_center_y:Number = circleCenter.y;
        var circle_radius:Number = radius;
        var line_k:Number =(line_p2_y-line_p1_y)/(line_p2_x-line_p1_x);
        var line_b:Number =line_p1_y-line_k*line_p1_x;
        var a:Number =1+line_k*line_k;
        var b:Number =-2*circle_center_x+2*line_k*line_b-2*line_k*circle_center_y;
        var c:Number =circle_center_x*circle_center_x+(line_b-circle_center_y)*(line_b-circle_center_y)-circle_radius*circle_radius; 
        var delta:Number =b*b-4*a*c;
        var x1:Number =(-b+Math.sqrt(delta))/(2*a);
        var x2:Number =(-b-Math.sqrt(delta))/(2*a);
        var y1:Number =line_k*x1+line_b;
        var y2:Number =line_k*x2+line_b;
       
        if(delta&gt;=0){
            return {p1: new Point(x1, y1), p2: new Point(x2, y2)};
        }else{
            return null;
        }
    }
  3. Delete ShapeLink and establish two Links in the event "DragEvent.DRAG_DROP".

2013/03/10

An Overview of TWaver Android

The characteristics of Android
Accounting for a large part in smartphone market, Android of Google is an open source operating system which is based on Linux kernel, mainly used on mobiles devices. The application layer of Android is developed by Java. It runs in Dalvik virtual machine. Every Android application functions in separate virtual machine, which ensures the protection and the thread safety of the resources between applications.

Android framework
Programming languages in Android: Java, NDK, HTML

Other programming languages can also be used to develop Android application apart from the usually used Java in Android application.

The three languages above have their own features. Java is the native-support language of Android, so it can be used in local development. As the improvement of Android system, it becomes more and more efficient; the development of Android NDK is comparatively complicated, but with the advantage in performance of C programming language, the efficiency of the program can be improved(As a cross-platform programming language, C programming language is widely used in games); now HTML has become a tendency and the applications on cross-browsers and cross-platforms can also be available through Web App.

There are quite some devices supporting Android and it is frequently updated. Progress has been made to the improvement on Dalvik virtual machine and Android application framework by Google. For example, the JIT system has been introduced in Android 2.2, so the efficiency has been increased twice to four times. At the same time, Android 4.1 has also made its contribution to UI fluency. The efforts Google made deserve praise, however, at the other hand, that also tells the inefficiency in the earlier versions. Then how much room of improvement remains? Let's see the data. We will test Android Java and SunJava on Android tablet and PC respectively to see their difference in performance and the suitable scenario to them.

The difference between Dalvik VM and SunJavaVM in perfomance

Devices: MacBook Pro(2.26 GHz Intel Core 2 Duo),Google Nexus 7(1.3GHz quad-core Tegra 3)
Software environment: Java 1.6.0_37 and Android 4.2.1
Contents: basic APIs including arithmetics, set operation(List and Map operation)

Here only the results are listed as the test codes in Java 1.6.0_37 are also java programming language. Please refer to PerformanceDemo.java in TWaver Android Demo:

Android 4.2.1 takes 12-50 times as Java 6 does in running a same program. In addition, the efficiency with other programming language on PC (like Flex or JavaScript) is also better than Nexus 7 tablet.

What about HTML? HTML on desktop is four times quicker than on mobile Chrome server, which mainly reflect the reality of the hardware; Android with JavaScript is superior to with original Java, at least in Chrome browser. However, this test only reflects the basic performance of programming languages. As for the convenience of development, Java is still the first choice.

The test indicates that compared to PC android devices is obviously deficient while Dalvik VM is not able to fully exert the performance of hardware, which should be noticed in developing applications in order to balance user experience and functions.

The difference in performance under platforms with programming languages:

The Target Platform of TWaver Android

There are many developing languages can be chosen in TWaver graphical components while there is still a vacancy on mobile platform. Although TWaver HTML5 is capable of running on Android and iOS, it is quite difficult to have the same user experience as in local application. In order to make that come true, TWaver Android is set to run on mobile platforms for graphical presentation of data with brand-new design architecture and higher UI efficiency to compensate for the deficiency in the performance of the mobile device itself. What's more, a smooth user experience with new multi-touch and roaming operation.

The Support of large amounts of data in TWaver Android

Compared to other edition of TWaver, some changes have been made in TWaver Android framework. The efficiency of DataBox has been improved several times and the problems existing in Link and Group have also been solved; the delay-invalid system has also been advanced in UI presentation; touch operations have been completely compatible; the scenarios in which Node, Link and Group have been used together are available. Even operations with more than one thousand data are still smooth on Nexus 7 tablet. The result is quite agreeable as it can be widely applied considering the problem that lies in the difference between PC and tablet and the efficiency of DM.

The contrast between the loading time of network with different amounts of elements.

Suggested Platforms of TWaver Android

A better view of graphical presentation of data can be enjoyed with a big screen, so tablets are suggested. Versions higher than Android 3.0 (Android 3.0 in which Drag and Drop is available is also included) are supported. Actually TWaver Android is tested and adjusted mainly with the help of Nexus 7/10 and Samsung Android, which, however, does not mean that it is incapable of functioning in mobile phones or low-version Android devices. Instead, TWaver Android Demo is able to run smoothly on Google Nexus S mobile phones. The simplified version of TWaver Android can be applied if you would like your device with Android 2.* to be compatible with it.

Run on Nexus 7 and Nexus S

2013/02/27

Customized Interactions on Network in TWaver Flex

The following several interactive modes have been provided in TWaver as default:


In most cases, they are sufficient, but customized interactions are also available. Now we will introduce how to customize interactive mode with the combination of just a "pan" and the general interactive modes:
If an element is selected, then all the operations will be performed on it; if not, then the "pan" interactive mode as dragging the background.
Generally, in "pan" interactive mode, elements cannot be selected and dragged. It is the same as general map interactive mode and they can only show the range of dragging. But through some operations element-dragging and background-dragging can be separated.
The logic of the method "handleMouseDown" has been changed.
public class CustomInteractionHandler extends BasicInteractionHandler {
        private var pressPoint:Point;
        private var oldButtonMode:Boolean;
        private var olduseHandCursor:Boolean;

        public function CustomInteractionHandler(network:Network) {
            super(network);
            installListeners();
        }

        override public function installListeners():void {
            this.network.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
            this.network.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
        }

        /**
         * @inheritDoc
         *
         */
        override public function uninstallListeners():void {
            this.network.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
            this.network.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
        }

        private function handleMouseDown(e:MouseEvent):void {
            if (!this.network.isValidMouseEvent(e) || this.network.isMovingElement || this.network.isEditingElement) {
                return;
            }
            var element:IElement=network.getElementByMouseEvent(e);
            if (element) {
                if (!this.network.selectionModel.contains(element)) {
                    this.network.selectionModel.setSelection(element);
                }
            } else {
                this.network.selectionModel.clearSelection();
                pressPoint=new Point(e.stageX, e.stageY);
                this.network.addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
                oldButtonMode=this.network.buttonMode;
                olduseHandCursor=this.network.useHandCursor;
                this.network.useHandCursor=true
                this.network.buttonMode=true
                this.network.cursorManager.removeAllCursors();
            }

        }

        private function handleMouseUp(e:MouseEvent):void {
            if (pressPoint != null) {
                pressPoint=null;
                this.network.buttonMode=oldButtonMode;
                this.network.useHandCursor=olduseHandCursor;
                this.network.removeEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
            }
        }

        private function handleMouseMove(e:MouseEvent):void {
            if (!e.buttonDown) {
                pressPoint=null;
                this.network.buttonMode=oldButtonMode;
                this.network.useHandCursor=olduseHandCursor;
                this.network.removeEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
                return;
            }
            if (this.network.verticalScrollBar == null &amp;&amp; this.network.horizontalScrollBar == null) {
                return;
            }
            var xOffset:Number=pressPoint.x - e.stageX;
            var yOffset:Number=pressPoint.y - e.stageY;
            pressPoint.x-=xOffset;
            pressPoint.y-=yOffset;
            this.network.horizontalScrollPosition+=xOffset;
            this.network.verticalScrollPosition+=yOffset;
        }
    }
Now it has become much more simpler.
network.interactionHandlers=new Collection([
       new CustomInteractionHandler(this),
new DefaultInteractionHandler(this),]);

2013/02/22

How to customize Icon in Menu in TWaver Flex

Icon in Menu must be specified through iconField and iconFunction. But in the two methods above, Icon has to be done with Class name of embedded resources. So if you would like to set dynamic image (like URL) as Icon in Menu, MenuItemRenderer must be customized.
First, set a class CustomMenuItemRender to inherit MenuItemRenderer with a parameter added as the component of customized Icon.
 private var image:UIComponent = new UIComponent();
Then rewrite the method “measure” (to calculate the width and height of “MenuItem”).
 override protected function measure():void {
    super.measure();
  
    if (separatorIcon || listData == null) {
        return;
    }
  
    var imageAsset:IImageAsset = Utils.getImageAsset(data.@iconName);
    if(imageAsset == null){
        return;
    }
    measuredWidth += imageAsset.width;
    if(imageAsset.height &gt; measuredHeight){
        measuredHeight = imageAsset.height;
    }
}
Rewrite the method “commitProperties” (Rewrite and add Icon & set the width and height of Icon).
 override protected function commitProperties():void {
    super.commitProperties();
   
    if (separatorIcon || listData == null) {
        return;
    }
   
    var imageAsset:IImageAsset = Utils.getImageAsset(data.@iconName);
    if(imageAsset == null){
        return;
    }
    image.width = imageAsset.width;
    image.height = imageAsset.height;
    image.graphics.beginBitmapFill(imageAsset.getBitmapData());
    image.graphics.drawRect(0, 0, image.width, image.height);
    image.graphics.endFill();
    if(!this.contains(image)){
        this.addChild(image);
    }
}
Rewrite the method “updateDisplayList”(specify the position of Icon: since Icon is at the left side, so we’d better first call the method “super” before the movement of Label):
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
    super.updateDisplayList(unscaledWidth, unscaledHeight);
   
    if (separatorIcon || listData == null) {
        return;
    }
   
    var imageAsset:IImageAsset = Utils.getImageAsset(data.@iconName);
    if(imageAsset == null){
        return;
    }
    if(typeIcon){
        typeIcon.x += imageAsset.width;
    }
    if(label){
        label.x += imageAsset.width;
    }
}
Rewrite the method “measuredIconWidth” (to calculate the width of Icon):
override public function get measuredIconWidth():Number {
    var imageAsset:IImageAsset = Utils.getImageAsset(data.@iconName);
    if(imageAsset == null){
        return 0 ;
    }else{
        var horizontalGap:Number = getStyle(&quot;horizontalGap&quot;);
        return imageAsset.width + horizontalGap;
    }
}
At last, specify “ItemRenderer” of Menu with customized CustomMenuItemRenderer. Notice that the name of icon specified by iconName should be used. (Here is the name of the registered image in TWawer.) Also, other names can be used. Do not forget to change “@iconNme” in “CustomMenuItemRenderer”.
var menu:Menu = Menu.createMenu(network, myMenuData, false);
menu.labelField = &quot;@label&quot;;
menu.itemRenderer = new ClassFactory(CustomMenuItemRenderer);
var point:Point = network.getLogicalPoint(event.mouseEvent);
network.callLater(function():void{
    menu.show(point.x, point.y);
});
Specify the XML file of the data in Menu as follows:
    &lt;mx:XML format=&quot;e4x&quot; id=&quot;myMenuData&quot;&gt;
        &lt;root&gt;
            &lt;menuitem label=&quot;www.servasoftware.com&quot; iconName=&quot;databox_icon&quot;&gt;
                &lt;menuitem label=&quot;TWaver&quot; type=&quot;check&quot; toggled=&quot;true&quot;&gt;
                    &lt;menuitem label=&quot;Java&quot; type=&quot;radio&quot; groupName=&quot;one&quot;/&gt;
                    &lt;menuitem label=&quot;Web&quot; type=&quot;radio&quot; groupName=&quot;one&quot; toggled=&quot;true&quot;/&gt;
                    &lt;menuitem label=&quot;Flex&quot; type=&quot;radio&quot; groupName=&quot;one&quot; iconName=&quot;bus_icon&quot;/&gt;
                    &lt;menuitem label=&quot;Silverlight&quot; type=&quot;radio&quot; groupName=&quot;one&quot;/&gt;
                &lt;/menuitem&gt;
                &lt;menuitem type=&quot;separator&quot;/&gt;
                &lt;menuitem label=&quot;2BizBox&quot; iconName=&quot;data_icon&quot;/&gt;
            &lt;/menuitem&gt;
            &lt;menuitem label=&quot;www.2bizbox.com&quot;/&gt;
            &lt;menuitem label=&quot;twaver.servasoft.com&quot;/&gt;
        &lt;/root&gt;
    &lt;/mx:XML&gt;
&lt;/code&gt;

Please see the official document for more on the method MenuItemRenderer:

http://livedocs.adobe.com/flex/3/html/help.html?content=menucontrols_3.htmlhttp://livedocs.adobe.com/flex/3/html/help.html?content=menucontrols_3.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/controls/menuClasses/MenuItemRenderer.html
 

 

2013/01/24

A Different Way to use JPopupMenu

To Swing developers, JPopupMenu must be a familiar component. However, most people only take it as 'right-click popup menu’. Actually, there are much more usages of JPopupMenu, so with the help of it, all the requirement involving popup effects can be met. For example, if we input "import java.util." with development tools and the drop up menu with the probable options listed will pop up automatically when inputting ".".

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

import twaver.TWaverUtil;

public class PopupTipDemo extends JFrame {

    String[] messages = new String[] {
            &quot;getTWaverJava()&quot;,
            &quot;getTWaverWeb()&quot;,
            &quot;getTWaverFlex()&quot;,
            &quot;getTWaverDotNET()&quot;,
            &quot;getTWaverGIS()&quot;,
            &quot;getTWaverHTML5()&quot;,
            &quot;getTWaverJavaFX()&quot;,
            &quot;getTWaver...&quot;, };

    JLabel label = new JLabel(&quot;TWaver makes everything easy!&quot;);
    JList list = new JList(messages);
    JComponent tip = new JScrollPane(list);
    JTextArea text = new JTextArea();
    JPopupMenu popup = new JPopupMenu();

    public PopupTipDemo() {
        super(&quot;www.servasoftware.com&quot;);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.getContentPane().setLayout(new BorderLayout());
        this.getContentPane().add(new JScrollPane(text), BorderLayout.CENTER);
        this.tip.setPreferredSize(new Dimension(230, 80));
        this.label.setForeground(Color.BLUE);
        this.label.setHorizontalAlignment(SwingConstants.CENTER);
        this.popup.setLayout(new BorderLayout());
        this.popup.add(label, BorderLayout.NORTH);
        this.popup.add(tip, BorderLayout.CENTER);

        this.text.setText(&quot;// Try to press '.'\nimport twaver.Node;\nimport twaver.Link;\nimport twaver.network&quot;);
        this.text.setBackground(Color.WHITE);
        this.text.setForeground(Color.BLUE);
        this.text.setCaretColor(Color.RED);

        this.text.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                if (popup.isShowing()) {
                    popup.setVisible(false);
                } else if (e.getKeyCode() == KeyEvent.VK_PERIOD) {
                    Point point = text.getCaret().getMagicCaretPosition();
                    if (point != null) {
                        popup.show(text, point.x, point.y);
                    }
                    text.requestFocus();
                }
            }
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                PopupTipDemo demo = new PopupTipDemo();
                demo.setSize(400, 200);
                TWaverUtil.centerWindow(demo);
                demo.setVisible(true);
            }
        });
    }
}
In fact, in Demo of TWaver Java, QuickSearch (the well-encapsulated component--twaver.swing.TDropDownSelector) is equipped with the feature described above. Through the following several lines of codes in demo.DemoUtil can realize the drop-down effect. What you need to do is only to focus on the contents you want to show on the menu.

// create drop down selector
final TDropDownSelector selector = new TDropDownSelector(txtSearch, new JScrollPane(list)){
    public Dimension getSelectorSize(){
        int width = this.getSize().width;
        if(width &lt; 200){
            width = 200;
        }
        return new Dimension(width, 320);
    }
};

2013/01/17

To Test the Memory Usage of TWaver Java

We have already seen the high performance of TWaver Java, but what about its memory usage? What about it with different amounts of data? How does view component have effects on memory? We have tested the following amounts of data respectively:
  • to only add elements into the DataBox
  • to add elements into DataBox and show them on Network
  • to add elements into the DataBox and show them on Tree
  • to add elements into the DataBox and show them on Tree and Network
The following is the four situations of the memory usage of different number of elements. (x-axis: the number of nodes, unit: K; y-axis: the memory that elements take, unit: MB)
  1. To only add elements into the DataBox:
  2. To add elements into DataBox and show them on Network:
  3. To add elements into the DataBox and show them on Tree:
  4. To add elements into the DataBox and show them on Tree and Network:
We can see from the graphs above: the view components do have effects on the memory. But it is good that 100 thousand of nodes take only less than 500MB. During the process of the test, we find that system environment has had affected the results. (Here we have taken the average value.)

2013/01/07

Bundled links in Flex

We have already introduced the link-bundles in TWaver Java and found that there are quite some functions therein. More powerful functions have been provided in link-bundles in TWaver Flex.
Now let's first see the similar functions offered in TWaver Java:
link.setStyle(Styles.LINK_BUNDLE_ENABLE,false); //to judge whether the link is in a link-bundle or not
link.setStyle(Styles.LINK_BUNDLE_EXPANDED,false);  //to set whether the link-bundle is expanded or not
link.setStyle(Styles.LINK_BUNDLE_GAP,10); //to set the gap between every two links in the link-bundle
link.setStyle(Styles.LINK_HANDLER_YOFFSET, -5); //to set the vertical offset when bundled
link.setStyle(Styles.LINK_HANDLER_XOFFSET, -5); //to set the horizontal offset when bundled
 What's more, the grouped link-bundles are available in TWaver Flex as follows:
link.setStyle(Styles.LINK_BUNDLE_INDEPENDENT,true); //to set whether the link is bundled independently
link.setStyle(Styles.LINK_BUNDLE_ID, 0);  //to set the index of the groups of link-bundles

More functions supplied in bundleHandler:
link.setStyle(Styles.LINK_HANDLER_POSITION, position); //to set the position of the link handler and the value of position can be obtained from Consts
link.setStyle(Styles.LINK_HANDLER_COLOR, 0xFF0000);  //to set the color of the texts in the link handler
link.setStyle(Styles.LINK_HANDLER_FILL, true); //whether the link handler has the background color
link.setStyle(Styles.LINK_HANDLER_FILL_COLOR, 0x00FFFF); //to set the background color in the link handler
link.setStyle(Styles.LINK_HANDLER_XOFFSET, -5);   //to set the horizontal offset of the link handler
link.setStyle(Styles.LINK_HANDLER_YOFFSET, -5);  //to set the vertical offset of the link handler
The label of BundleHandle can be set by linkHandlerFunction of network to implement the functions mentioned in the last article.

In addition, the label which is used to be double-clicked to expand the bundled links can be set as being gradient, bold, italic and underlined.
link.setStyle(Styles.LINK_HANDLER_GRADIENT,Consts.GRADIENT_LINEAR_EAST); //to set the gradient type of the background color in the link handler
link.setStyle(Styles.LINK_HANDLER_GRADIENT_COLOR,0xFF00FF); //to set the gradient color of the background in the link handler
link.setStyle(Styles.LINK_HANDLER_BOLD,true); //to set the text in the link handler as bold
link.setStyle(Styles.LINK_HANDLER_ITALIC,true); //to set the text in the link handler as italic
link.setStyle(Styles.LINK_HANDLER_UNDERLINE,true); //to set the text in the link handler to be underlined
There are functions on looped link different from those on link in TWaver Flex:
link.setStyle(Styles.LINK_LOOPED_GAP, 10); //to set the gap between every two looped links
link.setStyle(Styles.LINK_LOOPED_TYPE, type); //to set the style of the looped links
link.setStyle(Styles.LINK_LOOPED_DIRECTION, direction); //to set the direction of looped links

2012/12/27

Bundled Links in Java

Link-bundling is the way to solve the problems arising from links in TWaver: when there are several links between two nodes, we can double-click one of them to bundle them and represent them with only a link-agent, hence the simplification visually.
There will appear the number of the links once they are bundled together. However, in so much link- bundles, how can we know that a particular number is of which link-bundle?

In fact, if you make some adjustment, you will see clearly how many links every link-bundle has on the network. For example, the sentence link.putLinkLabelRotatable(true); is used to rotate the label to be paralleled to the link-agent and the label of links can also be set to be placed in the middle. What’s more, the content to be shown in the label can also be edited by labelgenerator. For instance:

Before:
After:

In this way, the label is directly shown on the link to clearly divide up the links in the image above. The code is quite simple:
network.setElementLabelGenerator(new Generator(){
   public Object generate(Object object) {
    if (object instanceof Link) {
     Link link = (Link)object;
     if(link.isLinkBundleExpand()){
      return link.getName();
     }else if(link.isBundleAgent()){
      return "spring will come ("+link.getLinkBundleSize()+")"//
     }else{
      return null;
     }
    } else {
     return ((Element) object).getName();
    }
   }
  });
The functions on link-bundle provided in TWaver Java are listed as below:
Link:
isLinkBundleExpand() //to judge whether the links are bundled or not

isBundleAgent() // to judge whether a link is the agent-link or not

getLinkBundleSize() // to obtain the number of the links bundled together

getLinkBundleIndex // to obtain the index of a link in a link-bundle

setBundleExpand(boolean bundleExpand) //to set whether a link-bundle is expanded or not

putLinkBundleExpand  //to set whether the links are expanded or not

putLinkBundleIndex  //to set the index of a link in a link-bundle

putLinkBundleSize  //to set the number of the links bundled together
The functions related to bundle are also provided in DataBox:

setLinkBundleFilter(VisibleFilter linkBundleFilter) //to set the filter which filters out the links to be bundled: to specify which links are to be bundled and which are not

setLinkBundleAgentGenerator //This method is used to set the generator of link-agent in a link-bundle to change the default that a random link is specified as the agent-link in TWaver.

getBundledLinks(Node node1, Node node2) //to obtain the bundled links between two nodes


2012/12/13

To Add Icons at Any Place on Elements in TWaver Flex

With the help of iconAttachment system provided in TWaver Java, we can be able to put any number of images, characters and figures on an element as you want. For example:

The following codes can also be called to show the icons on an element in TWaver Flex.


server1.setStyle(Styles.ICONS_NAMES, ["att5","att6", "att7","att8"]);
server1.setStyle(Styles.ICONS_POSITION, Consts.POSITION_BOTTOMRIGHT_TOPRIGHT);
server1.setStyle(Styles.ICONS_ORIENTATION, Consts.ORIENTATION_TOP);
server1.setStyle(Styles.ICONS_XOFFSET, 5);

The function to add icons at different positions on an element is not provided in TWaver Flex as default, but it can be realized through function-expansion, since the FlexMVC model is so flexible.
Several iconAttachments can be created to show icons at different places with the Attachment component in TWaver.

If you are interested in it, you can try to show attachments at different places and directions, mainly by changing the following two methods in CustomIconAttachment:
private function getIconsSize(names:Array, orientation:String, xgap:Number, ygap:Number):Size
and
override public function draw(graphics:Graphics):void

2012/12/02

Tree of files in JTree

In fact, what this article will say has nothing to do with the components in TWaver just because I find it quite interesting. Therefore, I would like to share it with you. The file tree is completely based on JTree of swing. Now let's first see the final effect:

Screenshot:



Introduction on Functions:
  • to show the structure of files in tree
  • the icon of files should be systematic ones
  • The background color of the current node will be changed when you mouse over it.(such as the bricky-red background of the text "Windows" in the image above)
First let' s see the class structure:
  • main program
  • file tree, which inherits from JTree
  • the encapsulated node, including the name and the icon of a file, the File class and other identity
  • The customized node renderer which inherits from DefaultTreeCellRenderer
  • the customized TreeModel which inherits from DefaultTreeModel
Considering that there will be quite a few system files, it is not reasonable to initialize the whole tree at starting a program. So we have taken measures to delay loading: to only initialize the child nodes of a node only when it is to be expanded. Now add a listener into the construction of FileTree:
addTreeWillExpandListener(new TreeWillExpandListener() {
            @Override
            public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
                DefaultMutableTreeNode lastTreeNode =
(DefaultMutableTreeNode) event.getPath().getLastPathComponent();
                FileNode fileNode = (FileNode) lastTreeNode.getUserObject();
                if (!fileNode.isInit) {
                    File[] files;
                    if (fileNode.isDummyRoot) {
                        files = fileSystemView.getRoots();
                    } else {
                        files = fileSystemView.getFiles(
                                ((FileNode) lastTreeNode.getUserObject()).file,
                                false);
                    }
                    for (int i = 0; i < files.length; i++) {
                       //The name and the icon of the file is obtained through fileSystemView.
                        FileNode childFileNode = new FileNode(
                                fileSystemView.getSystemDisplayName(files[i]),
                                fileSystemView.getSystemIcon(files[i]), files[i],
                                false);
                        DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(childFileNode);
                        lastTreeNode.add(childTreeNode);
                    }
                    //To notify that a node has been changed
                    DefaultTreeModel treeModel1 = (DefaultTreeModel) getModel();
                    treeModel1.nodeStructureChanged(lastTreeNode);
                }
                //to change the identifier to avoid loading repeatedly
                fileNode.isInit = true;
            }
            @Override
            public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {

            }
        });
Of course, this method must be combined with TableModel. I have reloaded DefaultTreeModel and initialized the root node in the construction. Then load the method isLeaf.

Now let's think how to change the background color of a node when you mouse over it.

Right at this time I recalled the mistake I made during the time when I first learned Renderer that every node has a Renderer and I even tend to add a listener to it! The thing I have to emphasize is that Renderer is just a renderer. JTree will call it to render nodes onto the screen to show them. But remember however nodes there are, there is only one Renderer in a JTree!

Since it is useless to add a listener to Renderer, we have to change our focus, that is, to add listener which listens to mouse-moves in JTree and then repaint the node on which the mouse is.

addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
//to obtain the TreePath of the mouse
                TreePath path=getPathForLocation(e.getX(), e.getY());

//to calculate the area to be reprinted and to repaint JTree
                if(path!=null){
                    if(mouseInPath!=null){
                        Rectangle oldRect=getPathBounds(mouseInPath);
                        mouseInPath=path;
                        repaint(getPathBounds(path).union(oldRect));
                    }else{
                        mouseInPath=path;
                        Rectangle bounds=getPathBounds(mouseInPath);
                        repaint(bounds);
                    }
                }else if(mouseInPath!=null){
                    Rectangle oldRect=getPathBounds(mouseInPath);
                    mouseInPath=null;
                    repaint(oldRect);
                }
            }
        });
The background color of mouseInPath can be changed in Renderer only when the TreePath(mouseInPath) of MouseOver is saved in JTree.
FileTree fileTree=(FileTree)tree;
        JLabel label= (JLabel) super.getTreeCellRendererComponent(tree,value,sel,expanded,leaf,row,hasFocus);

        DefaultMutableTreeNode node=(DefaultMutableTreeNode)value;
        FileNode fileNode=(FileNode)node.getUserObject();
        label.setText(fileNode.name);
        label.setIcon(fileNode.icon);

        label.setOpaque(false);
//to change the background color if the node in rendering now is the node under the mouse over
        if(fileTree.mouseInPath!=null&&
                fileTree.mouseInPath.getLastPathComponent().equals(value)){
            label.setOpaque(true);
            label.setBackground(new Color(255,0,0,90));
        }
        return label;
Now it is the end of the article. If you have interest in it, welcome for your communication and good ideas.

2012/11/22

TWaverGIS Flex/.Net Has Added the Support for E-map

TWaverGIS has already realized the support for engines like Google Map, GeoServer and MapABC as default. Now some clients would like to combine TWaver with E-map engine, so we add the function to support it as default.

2012/11/15

An Appreciation and Analysis on an Enterprise Application of TWaver

Automatic scheduling function for production can be added into ERP software through the interface for Application Modules developed by TWaver with Swing. We find it quite efficient after the test: it will only cost about one hour to list the 2000 progressing production orders of a medium-sized manufacturer enterprise.

This interface will be projected onto the wall-screen of the company through a big screen or a projector, so that all the members will be able to see the arrangement of tasks and the plans at any time. Some technical points:


2012/11/08

To Test the Code Coverage in Flex with FlexCover

During the process of development, when you want to have a look at the current code coverage as whether there are any unexecuted codes, you can do it with FlexCover. Here are the steps:
  1. Download FlexCover-SDK,http://code.google.com/p/flexcover/
  2. Install CoverageViewer.air:
  3. Please copy a sdk, take the edition 4.1 as an example, and then copy the sdk-modifications-4_0 in the download package into sdk, covering the original file.

  4. Add the revised SDK into the Flash Builder.
  5. Set the current SDK in use as copySDK.
  6. The operation you have done is successful if there is a file named *.cvm now in catalogue bin-debug.
  7. Run CoverageViewer and load the cvm file that has been generated.
  8. Run the demo and see the code coverage: