2009-04-28 Xan Lopez <xlopez@igalia.com>
[WebKit-https.git] / WebKit / gtk / WebCoreSupport / EditorClientGtk.cpp
1 /*
2  *  Copyright (C) 2007 Alp Toker <alp@atoker.com>
3  *  Copyright (C) 2008 Nuanti Ltd.
4  *  Copyright (C) 2009 Diego Escalante Urrelo <diegoe@gnome.org>
5  *  Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
6  *  Copyright (C) 2009, Igalia S.L.
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 #include "config.h"
24 #include "EditorClientGtk.h"
25
26 #include "CString.h"
27 #include "EditCommand.h"
28 #include "Editor.h"
29 #include <enchant.h>
30 #include "EventNames.h"
31 #include "FocusController.h"
32 #include "Frame.h"
33 #include <glib.h>
34 #include "KeyboardCodes.h"
35 #include "KeyboardEvent.h"
36 #include "NotImplemented.h"
37 #include "Page.h"
38 #include "PlatformKeyboardEvent.h"
39 #include "markup.h"
40 #include "webkitprivate.h"
41
42 using namespace WebCore;
43
44 namespace WebKit {
45
46 static void imContextCommitted(GtkIMContext* context, const gchar* str, EditorClient* client)
47 {
48     Frame* targetFrame = core(client->m_webView)->focusController()->focusedOrMainFrame();
49
50     if (!targetFrame || !targetFrame->editor()->canEdit())
51         return;
52
53     Editor* editor = targetFrame->editor();
54
55     String commitString = String::fromUTF8(str);
56     editor->confirmComposition(commitString);
57 }
58
59 static void imContextPreeditChanged(GtkIMContext* context, EditorClient* client)
60 {
61     Frame* frame = core(client->m_webView)->focusController()->focusedOrMainFrame();
62     Editor* editor = frame->editor();
63
64     gchar* preedit = NULL;
65     gint cursorPos = 0;
66     // We ignore the provided PangoAttrList for now.
67     gtk_im_context_get_preedit_string(context, &preedit, NULL, &cursorPos);
68     String preeditString = String::fromUTF8(preedit);
69     g_free(preedit);
70
71     // setComposition() will replace the user selection if passed an empty
72     // preedit. We don't want this to happen.
73     if (preeditString.isEmpty() && !editor->hasComposition())
74         return;
75
76     Vector<CompositionUnderline> underlines;
77     underlines.append(CompositionUnderline(0, preeditString.length(), Color(0, 0, 0), false));
78     editor->setComposition(preeditString, underlines, cursorPos, 0);
79 }
80
81 void EditorClient::setInputMethodState(bool active)
82 {
83     WebKitWebViewPrivate* priv = m_webView->priv;
84
85     if (active)
86         gtk_im_context_focus_in(priv->imContext);
87     else
88         gtk_im_context_focus_out(priv->imContext);
89
90 #ifdef MAEMO_CHANGES
91     if (active)
92         hildon_gtk_im_context_show(priv->imContext);
93     else
94         hildon_gtk_im_context_hide(priv->imContext);
95 #endif
96 }
97
98 bool EditorClient::shouldDeleteRange(Range*)
99 {
100     notImplemented();
101     return true;
102 }
103
104 bool EditorClient::shouldShowDeleteInterface(HTMLElement*)
105 {
106     return false;
107 }
108
109 bool EditorClient::isContinuousSpellCheckingEnabled()
110 {
111     WebKitWebSettings* settings = webkit_web_view_get_settings(m_webView);
112
113     gboolean enabled;
114     g_object_get(settings, "enable-spell-checking", &enabled, NULL);
115
116     return enabled;
117 }
118
119 bool EditorClient::isGrammarCheckingEnabled()
120 {
121     notImplemented();
122     return false;
123 }
124
125 int EditorClient::spellCheckerDocumentTag()
126 {
127     notImplemented();
128     return 0;
129 }
130
131 bool EditorClient::shouldBeginEditing(WebCore::Range*)
132 {
133     notImplemented();
134     return true;
135 }
136
137 bool EditorClient::shouldEndEditing(WebCore::Range*)
138 {
139     notImplemented();
140     return true;
141 }
142
143 bool EditorClient::shouldInsertText(const String&, Range*, EditorInsertAction)
144 {
145     notImplemented();
146     return true;
147 }
148
149 bool EditorClient::shouldChangeSelectedRange(Range*, Range*, EAffinity, bool)
150 {
151     notImplemented();
152     return true;
153 }
154
155 bool EditorClient::shouldApplyStyle(WebCore::CSSStyleDeclaration*, WebCore::Range*)
156 {
157     notImplemented();
158     return true;
159 }
160
161 bool EditorClient::shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*)
162 {
163     notImplemented();
164     return true;
165 }
166
167 void EditorClient::didBeginEditing()
168 {
169     notImplemented();
170 }
171
172 void EditorClient::respondToChangedContents()
173 {
174     notImplemented();
175 }
176
177 static void clipboard_get_contents_cb(GtkClipboard* clipboard, GtkSelectionData* selection_data, guint info, gpointer data)
178 {
179     WebKitWebView* webView = reinterpret_cast<WebKitWebView*>(data);
180     Frame* frame = core(webView)->focusController()->focusedOrMainFrame();
181     PassRefPtr<Range> selectedRange = frame->selection()->toNormalizedRange();
182
183     if (static_cast<gint>(info) == WEBKIT_WEB_VIEW_TARGET_INFO_HTML) {
184         String markup = createMarkup(selectedRange.get(), 0, AnnotateForInterchange);
185         gtk_selection_data_set(selection_data, selection_data->target, 8,
186                                reinterpret_cast<const guchar*>(markup.utf8().data()), markup.utf8().length());
187     } else {
188         String text = selectedRange->text();
189         gtk_selection_data_set_text(selection_data, text.utf8().data(), text.utf8().length());
190     }
191 }
192
193 static void clipboard_clear_contents_cb(GtkClipboard* clipboard, gpointer data)
194 {
195     WebKitWebView* webView = reinterpret_cast<WebKitWebView*>(data);
196     Frame* frame = core(webView)->focusController()->focusedOrMainFrame();
197
198     // Collapse the selection without clearing it
199     frame->selection()->setBase(frame->selection()->extent(), frame->selection()->affinity());
200 }
201
202 void EditorClient::respondToChangedSelection()
203 {
204     WebKitWebViewPrivate* priv = m_webView->priv;
205     Frame* targetFrame = core(m_webView)->focusController()->focusedOrMainFrame();
206
207     if (!targetFrame)
208         return;
209
210     if (targetFrame->editor()->ignoreCompositionSelectionChange())
211         return;
212
213     GtkClipboard* clipboard = gtk_widget_get_clipboard(GTK_WIDGET(m_webView), GDK_SELECTION_PRIMARY);
214     if (targetFrame->selection()->isRange()) {
215         GtkTargetList* targetList = webkit_web_view_get_copy_target_list(m_webView);
216         gint targetCount;
217         GtkTargetEntry* targets = gtk_target_table_new_from_list(targetList, &targetCount);
218         gtk_clipboard_set_with_owner(clipboard, targets, targetCount,
219                                      clipboard_get_contents_cb, clipboard_clear_contents_cb, G_OBJECT(m_webView));
220         gtk_target_table_free(targets, targetCount);
221     } else if (gtk_clipboard_get_owner(clipboard) == G_OBJECT(m_webView))
222         gtk_clipboard_clear(clipboard);
223
224     if (!targetFrame->editor()->hasComposition())
225         return;
226
227     unsigned start;
228     unsigned end;
229     if (!targetFrame->editor()->getCompositionSelection(start, end)) {
230         // gtk_im_context_reset() clears the composition for us.
231         gtk_im_context_reset(priv->imContext);
232         targetFrame->editor()->confirmCompositionWithoutDisturbingSelection();
233     }
234 }
235
236 void EditorClient::didEndEditing()
237 {
238     notImplemented();
239 }
240
241 void EditorClient::didWriteSelectionToPasteboard()
242 {
243     notImplemented();
244 }
245
246 void EditorClient::didSetSelectionTypesForPasteboard()
247 {
248     notImplemented();
249 }
250
251 bool EditorClient::isEditable()
252 {
253     return webkit_web_view_get_editable(m_webView);
254 }
255
256 void EditorClient::registerCommandForUndo(WTF::PassRefPtr<WebCore::EditCommand>)
257 {
258     notImplemented();
259 }
260
261 void EditorClient::registerCommandForRedo(WTF::PassRefPtr<WebCore::EditCommand>)
262 {
263     notImplemented();
264 }
265
266 void EditorClient::clearUndoRedoOperations()
267 {
268     notImplemented();
269 }
270
271 bool EditorClient::canUndo() const
272 {
273     notImplemented();
274     return false;
275 }
276
277 bool EditorClient::canRedo() const
278 {
279     notImplemented();
280     return false;
281 }
282
283 void EditorClient::undo()
284 {
285     notImplemented();
286 }
287
288 void EditorClient::redo()
289 {
290     notImplemented();
291 }
292
293 bool EditorClient::shouldInsertNode(Node*, Range*, EditorInsertAction)
294 {
295     notImplemented();
296     return true;
297 }
298
299 void EditorClient::pageDestroyed()
300 {
301     delete this;
302 }
303
304 bool EditorClient::smartInsertDeleteEnabled()
305 {
306     notImplemented();
307     return false;
308 }
309
310 bool EditorClient::isSelectTrailingWhitespaceEnabled()
311 {
312     notImplemented();
313     return false;
314 }
315
316 void EditorClient::toggleContinuousSpellChecking()
317 {
318     WebKitWebSettings* settings = webkit_web_view_get_settings(m_webView);
319
320     gboolean enabled;
321     g_object_get(settings, "enable-spell-checking", &enabled, NULL);
322
323     g_object_set(settings, "enable-spell-checking", !enabled, NULL);
324 }
325
326 void EditorClient::toggleGrammarChecking()
327 {
328 }
329
330 static const unsigned CtrlKey = 1 << 0;
331 static const unsigned AltKey = 1 << 1;
332 static const unsigned ShiftKey = 1 << 2;
333
334 struct KeyDownEntry {
335     unsigned virtualKey;
336     unsigned modifiers;
337     const char* name;
338 };
339
340 struct KeyPressEntry {
341     unsigned charCode;
342     unsigned modifiers;
343     const char* name;
344 };
345
346 static const KeyDownEntry keyDownEntries[] = {
347     { VK_LEFT,   0,                  "MoveLeft"                                    },
348     { VK_LEFT,   ShiftKey,           "MoveLeftAndModifySelection"                  },
349     { VK_LEFT,   CtrlKey,            "MoveWordLeft"                                },
350     { VK_LEFT,   CtrlKey | ShiftKey, "MoveWordLeftAndModifySelection"              },
351     { VK_RIGHT,  0,                  "MoveRight"                                   },
352     { VK_RIGHT,  ShiftKey,           "MoveRightAndModifySelection"                 },
353     { VK_RIGHT,  CtrlKey,            "MoveWordRight"                               },
354     { VK_RIGHT,  CtrlKey | ShiftKey, "MoveWordRightAndModifySelection"             },
355     { VK_UP,     0,                  "MoveUp"                                      },
356     { VK_UP,     ShiftKey,           "MoveUpAndModifySelection"                    },
357     { VK_PRIOR,  ShiftKey,           "MovePageUpAndModifySelection"                },
358     { VK_DOWN,   0,                  "MoveDown"                                    },
359     { VK_DOWN,   ShiftKey,           "MoveDownAndModifySelection"                  },
360     { VK_NEXT,   ShiftKey,           "MovePageDownAndModifySelection"              },
361     { VK_PRIOR,  0,                  "MovePageUp"                                  },
362     { VK_NEXT,   0,                  "MovePageDown"                                },
363     { VK_HOME,   0,                  "MoveToBeginningOfLine"                       },
364     { VK_HOME,   ShiftKey,           "MoveToBeginningOfLineAndModifySelection"     },
365     { VK_HOME,   CtrlKey,            "MoveToBeginningOfDocument"                   },
366     { VK_HOME,   CtrlKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" },
367
368     { VK_END,    0,                  "MoveToEndOfLine"                             },
369     { VK_END,    ShiftKey,           "MoveToEndOfLineAndModifySelection"           },
370     { VK_END,    CtrlKey,            "MoveToEndOfDocument"                         },
371     { VK_END,    CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection"       },
372
373     { VK_BACK,   0,                  "DeleteBackward"                              },
374     { VK_BACK,   ShiftKey,           "DeleteBackward"                              },
375     { VK_DELETE, 0,                  "DeleteForward"                               },
376     { VK_BACK,   CtrlKey,            "DeleteWordBackward"                          },
377     { VK_DELETE, CtrlKey,            "DeleteWordForward"                           },
378
379     { 'B',       CtrlKey,            "ToggleBold"                                  },
380     { 'I',       CtrlKey,            "ToggleItalic"                                },
381
382     { VK_ESCAPE, 0,                  "Cancel"                                      },
383     { VK_OEM_PERIOD, CtrlKey,        "Cancel"                                      },
384     { VK_TAB,    0,                  "InsertTab"                                   },
385     { VK_TAB,    ShiftKey,           "InsertBacktab"                               },
386     { VK_RETURN, 0,                  "InsertNewline"                               },
387     { VK_RETURN, CtrlKey,            "InsertNewline"                               },
388     { VK_RETURN, AltKey,             "InsertNewline"                               },
389     { VK_RETURN, AltKey | ShiftKey,  "InsertNewline"                               },
390
391     // It's not quite clear whether Undo/Redo should be handled
392     // in the application or in WebKit. We chose WebKit.
393     { 'Z',       CtrlKey,            "Undo"                                        },
394     { 'Z',       CtrlKey | ShiftKey, "Redo"                                        },
395 };
396
397 static const KeyPressEntry keyPressEntries[] = {
398     { '\t',   0,                  "InsertTab"                                   },
399     { '\t',   ShiftKey,           "InsertBacktab"                               },
400     { '\r',   0,                  "InsertNewline"                               },
401     { '\r',   CtrlKey,            "InsertNewline"                               },
402     { '\r',   AltKey,             "InsertNewline"                               },
403     { '\r',   AltKey | ShiftKey,  "InsertNewline"                               },
404 };
405
406 static const char* interpretKeyEvent(const KeyboardEvent* evt)
407 {
408     ASSERT(evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);
409
410     static HashMap<int, const char*>* keyDownCommandsMap = 0;
411     static HashMap<int, const char*>* keyPressCommandsMap = 0;
412
413     if (!keyDownCommandsMap) {
414         keyDownCommandsMap = new HashMap<int, const char*>;
415         keyPressCommandsMap = new HashMap<int, const char*>;
416
417         for (unsigned i = 0; i < G_N_ELEMENTS(keyDownEntries); i++)
418             keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
419
420         for (unsigned i = 0; i < G_N_ELEMENTS(keyPressEntries); i++)
421             keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
422     }
423
424     unsigned modifiers = 0;
425     if (evt->shiftKey())
426         modifiers |= ShiftKey;
427     if (evt->altKey())
428         modifiers |= AltKey;
429     if (evt->ctrlKey())
430         modifiers |= CtrlKey;
431
432     if (evt->type() == eventNames().keydownEvent) {
433         int mapKey = modifiers << 16 | evt->keyCode();
434         return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
435     }
436
437     int mapKey = modifiers << 16 | evt->charCode();
438     return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
439 }
440
441 static bool handleEditingKeyboardEvent(KeyboardEvent* evt)
442 {
443     Node* node = evt->target()->toNode();
444     ASSERT(node);
445     Frame* frame = node->document()->frame();
446     ASSERT(frame);
447
448     const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
449     if (!keyEvent)
450         return false;
451
452     bool caretBrowsing = frame->settings()->caretBrowsingEnabled();
453     if (caretBrowsing) {
454         switch (keyEvent->windowsVirtualKeyCode()) {
455             case VK_LEFT:
456                 frame->selection()->modify(keyEvent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
457                         SelectionController::LEFT,
458                         keyEvent->ctrlKey() ? WordGranularity : CharacterGranularity,
459                         true);
460                 return true;
461             case VK_RIGHT:
462                 frame->selection()->modify(keyEvent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
463                         SelectionController::RIGHT,
464                         keyEvent->ctrlKey() ? WordGranularity : CharacterGranularity,
465                         true);
466                 return true;
467             case VK_UP:
468                 frame->selection()->modify(keyEvent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
469                         SelectionController::BACKWARD,
470                         keyEvent->ctrlKey() ? ParagraphGranularity : LineGranularity,
471                         true);
472                 return true;
473             case VK_DOWN:
474                 frame->selection()->modify(keyEvent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
475                         SelectionController::FORWARD,
476                         keyEvent->ctrlKey() ? ParagraphGranularity : LineGranularity,
477                         true);
478                 return true;
479         }
480     }
481
482     Editor::Command command = frame->editor()->command(interpretKeyEvent(evt));
483
484     if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) {
485         // WebKit doesn't have enough information about mode to decide how commands that just insert text if executed via Editor should be treated,
486         // so we leave it upon WebCore to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
487         // (e.g. Tab that inserts a Tab character, or Enter).
488         return !command.isTextInsertion() && command.execute(evt);
489     }
490
491     if (command.execute(evt))
492         return true;
493
494     // Don't insert null or control characters as they can result in unexpected behaviour
495     if (evt->charCode() < ' ')
496         return false;
497
498     return frame->editor()->insertText(evt->keyEvent()->text(), evt);
499 }
500
501 void EditorClient::handleKeyboardEvent(KeyboardEvent* event)
502 {
503     if (handleEditingKeyboardEvent(event))
504         event->setDefaultHandled();
505 }
506
507 void EditorClient::handleInputMethodKeydown(KeyboardEvent* event)
508 {
509     Frame* targetFrame = core(m_webView)->focusController()->focusedOrMainFrame();
510     if (!targetFrame || !targetFrame->editor()->canEdit())
511         return;
512
513     WebKitWebViewPrivate* priv = m_webView->priv;
514     // TODO: Dispatch IE-compatible text input events for IM events.
515     if (gtk_im_context_filter_keypress(priv->imContext, event->keyEvent()->gdkEventKey()))
516         event->setDefaultHandled();
517 }
518
519 EditorClient::EditorClient(WebKitWebView* webView)
520     : m_webView(webView)
521 {
522     WebKitWebViewPrivate* priv = m_webView->priv;
523     g_signal_connect(priv->imContext, "commit", G_CALLBACK(imContextCommitted), this);
524     g_signal_connect(priv->imContext, "preedit-changed", G_CALLBACK(imContextPreeditChanged), this);
525 }
526
527 EditorClient::~EditorClient()
528 {
529     WebKitWebViewPrivate* priv = m_webView->priv;
530     g_signal_handlers_disconnect_by_func(priv->imContext, (gpointer)imContextCommitted, this);
531     g_signal_handlers_disconnect_by_func(priv->imContext, (gpointer)imContextPreeditChanged, this);
532 }
533
534 void EditorClient::textFieldDidBeginEditing(Element*)
535 {
536 }
537
538 void EditorClient::textFieldDidEndEditing(Element*)
539 {
540 }
541
542 void EditorClient::textDidChangeInTextField(Element*)
543 {
544 }
545
546 bool EditorClient::doTextFieldCommandFromEvent(Element*, KeyboardEvent*)
547 {
548     return false;
549 }
550
551 void EditorClient::textWillBeDeletedInTextField(Element*)
552 {
553     notImplemented();
554 }
555
556 void EditorClient::textDidChangeInTextArea(Element*)
557 {
558     notImplemented();
559 }
560
561 void EditorClient::ignoreWordInSpellDocument(const String& text)
562 {
563     GSList* langs = webkit_web_settings_get_spell_languages(m_webView);
564
565     for (; langs; langs = langs->next) {
566         SpellLanguage* lang = static_cast<SpellLanguage*>(langs->data);
567
568         enchant_dict_add_to_session(lang->speller, text.utf8().data(), -1);
569     }
570 }
571
572 void EditorClient::learnWord(const String& text)
573 {
574     GSList* langs = webkit_web_settings_get_spell_languages(m_webView);
575
576     for (; langs; langs = langs->next) {
577         SpellLanguage* lang = static_cast<SpellLanguage*>(langs->data);
578
579         enchant_dict_add_to_personal(lang->speller, text.utf8().data(), -1);
580     }
581 }
582
583 void EditorClient::checkSpellingOfString(const UChar* text, int length, int* misspellingLocation, int* misspellingLength)
584 {
585     gchar* ctext = g_utf16_to_utf8(const_cast<gunichar2*>(text), length, 0, 0, 0);
586     int utflen = g_utf8_strlen(ctext, -1);
587
588     PangoLanguage* language = pango_language_get_default();
589     PangoLogAttr* attrs = g_new(PangoLogAttr, utflen+1);
590
591     // pango_get_log_attrs uses an aditional position at the end of the text.
592     pango_get_log_attrs(ctext, -1, -1, language, attrs, utflen+1);
593
594     for (int i = 0; i < length+1; i++) {
595         // We go through each character until we find an is_word_start,
596         // then we get into an inner loop to find the is_word_end corresponding
597         // to it.
598         if (attrs[i].is_word_start) {
599             int start = i;
600             int end = i;
601             int wordLength;
602             GSList* langs = webkit_web_settings_get_spell_languages(m_webView);
603
604             while (attrs[end].is_word_end < 1)
605                 end++;
606
607             wordLength = end - start;
608             // Set the iterator to be at the current word end, so we don't
609             // check characters twice.
610             i = end;
611
612             for (; langs; langs = langs->next) {
613                 SpellLanguage* lang = static_cast<SpellLanguage*>(langs->data);
614                 gchar* cstart = g_utf8_offset_to_pointer(ctext, start);
615                 gint bytes = static_cast<gint>(g_utf8_offset_to_pointer(ctext, end) - cstart);
616                 gchar* word = g_new0(gchar, bytes+1);
617                 int result;
618
619                 g_utf8_strncpy(word, cstart, end - start);
620
621                 result = enchant_dict_check(lang->speller, word, -1);
622                 if (result) {
623                     *misspellingLocation = start;
624                     *misspellingLength = wordLength;
625                 } else {
626                     // Stop checking, this word is ok in at least one dict.
627                     *misspellingLocation = -1;
628                     *misspellingLength = 0;
629                     break;
630                 }
631             }
632         }
633     }
634 }
635
636 void EditorClient::checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*)
637 {
638     notImplemented();
639 }
640
641 void EditorClient::updateSpellingUIWithGrammarString(const String&, const GrammarDetail&)
642 {
643     notImplemented();
644 }
645
646 void EditorClient::updateSpellingUIWithMisspelledWord(const String&)
647 {
648     notImplemented();
649 }
650
651 void EditorClient::showSpellingUI(bool)
652 {
653     notImplemented();
654 }
655
656 bool EditorClient::spellingUIIsShowing()
657 {
658     notImplemented();
659     return false;
660 }
661
662 void EditorClient::getGuessesForWord(const String& word, WTF::Vector<String>& guesses)
663 {
664     GSList* langs = webkit_web_settings_get_spell_languages(m_webView);
665     guesses.clear();
666
667     for (; langs; langs = langs->next) {
668         size_t numberOfSuggestions;
669         size_t i;
670
671         SpellLanguage* lang = static_cast<SpellLanguage*>(langs->data);
672         gchar** suggestions = enchant_dict_suggest(lang->speller, word.utf8().data(), -1, &numberOfSuggestions);
673
674         for (i = 0; i < numberOfSuggestions && i < 10; i++)
675             guesses.append(String::fromUTF8(suggestions[i]));
676
677         if (numberOfSuggestions > 0)
678             enchant_dict_free_suggestions(lang->speller, suggestions);
679     }
680 }
681
682 }