1 /* 2 * Copyright (C) 2006 Michael Emmel mike.emmel (at) gmail.com 3 * Copyright (C) 2007 Holger Hans Peter Freyther 4 * Copyright (C) 2008 Kenneth Rohde Christiansen 5 * Copyright (C) 2009-2010 ProFUSION embedded systems 6 * Copyright (C) 2009-2010 Samsung Electronics 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 26 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "Widget.h" 33 34 #include "ChromeClient.h" 35 #include "Cursor.h" 36 #include "Frame.h" 37 #include "FrameView.h" 38 #include "GraphicsContext.h" 39 #include "IntRect.h" 40 #include "NotImplemented.h" 41 #include "Page.h" 42 43 #include <Ecore.h> 44 #include <Ecore_Evas.h> 45 #include <Edje.h> 46 #include <Evas.h> 47 48 #ifdef HAVE_ECORE_X 49 #include <Ecore_X.h> 50 #include <Ecore_X_Cursor.h> 51 #endif 52 53 #include <wtf/HashMap.h> 54 #include <wtf/text/CString.h> 55 56 namespace WebCore { 57 58 #ifdef HAVE_ECORE_X 59 class CursorMap { 60 private: 61 HashMap<String, unsigned short> m_cursorStringMap; 62 63 public: 64 CursorMap(); 65 unsigned int cursor(String); 66 }; 67 68 unsigned int CursorMap::cursor(String cursorGroup) 69 { 70 int ret = m_cursorStringMap.get(cursorGroup); 71 72 if (ret < ECORE_X_CURSOR_X || ret > ECORE_X_CURSOR_XTERM) 73 ret = ECORE_X_CURSOR_LEFT_PTR; 74 75 return ret; 76 } 77 78 CursorMap::CursorMap() 79 { 80 m_cursorStringMap.set("cursor/pointer", ECORE_X_CURSOR_LEFT_PTR); 81 m_cursorStringMap.set("cursor/move", ECORE_X_CURSOR_FLEUR); 82 m_cursorStringMap.set("cursor/cross", ECORE_X_CURSOR_CROSS); 83 m_cursorStringMap.set("cursor/hand", ECORE_X_CURSOR_HAND2); 84 m_cursorStringMap.set("cursor/i_beam", ECORE_X_CURSOR_XTERM); 85 m_cursorStringMap.set("cursor/wait", ECORE_X_CURSOR_WATCH); 86 m_cursorStringMap.set("cursor/help", ECORE_X_CURSOR_QUESTION_ARROW); 87 m_cursorStringMap.set("cursor/east_resize", ECORE_X_CURSOR_RIGHT_SIDE); 88 m_cursorStringMap.set("cursor/north_resize", ECORE_X_CURSOR_TOP_SIDE); 89 m_cursorStringMap.set("cursor/north_east_resize", ECORE_X_CURSOR_TOP_RIGHT_CORNER); 90 m_cursorStringMap.set("cursor/north_west_resize", ECORE_X_CURSOR_TOP_LEFT_CORNER); 91 m_cursorStringMap.set("cursor/south_resize", ECORE_X_CURSOR_BOTTOM_SIDE); 92 m_cursorStringMap.set("cursor/south_east_resize", ECORE_X_CURSOR_BOTTOM_RIGHT_CORNER); 93 m_cursorStringMap.set("cursor/south_west_resize", ECORE_X_CURSOR_BOTTOM_LEFT_CORNER); 94 m_cursorStringMap.set("cursor/west_resize", ECORE_X_CURSOR_LEFT_SIDE); 95 m_cursorStringMap.set("cursor/north_south_resize", ECORE_X_CURSOR_SB_H_DOUBLE_ARROW); 96 m_cursorStringMap.set("cursor/east_west_resize", ECORE_X_CURSOR_SB_V_DOUBLE_ARROW); 97 m_cursorStringMap.set("cursor/north_east_south_west_resize", ECORE_X_CURSOR_SIZING); 98 m_cursorStringMap.set("cursor/north_west_south_east_resize", ECORE_X_CURSOR_SIZING); 99 m_cursorStringMap.set("cursor/column_resize", ECORE_X_CURSOR_SB_V_DOUBLE_ARROW); 100 m_cursorStringMap.set("cursor/row_resize", ECORE_X_CURSOR_SB_H_DOUBLE_ARROW); 101 m_cursorStringMap.set("cursor/middle_panning", ECORE_X_CURSOR_CROSS_REVERSE); 102 m_cursorStringMap.set("cursor/east_panning", ECORE_X_CURSOR_CROSS_REVERSE); 103 m_cursorStringMap.set("cursor/north_panning", ECORE_X_CURSOR_CROSS_REVERSE); 104 m_cursorStringMap.set("cursor/north_east_panning", ECORE_X_CURSOR_CROSS_REVERSE); 105 m_cursorStringMap.set("cursor/north_west_panning", ECORE_X_CURSOR_CROSS_REVERSE); 106 m_cursorStringMap.set("cursor/south_panning", ECORE_X_CURSOR_CROSS_REVERSE); 107 m_cursorStringMap.set("cursor/south_east_panning", ECORE_X_CURSOR_CROSS_REVERSE); 108 m_cursorStringMap.set("cursor/south_west_panning", ECORE_X_CURSOR_CROSS_REVERSE); 109 m_cursorStringMap.set("cursor/west_panning", ECORE_X_CURSOR_CROSS_REVERSE); 110 m_cursorStringMap.set("cursor/vertical_text", ECORE_X_CURSOR_SB_DOWN_ARROW); 111 m_cursorStringMap.set("cursor/cell", ECORE_X_CURSOR_ICON); 112 m_cursorStringMap.set("cursor/context_menu", ECORE_X_CURSOR_HAND2); 113 m_cursorStringMap.set("cursor/no_drop", ECORE_X_CURSOR_DOT_BOX_MASK); 114 m_cursorStringMap.set("cursor/copy", ECORE_X_CURSOR_ICON); 115 m_cursorStringMap.set("cursor/progress", ECORE_X_CURSOR_WATCH); 116 m_cursorStringMap.set("cursor/alias", ECORE_X_CURSOR_MAN); 117 m_cursorStringMap.set("cursor/none", ECORE_X_CURSOR_X); 118 m_cursorStringMap.set("cursor/not_allowed", ECORE_X_CURSOR_X); 119 m_cursorStringMap.set("cursor/zoom_in", ECORE_X_CURSOR_DIAMOND_CROSS); 120 m_cursorStringMap.set("cursor/zoom_out", ECORE_X_CURSOR_DIAMOND_CROSS); 121 m_cursorStringMap.set("cursor/grab", ECORE_X_CURSOR_HAND2); 122 m_cursorStringMap.set("cursor/grabbing", ECORE_X_CURSOR_HAND2); 123 } 124 125 static CursorMap cursorStringMap = CursorMap(); 126 #endif 127 128 class WidgetPrivate { 129 public: 130 Evas* m_evas; 131 Evas_Object* m_evasObject; 132 String m_theme; 133 134 WidgetPrivate() 135 : m_evas(0) 136 , m_evasObject(0) 137 , m_cursorObject(0) 138 #ifdef HAVE_ECORE_X 139 , m_isUsingEcoreX(false) 140 #endif 141 {} 142 143 /* cursor */ 144 String m_cursorGroup; 145 Evas_Object* m_cursorObject; 146 147 #ifdef HAVE_ECORE_X 148 bool m_isUsingEcoreX; 149 #endif 150 }; 151 152 Widget::Widget(PlatformWidget widget) 153 : m_parent(0) 154 , m_widget(0) 155 , m_selfVisible(false) 156 , m_parentVisible(false) 157 , m_frame(0, 0, 0, 0) 158 , m_data(new WidgetPrivate) 159 { 160 init(widget); 161 } 162 163 Widget::~Widget() 164 { 165 ASSERT(!parent()); 166 167 if (m_data->m_cursorObject) 168 evas_object_del(m_data->m_cursorObject); 169 170 delete m_data; 171 } 172 173 IntRect Widget::frameRect() const 174 { 175 return m_frame; 176 } 177 178 void Widget::setFrameRect(const IntRect& rect) 179 { 180 m_frame = rect; 181 Widget::frameRectsChanged(); 182 } 183 184 void Widget::frameRectsChanged() 185 { 186 Evas_Object* object = evasObject(); 187 Evas_Coord x, y; 188 189 if (!parent() || !object) 190 return; 191 192 IntRect rect = frameRect(); 193 if (parent()->isScrollViewScrollbar(this)) 194 rect.setLocation(parent()->convertToContainingWindow(rect.location())); 195 else 196 rect.setLocation(parent()->contentsToWindow(rect.location())); 197 198 evas_object_geometry_get(root()->evasObject(), &x, &y, 0, 0); 199 evas_object_move(object, x + rect.x(), y + rect.y()); 200 evas_object_resize(object, rect.width(), rect.height()); 201 } 202 203 void Widget::setFocus(bool focused) 204 { 205 } 206 207 void Widget::applyFallbackCursor() 208 { 209 #ifdef HAVE_ECORE_X 210 if (m_data->m_isUsingEcoreX && !m_data->m_cursorGroup.isNull()) { 211 int shape = cursorStringMap.cursor(m_data->m_cursorGroup.utf8().data()); 212 213 if (shape < ECORE_X_CURSOR_X || shape > ECORE_X_CURSOR_XTERM) { 214 LOG_ERROR("cannot map an equivalent X cursor for" 215 " c ursor group %s", m_data->m_cursorGroup.utf8().data()); 216 shape = ECORE_X_CURSOR_LEFT_PTR; 217 } 218 219 Ecore_X_Window win = ecore_evas_software_x11_window_get(ecoreEvas()); 220 Ecore_X_Cursor cur = ecore_x_cursor_shape_get(shape); 221 ecore_x_window_cursor_set(win, cur); 222 return; 223 } 224 #endif 225 LOG_ERROR("Ooops, no fallback to set cursor %s!\n", 226 m_data->m_cursorGroup.utf8().data()); 227 } 228 229 void Widget::applyCursor() 230 { 231 CString file = edjeThemeRecursive().utf8(); 232 233 m_data->m_cursorObject = edje_object_add(evas()); 234 if (!file.isNull() && !edje_object_file_set(m_data->m_cursorObject, file.data(), m_data->m_cursorGroup.utf8().data())) { 235 evas_object_del(m_data->m_cursorObject); 236 m_data->m_cursorObject = 0; 237 ecore_evas_object_cursor_set(ecoreEvas(), 0, 0, 0, 0); 238 applyFallbackCursor(); 239 } else { 240 Evas_Coord x, y, w, h; 241 const char *d; 242 243 edje_object_size_min_get(m_data->m_cursorObject, &w, &h); 244 if ((w <= 0) || (h <= 0)) 245 edje_object_size_min_calc(m_data->m_cursorObject, &w, &h); 246 if ((w <= 0) || (h <= 0)) 247 w = h = 16; 248 evas_object_resize(m_data->m_cursorObject, w, h); 249 250 d = edje_object_data_get(m_data->m_cursorObject, "hot.x"); 251 x = d ? atoi(d) : 0; 252 253 d = edje_object_data_get(m_data->m_cursorObject, "hot.y"); 254 y = d ? atoi(d) : 0; 255 256 ecore_evas_object_cursor_set(ecoreEvas(), m_data->m_cursorObject, 257 EVAS_LAYER_MAX, x, y); 258 } 259 } 260 261 void Widget::setCursor(const Cursor& cursor) 262 { 263 if (!evas()) 264 return; 265 266 const char *group = cursor.impl(); 267 if (!group || String(group) == m_data->m_cursorGroup) 268 return; 269 270 m_data->m_cursorGroup = group; 271 272 applyCursor(); 273 } 274 275 void Widget::show() 276 { 277 if (!platformWidget()) 278 return; 279 280 evas_object_show(platformWidget()); 281 } 282 283 void Widget::hide() 284 { 285 if (!platformWidget()) 286 return; 287 288 evas_object_hide(platformWidget()); 289 } 290 291 void Widget::paint(GraphicsContext* context, const IntRect&) 292 { 293 notImplemented(); 294 } 295 296 void Widget::setIsSelected(bool) 297 { 298 notImplemented(); 299 } 300 301 const String Widget::edjeTheme() const 302 { 303 return m_data->m_theme; 304 } 305 306 void Widget::setEdjeTheme(const String& themePath) 307 { 308 if (m_data->m_theme == themePath) 309 return; 310 311 m_data->m_theme = themePath; 312 } 313 314 const String Widget::edjeThemeRecursive() const 315 { 316 if (!m_data->m_theme.isNull()) 317 return m_data->m_theme; 318 if (m_parent) 319 return m_parent->edjeThemeRecursive(); 320 321 return String(); 322 } 323 324 Evas* Widget::evas() const 325 { 326 return m_data->m_evas; 327 } 328 329 Ecore_Evas* Widget::ecoreEvas() const 330 { 331 // FIXME EFL: XXX assume evas was created by ecore_evas 332 return static_cast<Ecore_Evas*>(evas_data_attach_get(evas())); 333 } 334 335 void Widget::setEvasObject(Evas_Object *object) 336 { 337 // FIXME: study platformWidget() and use it 338 // FIXME: right now platformWidget() requires implementing too much 339 if (m_data->m_evasObject == object) 340 return; 341 m_data->m_evasObject = object; 342 if (!object) { 343 m_data->m_evas = 0; 344 #ifdef HAVE_ECORE_X 345 m_data->m_isUsingEcoreX = false; 346 #endif 347 return; 348 } 349 350 m_data->m_evas = evas_object_evas_get(object); 351 352 #ifdef HAVE_ECORE_X 353 const char *engine = ecore_evas_engine_name_get(ecoreEvas()); 354 m_data->m_isUsingEcoreX = (!strcmp(engine, "software_x11") 355 || !strcmp(engine, "software_xcb") 356 || !strcmp(engine, "software_16_x11") 357 || !strncmp(engine, "xrender", sizeof("xrender") - 1)); 358 #endif 359 360 Widget::frameRectsChanged(); 361 } 362 363 Evas_Object* Widget::evasObject() const 364 { 365 return m_data->m_evasObject; 366 } 367 368 } 369