1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.common.layout;
18 
19 import static com.android.SdkConstants.ANDROID_URI;
20 import static com.android.SdkConstants.ATTR_EMS;
21 import static com.android.SdkConstants.REQUEST_FOCUS;
22 
23 import com.android.annotations.NonNull;
24 import com.android.annotations.Nullable;
25 import com.android.ide.common.api.IMenuCallback;
26 import com.android.ide.common.api.INode;
27 import com.android.ide.common.api.INodeHandler;
28 import com.android.ide.common.api.IViewRule;
29 import com.android.ide.common.api.InsertType;
30 import com.android.ide.common.api.RuleAction;
31 
32 import java.util.List;
33 
34 /**
35  * An {@link IViewRule} for android.widget.EditText.
36  */
37 public class EditTextRule extends BaseViewRule {
38 
39     @Override
onCreate(@onNull INode node, @NonNull INode parent, @NonNull InsertType insertType)40     public void onCreate(@NonNull INode node, @NonNull INode parent,
41             @NonNull InsertType insertType) {
42         super.onCreate(node, parent, insertType);
43 
44         if (parent != null) {
45             INode focus = findFocus(findRoot(parent));
46             if (focus == null) {
47                 // Add <requestFocus>
48                 node.appendChild(REQUEST_FOCUS);
49             }
50 
51             if (parent.getBounds().w >= 320) {
52                 node.setAttribute(ANDROID_URI, ATTR_EMS, "10"); //$NON-NLS-1$
53             }
54         }
55     }
56 
57     /**
58      * {@inheritDoc}
59      * <p>
60      * Adds a "Request Focus" menu item.
61      */
62     @Override
addContextMenuActions(@onNull List<RuleAction> actions, final @NonNull INode selectedNode)63     public void addContextMenuActions(@NonNull List<RuleAction> actions,
64             final @NonNull INode selectedNode) {
65         super.addContextMenuActions(actions, selectedNode);
66 
67         final boolean hasFocus = hasFocus(selectedNode);
68         final String label = hasFocus ? "Clear Focus" : "Request Focus";
69 
70         IMenuCallback onChange = new IMenuCallback() {
71             @Override
72             public void action(
73                     @NonNull RuleAction menuAction,
74                     @NonNull List<? extends INode> selectedNodes,
75                     @Nullable String valueId,
76                     @Nullable Boolean newValue) {
77                 selectedNode.editXml(label, new INodeHandler() {
78                     @Override
79                     public void handle(@NonNull INode node) {
80                         INode focus = findFocus(findRoot(node));
81                         if (focus != null && focus.getParent() != null) {
82                             focus.getParent().removeChild(focus);
83                         }
84                         if (!hasFocus) {
85                             node.appendChild(REQUEST_FOCUS);
86                         }
87                     }
88                 });
89             }
90         };
91 
92         actions.add(RuleAction.createAction("_setfocus", label, onChange, //$NON-NLS-1$
93                 null, 5, false /*supportsMultipleNodes*/));
94         actions.add(RuleAction.createSeparator(7));
95     }
96 
97     /** Returns true if the given node currently has focus */
hasFocus(INode node)98     private static boolean hasFocus(INode node) {
99         INode focus = findFocus(node);
100         if (focus != null) {
101             return focus.getParent() == node;
102         }
103 
104         return false;
105     }
106 
107     /** Returns the root/top level node in the view hierarchy that contains the given node */
findRoot(INode node)108     private static INode findRoot(INode node) {
109         // First find the parent
110         INode root = node;
111         while (root != null) {
112             INode parent = root.getParent();
113             if (parent == null) {
114                 break;
115             } else {
116                 root = parent;
117             }
118         }
119 
120         return root;
121     }
122 
123     /** Finds the focus node (not the node containing focus, but the actual request focus node
124      * under a given node */
findFocus(INode node)125     private static INode findFocus(INode node) {
126         if (node.getFqcn().equals(REQUEST_FOCUS)) {
127             return node;
128         }
129 
130         for (INode child : node.getChildren()) {
131             INode focus = findFocus(child);
132             if (focus != null) {
133                 return focus;
134             }
135         }
136         return null;
137     }
138 
139 }
140