1 // Selection.cpp:  Selectable graphical things, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 //   Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 //
20 
21 #include "namedStrings.h"
22 #include "Selection_as.h"
23 #include "as_object.h" // for inheritance
24 #include "log.h"
25 #include "fn_call.h"
26 #include "Global_as.h"
27 #include "NativeFunction.h"
28 #include "AsBroadcaster.h"
29 #include "TextField.h"
30 
31 // For getting and setting focus
32 #include "VM.h"
33 #include "movie_root.h"
34 
35 namespace gnash {
36 
37 namespace {
38     as_value selection_getBeginIndex(const fn_call& fn);
39     as_value selection_getCaretIndex(const fn_call& fn);
40     as_value selection_getEndIndex(const fn_call& fn);
41     as_value selection_getFocus(const fn_call& fn);
42     as_value selection_setFocus(const fn_call& fn);
43     as_value selection_setSelection(const fn_call& fn);
44 
45     void attachSelectionInterface(as_object& o);
46 }
47 
48 
49 // extern (used by Global.cpp)
50 void
selection_class_init(as_object & where,const ObjectURI & uri)51 selection_class_init(as_object& where, const ObjectURI& uri)
52 {
53 	// Selection is NOT a class, but a simple object, see Selection.as
54     as_object* o = registerBuiltinObject(where, attachSelectionInterface,
55             uri);
56 
57     /// Handles addListener, removeListener, and _listeners.
58     AsBroadcaster::initialize(*o);
59 
60     // All properties are protected using ASSetPropFlags.
61     Global_as& gl = getGlobal(where);
62     as_object* null = nullptr;
63     callMethod(&gl, NSV::PROP_AS_SET_PROP_FLAGS, o, null, 7);
64 }
65 
66 void
registerSelectionNative(as_object & global)67 registerSelectionNative(as_object& global)
68 {
69     VM& vm = getVM(global);
70 
71     vm.registerNative(selection_getBeginIndex, 600, 0);
72     vm.registerNative(selection_getEndIndex, 600, 1);
73     vm.registerNative(selection_getCaretIndex, 600, 2);
74     vm.registerNative(selection_getFocus, 600, 3);
75     vm.registerNative(selection_setFocus, 600, 4);
76     vm.registerNative(selection_setSelection, 600, 5);
77 }
78 
79 namespace {
80 
81 void
attachSelectionInterface(as_object & o)82 attachSelectionInterface(as_object& o)
83 {
84     VM& vm = getVM(o);
85 
86     const int flags = PropFlags::dontEnum |
87                       PropFlags::dontDelete |
88                       PropFlags::readOnly;
89 
90 	o.init_member("getBeginIndex", vm.getNative(600, 0), flags);
91 	o.init_member("getEndIndex", vm.getNative(600, 1), flags);
92 	o.init_member("getCaretIndex", vm.getNative(600, 2), flags);
93 	o.init_member("getFocus", vm.getNative(600, 3), flags);
94 	o.init_member("setFocus", vm.getNative(600, 4), flags);
95 	o.init_member("setSelection", vm.getNative(600, 5), flags);
96 
97 }
98 
99 as_value
selection_getBeginIndex(const fn_call & fn)100 selection_getBeginIndex(const fn_call& fn)
101 {
102     movie_root& mr = getRoot(fn);
103     DisplayObject* focus = mr.getFocus();
104 
105     TextField* tf = dynamic_cast<TextField*>(focus);
106 
107     if (!tf) return as_value(-1);
108 
109     return as_value(tf->getSelection().first);
110 
111 }
112 
113 /// Return -1 if focus is not a TextField, otherwise the 0-based index of the
114 /// selection.
115 //
116 /// An alternative implementation would have a getCaretIndex in the DisplayObject
117 /// base class, with a default implementation returning -1. We would still
118 /// have to check for no-focus events here, though.
119 as_value
selection_getCaretIndex(const fn_call & fn)120 selection_getCaretIndex(const fn_call& fn)
121 {
122     movie_root& mr = getRoot(fn);
123     DisplayObject* focus = mr.getFocus();
124 
125     TextField* tf = dynamic_cast<TextField*>(focus);
126 
127     if (!tf) return as_value(-1);
128 
129     return as_value(tf->getCaretIndex());
130 }
131 
132 
133 as_value
selection_getEndIndex(const fn_call & fn)134 selection_getEndIndex(const fn_call& fn)
135 {
136     movie_root& mr = getRoot(fn);
137     DisplayObject* focus = mr.getFocus();
138 
139     TextField* tf = dynamic_cast<TextField*>(focus);
140 
141     if (!tf) return as_value(-1);
142 
143     return as_value(tf->getSelection().second);
144 }
145 
146 /// Returns null when there is no focus, otherwise the target of the
147 /// DisplayObject.
148 as_value
selection_getFocus(const fn_call & fn)149 selection_getFocus(const fn_call& fn)
150 {
151     movie_root& mr = getRoot(fn);
152 
153     DisplayObject* ch = mr.getFocus();
154     if (!ch) {
155         as_value null;
156         null.set_null();
157         return null;
158     }
159 
160     return as_value(ch->getTarget());
161 }
162 
163 
164 // Documented to return true when setFocus succeeds, but that seems like the
165 // usual Adobe crap.
166 //
167 // Returns true if focus is set to 0 (no focus), otherwise false. It is
168 // irrelevant whether focus was set.
169 //
170 // A MovieClip must have the focusEnabled property evaluate to true or at
171 // least one mouse event handler defined in order to receive focus.
172 //
173 // TextFields can only receive focus if selectable (TODO: check this).
174 // Buttons are documented to be able to receive focus always.
175 //
176 // focusEnabled has no effect in SWF5.
177 //
178 // Any number of arguments other than one returns false and does nothing. The
179 // single argument can be a DisplayObject or a full target path, otherwise it's
180 // a no-op and returns false.
181 as_value
selection_setFocus(const fn_call & fn)182 selection_setFocus(const fn_call& fn)
183 {
184 
185     /// Handle invalid arguments: must be one argument, or no action is
186     /// taken.
187     if (!fn.nargs || fn.nargs > 1) {
188         IF_VERBOSE_ASCODING_ERRORS(
189             log_aserror(_("Selection.setFocus: expected 1 argument, got %d"),
190                fn.nargs);
191         );
192         return as_value(false);
193     }
194 
195     movie_root& mr = getRoot(fn);
196 
197     const as_value& focus = fn.arg(0);
198 
199     /// These should remove focus.
200     if (focus.is_null() || focus.is_undefined()) {
201         mr.setFocus(nullptr);
202         return as_value(true);
203     }
204 
205     DisplayObject* ch;
206 
207     if (focus.is_string()) {
208         const std::string& target = focus.to_string();
209         ch = findTarget(fn.env(), target);
210     }
211     else {
212         /// Try converting directly to DisplayObject.
213         as_object* obj = toObject(focus, getVM(fn));
214         ch = get<DisplayObject>(obj);
215     }
216 
217     // If the argument does not resolve to a DisplayObject, do nothing.
218     if (!ch) return as_value(false);
219 
220     // HACK FIXME ! This is an hack to succeed an swfdec testcase
221     if (getSWFVersion(fn) >= 6) {
222         // Will handle whether to set focus or not.
223         mr.setFocus(ch);
224     }
225 
226     return as_value(false);
227 }
228 
229 
230 as_value
selection_setSelection(const fn_call & fn)231 selection_setSelection(const fn_call& fn)
232 {
233 
234     movie_root& mr = getRoot(fn);
235     DisplayObject* focus = mr.getFocus();
236 
237     TextField* tf = dynamic_cast<TextField*>(focus);
238 
239     if (!tf) return as_value();
240 
241     if (fn.nargs != 2) {
242         // Only two arguments are acceptable.
243         return as_value();
244     }
245 
246     int start = toInt(fn.arg(0), getVM(fn));
247     int end = toInt(fn.arg(1), getVM(fn));
248 
249     tf->setSelection(start, end);
250 
251     return as_value();
252 }
253 
254 } // anonymous namespace
255 } // end of gnash namespace
256