hhctrl.ocx: Don't insert content item that could not be merged.
[wine] / dlls / mshtml / editor.c
1 /*
2  * Copyright 2006 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "winnls.h"
30 #include "ole2.h"
31
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34
35 #include "mshtml_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
38
39 #define DOM_VK_LEFT  VK_LEFT
40 #define DOM_VK_UP    VK_UP
41 #define DOM_VK_RIGHT VK_RIGHT
42 #define DOM_VK_DOWN  VK_DOWN
43
44 static const WCHAR wszFont[] = {'f','o','n','t',0};
45 static const WCHAR wszSize[] = {'s','i','z','e',0};
46
47 static nsISelection *get_ns_selection(HTMLDocument *This)
48 {
49     nsIDOMWindow *dom_window;
50     nsISelection *nsselection = NULL;
51     nsresult nsres;
52
53     if(!This->nscontainer)
54         return NULL;
55
56     nsres = nsIWebBrowser_GetContentDOMWindow(This->nscontainer->webbrowser, &dom_window);
57     if(NS_FAILED(nsres))
58         return NULL;
59
60     nsIDOMWindow_GetSelection(dom_window, &nsselection);
61     nsIDOMWindow_Release(dom_window);
62
63     return nsselection;
64
65 }
66
67 static void remove_child_attr(nsIDOMElement *elem, LPCWSTR tag, nsAString *attr_str)
68 {
69     PRBool has_children;
70     PRUint32 child_cnt, i;
71     nsIDOMNode *child_node;
72     nsIDOMNodeList *node_list;
73     PRUint16 node_type;
74
75     nsIDOMElement_HasChildNodes(elem, &has_children);
76     if(!has_children)
77         return;
78
79     nsIDOMElement_GetChildNodes(elem, &node_list);
80     nsIDOMNodeList_GetLength(node_list, &child_cnt);
81
82     for(i=0; i<child_cnt; i++) {
83         nsIDOMNodeList_Item(node_list, i, &child_node);
84
85         nsIDOMNode_GetNodeType(child_node, &node_type);
86         if(node_type == ELEMENT_NODE) {
87             nsIDOMElement *child_elem;
88             nsAString tag_str;
89             const PRUnichar *ctag;
90
91             nsIDOMNode_QueryInterface(child_node, &IID_nsIDOMElement, (void**)&child_elem);
92
93             nsAString_Init(&tag_str, NULL);
94             nsIDOMElement_GetTagName(child_elem, &tag_str);
95             nsAString_GetData(&tag_str, &ctag, NULL);
96
97             if(!strcmpiW(ctag, tag))
98                 /* FIXME: remove node if there are no more attributes */
99                 nsIDOMElement_RemoveAttribute(child_elem, attr_str);
100
101             nsAString_Finish(&tag_str);
102
103             remove_child_attr(child_elem, tag, attr_str);
104
105             nsIDOMNode_Release(child_elem);
106         }
107
108         nsIDOMNode_Release(child_node);
109     }
110
111     nsIDOMNodeList_Release(node_list);
112 }
113
114 void get_font_size(HTMLDocument *This, WCHAR *ret)
115 {
116     nsISelection *nsselection = get_ns_selection(This);
117     nsIDOMElement *elem = NULL;
118     nsIDOMNode *node = NULL, *tmp_node;
119     nsAString tag_str;
120     LPCWSTR tag;
121     PRUint16 node_type;
122     nsresult nsres;
123
124     *ret = 0;
125
126     if(!nsselection)
127         return;
128
129     nsISelection_GetFocusNode(nsselection, &node);
130     nsISelection_Release(nsselection);
131
132     while(node) {
133         nsres = nsIDOMNode_GetNodeType(node, &node_type);
134         if(NS_FAILED(nsres) || node_type == DOCUMENT_NODE)
135             break;
136
137         if(node_type == ELEMENT_NODE) {
138             nsIDOMNode_QueryInterface(node, &IID_nsIDOMElement, (void**)&elem);
139
140             nsAString_Init(&tag_str, NULL);
141             nsIDOMElement_GetTagName(elem, &tag_str);
142             nsAString_GetData(&tag_str, &tag, NULL);
143
144             if(!strcmpiW(tag, wszFont)) {
145                 nsAString size_str, val_str;
146                 LPCWSTR val;
147
148                 TRACE("found font tag %p\n", elem);
149
150                 nsAString_Init(&size_str, wszSize);
151                 nsAString_Init(&val_str, NULL);
152
153                 nsIDOMElement_GetAttribute(elem, &size_str, &val_str);
154                 nsAString_GetData(&val_str, &val, NULL);
155
156                 if(*val) {
157                     TRACE("found size %s\n", debugstr_w(val));
158                     strcpyW(ret, val);
159                 }
160
161                 nsAString_Finish(&size_str);
162                 nsAString_Finish(&val_str);
163             }
164
165             nsAString_Finish(&tag_str);
166
167             nsIDOMElement_Release(elem);
168         }
169
170         if(*ret)
171             break;
172
173         tmp_node = node;
174         nsIDOMNode_GetParentNode(node, &node);
175         nsIDOMNode_Release(tmp_node);
176     }
177
178     if(node)
179         nsIDOMNode_Release(node);
180 }
181
182 void set_font_size(HTMLDocument *This, LPCWSTR size)
183 {
184     nsISelection *nsselection;
185     PRBool collapsed;
186     nsIDOMDocument *nsdoc;
187     nsIDOMElement *elem;
188     nsIDOMRange *range;
189     PRInt32 range_cnt = 0;
190     nsAString font_str;
191     nsAString size_str;
192     nsAString val_str;
193     nsresult nsres;
194
195     nsselection = get_ns_selection(This);
196
197     if(!nsselection)
198         return;
199
200     nsres = nsIWebNavigation_GetDocument(This->nscontainer->navigation, &nsdoc);
201     if(NS_FAILED(nsres))
202         return;
203
204     nsAString_Init(&font_str, wszFont);
205     nsAString_Init(&size_str, wszSize);
206     nsAString_Init(&val_str, size);
207
208     nsISelection_GetRangeCount(nsselection, &range_cnt);
209     if(range_cnt != 1)
210         FIXME("range_cnt %d not supprted\n", range_cnt);
211
212     nsIDOMDocument_CreateElement(nsdoc, &font_str, &elem);
213     nsIDOMElement_SetAttribute(elem, &size_str, &val_str);        
214
215     nsISelection_GetRangeAt(nsselection, 0, &range);
216     nsISelection_GetIsCollapsed(nsselection, &collapsed);
217     nsISelection_RemoveAllRanges(nsselection);
218
219     nsIDOMRange_SurroundContents(range, (nsIDOMNode*)elem);
220
221     if(collapsed) {
222         nsISelection_Collapse(nsselection, (nsIDOMNode*)elem, 0);
223     }else {
224         /* Remove all size attrbutes from the range */
225         remove_child_attr(elem, wszFont, &size_str);
226         nsISelection_SelectAllChildren(nsselection, (nsIDOMNode*)elem);
227     }
228
229     nsIDOMRange_Release(range);
230     nsIDOMElement_Release(elem);
231
232     nsAString_Finish(&font_str);
233     nsAString_Finish(&size_str);
234     nsAString_Finish(&val_str);
235
236     nsISelection_Release(nsselection);
237     nsIDOMDocument_Release(nsdoc);
238 }
239
240 static BOOL is_visible_text_node(nsIDOMNode *node)
241 {
242     nsIDOMCharacterData *char_data;
243     nsAString data_str;
244     LPCWSTR data, ptr;
245     PRUint32 len;
246
247     nsIDOMNode_QueryInterface(node, &IID_nsIDOMCharacterData, (void**)&char_data);
248
249     nsIDOMCharacterData_GetLength(char_data, &len);
250
251     nsAString_Init(&data_str, NULL);
252     nsIDOMCharacterData_GetData(char_data, &data_str);
253     nsAString_GetData(&data_str, &data, NULL);
254
255     if(*data == '\n') {
256         len--;
257         for(ptr=data+1; ptr && isspaceW(*ptr); ptr++)
258             len--;
259     }
260
261     nsAString_Finish(&data_str);
262
263     nsIDOMCharacterData_Release(char_data);
264
265     return len != 0;
266 }
267
268 static nsIDOMNode *get_child_text_node(nsIDOMNode *node, BOOL first)
269 {
270     nsIDOMNode *iter, *iter2;
271
272     if(first)
273         nsIDOMNode_GetFirstChild(node, &iter);
274     else
275         nsIDOMNode_GetLastChild(node, &iter);
276
277     while(iter) {
278         PRUint16 node_type;
279
280         nsIDOMNode_GetNodeType(iter, &node_type);
281         switch(node_type) {
282         case TEXT_NODE:
283             if(is_visible_text_node(iter))
284                 return iter;
285         case ELEMENT_NODE:
286             iter2 = get_child_text_node(iter, first);
287             if(iter2) {
288                 nsIDOMNode_Release(iter);
289                 return iter2;
290             }
291         }
292
293         if(first)
294             nsIDOMNode_GetNextSibling(iter, &iter2);
295         else
296             nsIDOMNode_GetPreviousSibling(iter, &iter2);
297
298         nsIDOMNode_Release(iter);
299         iter = iter2;
300     }
301
302     return NULL;
303 }
304
305 static nsIDOMNode *get_next_text_node(nsIDOMNode *node, BOOL next)
306 {
307     nsIDOMNode *iter, *iter2 = NULL, *parent = NULL;
308     PRUint16 node_type;
309
310     iter = node;
311     nsIDOMNode_AddRef(iter);
312
313     while(1) {
314         if(next)
315             nsIDOMNode_GetNextSibling(iter, &iter2);
316         else
317             nsIDOMNode_GetPreviousSibling(iter, &iter2);
318
319         while(!iter2) {
320             nsIDOMNode_GetParentNode(iter, &parent);
321             nsIDOMNode_Release(iter);
322             if(!parent)
323                 return NULL;
324
325             iter = parent;
326
327             if(next)
328                 nsIDOMNode_GetNextSibling(iter, &iter2);
329             else
330                 nsIDOMNode_GetPreviousSibling(iter, &iter2);
331         }
332
333         nsIDOMNode_Release(iter);
334         iter = iter2;
335
336         nsIDOMNode_GetNodeType(iter, &node_type);
337
338         switch(node_type) {
339         case TEXT_NODE:
340             if(is_visible_text_node(iter))
341                 return iter;
342         case ELEMENT_NODE:
343             iter2 = get_child_text_node(iter, next);
344             if(iter2) {
345                 nsIDOMNode_Release(iter);
346                 return iter2;
347             }
348         }
349     }
350
351     return NULL;
352 }
353
354 static void collapse_end_node(nsISelection *selection, nsIDOMNode *node)
355 {
356     nsIDOMCharacterData *char_data;
357     PRUint32 len;
358
359     nsIDOMNode_QueryInterface(node, &IID_nsIDOMCharacterData, (void**)&char_data);
360     nsIDOMCharacterData_GetLength(char_data, &len);
361     nsIDOMCharacterData_Release(char_data);
362
363     nsISelection_Collapse(selection, node, len);
364 }
365
366 static void collapse_next_char(HTMLDocument *doc, nsIDOMKeyEvent *event, BOOL next)
367 {
368     nsISelection *selection = get_ns_selection(doc);
369     nsIDOMNode *node;
370     PRBool collapsed, b;
371     PRUint16 node_type;
372     nsIDOMNode *text_node;
373
374     nsIDOMKeyEvent_GetCtrlKey(event, &b);
375     if(b) return;
376
377     nsIDOMKeyEvent_GetShiftKey(event, &b);
378     if(b) return;
379
380     nsISelection_GetIsCollapsed(selection, &collapsed);
381     if(!collapsed)
382         nsISelection_CollapseToEnd(selection);
383
384     nsISelection_GetFocusNode(selection, &node);
385     nsIDOMNode_GetNodeType(node, &node_type);
386
387     if(node_type == TEXT_NODE) {
388         nsIDOMCharacterData *char_data;
389         PRInt32 offset;
390         PRUint32 len;
391
392         nsISelection_GetFocusOffset(selection, &offset);
393
394         nsIDOMNode_QueryInterface(node, &IID_nsIDOMCharacterData, (void**)&char_data);
395         nsIDOMCharacterData_GetLength(char_data, &len);
396         nsIDOMCharacterData_Release(char_data);
397
398         if(next ? offset != len : offset) {
399             nsISelection_Collapse(selection, node, offset + (next?1:-1));
400             return;
401         }
402     }
403
404     text_node = get_next_text_node(node, next);
405     if(text_node) {
406         if(next)
407             nsISelection_Collapse(selection, text_node, 1);
408         else
409             collapse_end_node(selection, text_node);
410         nsIDOMNode_Release(text_node);
411     }
412
413     nsIDOMNode_Release(node);
414     nsISelection_Release(selection);
415 }
416
417 void handle_edit_event(HTMLDocument *This, nsIDOMEvent *event)
418 {
419     nsIDOMKeyEvent *key_event;
420     PRUint32 code;
421
422     nsIDOMEvent_QueryInterface(event, &IID_nsIDOMKeyEvent, (void**)&key_event);
423
424     nsIDOMKeyEvent_GetKeyCode(key_event, &code);
425
426     switch(code) {
427     case DOM_VK_LEFT:
428         TRACE("left\n");
429         collapse_next_char(This, key_event, FALSE);
430         break;
431     case DOM_VK_RIGHT:
432         TRACE("right\n");
433         collapse_next_char(This, key_event, TRUE);
434     };
435
436     nsIDOMKeyEvent_Release(key_event);
437 }