[NTOSKRNL] Drop the useless Timestamp field
[reactos.git] / dll / win32 / mshtml / txtrange.c
1 /*
2 * Copyright 2006-2007 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 "mshtml_private.h"
20
21 static const WCHAR brW[] = {'b','r',0};
22 static const WCHAR hrW[] = {'h','r',0};
23
24 typedef struct {
25 DispatchEx dispex;
26 IHTMLTxtRange IHTMLTxtRange_iface;
27 IOleCommandTarget IOleCommandTarget_iface;
28
29 LONG ref;
30
31 nsIDOMRange *nsrange;
32 HTMLDocumentNode *doc;
33
34 struct list entry;
35 } HTMLTxtRange;
36
37 typedef struct {
38 WCHAR *buf;
39 DWORD len;
40 DWORD size;
41 } wstrbuf_t;
42
43 typedef struct {
44 UINT16 type;
45 nsIDOMNode *node;
46 UINT32 off;
47 } rangepoint_t;
48
49 typedef enum {
50 RU_UNKNOWN,
51 RU_CHAR,
52 RU_WORD,
53 RU_SENTENCE,
54 RU_TEXTEDIT
55 } range_unit_t;
56
57 static HTMLTxtRange *get_range_object(HTMLDocumentNode *doc, IHTMLTxtRange *iface)
58 {
59 HTMLTxtRange *iter;
60
61 LIST_FOR_EACH_ENTRY(iter, &doc->range_list, HTMLTxtRange, entry) {
62 if(&iter->IHTMLTxtRange_iface == iface)
63 return iter;
64 }
65
66 ERR("Could not find range in document\n");
67 return NULL;
68 }
69
70 static range_unit_t string_to_unit(LPCWSTR str)
71 {
72 static const WCHAR characterW[] =
73 {'c','h','a','r','a','c','t','e','r',0};
74 static const WCHAR wordW[] =
75 {'w','o','r','d',0};
76 static const WCHAR sentenceW[] =
77 {'s','e','n','t','e','n','c','e',0};
78 static const WCHAR texteditW[] =
79 {'t','e','x','t','e','d','i','t',0};
80
81 if(!strcmpiW(str, characterW)) return RU_CHAR;
82 if(!strcmpiW(str, wordW)) return RU_WORD;
83 if(!strcmpiW(str, sentenceW)) return RU_SENTENCE;
84 if(!strcmpiW(str, texteditW)) return RU_TEXTEDIT;
85
86 return RU_UNKNOWN;
87 }
88
89 static int string_to_nscmptype(LPCWSTR str)
90 {
91 static const WCHAR seW[] = {'S','t','a','r','t','T','o','E','n','d',0};
92 static const WCHAR ssW[] = {'S','t','a','r','t','T','o','S','t','a','r','t',0};
93 static const WCHAR esW[] = {'E','n','d','T','o','S','t','a','r','t',0};
94 static const WCHAR eeW[] = {'E','n','d','T','o','E','n','d',0};
95
96 if(!strcmpiW(str, seW)) return NS_START_TO_END;
97 if(!strcmpiW(str, ssW)) return NS_START_TO_START;
98 if(!strcmpiW(str, esW)) return NS_END_TO_START;
99 if(!strcmpiW(str, eeW)) return NS_END_TO_END;
100
101 return -1;
102 }
103
104 static UINT16 get_node_type(nsIDOMNode *node)
105 {
106 UINT16 type = 0;
107
108 if(node)
109 nsIDOMNode_GetNodeType(node, &type);
110
111 return type;
112 }
113
114 static void get_text_node_data(nsIDOMNode *node, nsAString *nsstr, const PRUnichar **str)
115 {
116 nsIDOMText *nstext;
117 nsresult nsres;
118
119 nsres = nsIDOMNode_QueryInterface(node, &IID_nsIDOMText, (void**)&nstext);
120 assert(nsres == NS_OK);
121
122 nsAString_Init(nsstr, NULL);
123 nsres = nsIDOMText_GetData(nstext, nsstr);
124 nsIDOMText_Release(nstext);
125 if(NS_FAILED(nsres))
126 ERR("GetData failed: %08x\n", nsres);
127
128 nsAString_GetData(nsstr, str);
129 }
130
131 static nsIDOMNode *get_child_node(nsIDOMNode *node, UINT32 off)
132 {
133 nsIDOMNodeList *node_list;
134 nsIDOMNode *ret = NULL;
135
136 nsIDOMNode_GetChildNodes(node, &node_list);
137 nsIDOMNodeList_Item(node_list, off, &ret);
138 nsIDOMNodeList_Release(node_list);
139
140 return ret;
141 }
142
143 /* This is very inefficient, but there is no faster way to compute index in
144 * child node list using public API. Gecko has internal nsINode::IndexOf
145 * function that we could consider exporting and use instead. */
146 static int get_child_index(nsIDOMNode *parent, nsIDOMNode *child)
147 {
148 nsIDOMNodeList *node_list;
149 nsIDOMNode *node;
150 int ret = 0;
151 nsresult nsres;
152
153 nsres = nsIDOMNode_GetChildNodes(parent, &node_list);
154 assert(nsres == NS_OK);
155
156 while(1) {
157 nsres = nsIDOMNodeList_Item(node_list, ret, &node);
158 assert(nsres == NS_OK && node);
159 if(node == child) {
160 nsIDOMNode_Release(node);
161 break;
162 }
163 nsIDOMNode_Release(node);
164 ret++;
165 }
166
167 nsIDOMNodeList_Release(node_list);
168 return ret;
169 }
170
171 static void init_rangepoint(rangepoint_t *rangepoint, nsIDOMNode *node, UINT32 off)
172 {
173 nsIDOMNode_AddRef(node);
174
175 rangepoint->type = get_node_type(node);
176 rangepoint->node = node;
177 rangepoint->off = off;
178 }
179
180 static inline void free_rangepoint(rangepoint_t *rangepoint)
181 {
182 nsIDOMNode_Release(rangepoint->node);
183 }
184
185 static inline BOOL rangepoint_cmp(const rangepoint_t *point1, const rangepoint_t *point2)
186 {
187 return point1->node == point2->node && point1->off == point2->off;
188 }
189
190 static BOOL rangepoint_next_node(rangepoint_t *iter)
191 {
192 nsIDOMNode *node;
193 UINT32 off;
194 nsresult nsres;
195
196 /* Try to move to the child node. */
197 node = get_child_node(iter->node, iter->off);
198 if(node) {
199 free_rangepoint(iter);
200 init_rangepoint(iter, node, 0);
201 nsIDOMNode_Release(node);
202 return TRUE;
203 }
204
205 /* There are no more children in the node. Move to parent. */
206 nsres = nsIDOMNode_GetParentNode(iter->node, &node);
207 assert(nsres == NS_OK);
208 if(!node)
209 return FALSE;
210
211 off = get_child_index(node, iter->node)+1;
212 free_rangepoint(iter);
213 init_rangepoint(iter, node, off);
214 nsIDOMNode_Release(node);
215 return TRUE;
216 }
217
218 static UINT32 get_child_count(nsIDOMNode *node)
219 {
220 nsIDOMNodeList *node_list;
221 UINT32 ret;
222 nsresult nsres;
223
224 nsres = nsIDOMNode_GetChildNodes(node, &node_list);
225 assert(nsres == NS_OK);
226
227 nsres = nsIDOMNodeList_GetLength(node_list, &ret);
228 nsIDOMNodeList_Release(node_list);
229 assert(nsres == NS_OK);
230
231 return ret;
232 }
233
234 static UINT32 get_text_length(nsIDOMNode *node)
235 {
236 nsIDOMText *nstext;
237 UINT32 ret;
238 nsresult nsres;
239
240 nsres = nsIDOMNode_QueryInterface(node, &IID_nsIDOMText, (void**)&nstext);
241 assert(nsres == NS_OK);
242
243 nsres = nsIDOMText_GetLength(nstext, &ret);
244 nsIDOMText_Release(nstext);
245 assert(nsres == NS_OK);
246
247 return ret;
248 }
249
250 static BOOL rangepoint_prev_node(rangepoint_t *iter)
251 {
252 nsIDOMNode *node;
253 UINT32 off;
254 nsresult nsres;
255
256 /* Try to move to the child node. */
257 if(iter->off) {
258 node = get_child_node(iter->node, iter->off-1);
259 assert(node != NULL);
260
261 off = get_node_type(node) == TEXT_NODE ? get_text_length(node) : get_child_count(node);
262 free_rangepoint(iter);
263 init_rangepoint(iter, node, off);
264 nsIDOMNode_Release(node);
265 return TRUE;
266 }
267
268 /* There are no more children in the node. Move to parent. */
269 nsres = nsIDOMNode_GetParentNode(iter->node, &node);
270 assert(nsres == NS_OK);
271 if(!node)
272 return FALSE;
273
274 off = get_child_index(node, iter->node);
275 free_rangepoint(iter);
276 init_rangepoint(iter, node, off);
277 return TRUE;
278 }
279
280 static void get_start_point(HTMLTxtRange *This, rangepoint_t *ret)
281 {
282 nsIDOMNode *node;
283 LONG off;
284
285 nsIDOMRange_GetStartContainer(This->nsrange, &node);
286 nsIDOMRange_GetStartOffset(This->nsrange, &off);
287
288 init_rangepoint(ret, node, off);
289
290 nsIDOMNode_Release(node);
291 }
292
293 static void get_end_point(HTMLTxtRange *This, rangepoint_t *ret)
294 {
295 nsIDOMNode *node;
296 LONG off;
297
298 nsIDOMRange_GetEndContainer(This->nsrange, &node);
299 nsIDOMRange_GetEndOffset(This->nsrange, &off);
300
301 init_rangepoint(ret, node, off);
302
303 nsIDOMNode_Release(node);
304 }
305
306 static void set_start_point(HTMLTxtRange *This, const rangepoint_t *start)
307 {
308 nsresult nsres = nsIDOMRange_SetStart(This->nsrange, start->node, start->off);
309 if(NS_FAILED(nsres))
310 ERR("failed: %08x\n", nsres);
311 }
312
313 static void set_end_point(HTMLTxtRange *This, const rangepoint_t *end)
314 {
315 nsresult nsres = nsIDOMRange_SetEnd(This->nsrange, end->node, end->off);
316 if(NS_FAILED(nsres))
317 ERR("failed: %08x\n", nsres);
318 }
319
320 static BOOL is_elem_tag(nsIDOMNode *node, LPCWSTR istag)
321 {
322 nsIDOMElement *elem;
323 nsAString tag_str;
324 const PRUnichar *tag;
325 BOOL ret = FALSE;
326 nsresult nsres;
327
328 nsres = nsIDOMNode_QueryInterface(node, &IID_nsIDOMElement, (void**)&elem);
329 if(NS_FAILED(nsres))
330 return FALSE;
331
332 nsAString_Init(&tag_str, NULL);
333 nsIDOMElement_GetTagName(elem, &tag_str);
334 nsIDOMElement_Release(elem);
335 nsAString_GetData(&tag_str, &tag);
336
337 ret = !strcmpiW(tag, istag);
338
339 nsAString_Finish(&tag_str);
340
341 return ret;
342 }
343
344 static inline BOOL wstrbuf_init(wstrbuf_t *buf)
345 {
346 buf->len = 0;
347 buf->size = 16;
348 buf->buf = heap_alloc(buf->size * sizeof(WCHAR));
349 if (!buf->buf) return FALSE;
350 *buf->buf = 0;
351 return TRUE;
352 }
353
354 static inline void wstrbuf_finish(wstrbuf_t *buf)
355 {
356 heap_free(buf->buf);
357 }
358
359 static void wstrbuf_append_len(wstrbuf_t *buf, LPCWSTR str, int len)
360 {
361 if(buf->len+len >= buf->size) {
362 buf->size = 2*buf->size+len;
363 buf->buf = heap_realloc(buf->buf, buf->size * sizeof(WCHAR));
364 }
365
366 memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
367 buf->len += len;
368 buf->buf[buf->len] = 0;
369 }
370
371 static void wstrbuf_append_nodetxt(wstrbuf_t *buf, LPCWSTR str, int len)
372 {
373 const WCHAR *s = str;
374 WCHAR *d;
375
376 TRACE("%s\n", debugstr_wn(str, len));
377
378 if(buf->len+len >= buf->size) {
379 buf->size = 2*buf->size+len;
380 buf->buf = heap_realloc(buf->buf, buf->size * sizeof(WCHAR));
381 }
382
383 if(buf->len && isspaceW(buf->buf[buf->len-1])) {
384 while(s < str+len && isspaceW(*s))
385 s++;
386 }
387
388 d = buf->buf+buf->len;
389 while(s < str+len) {
390 if(isspaceW(*s)) {
391 *d++ = ' ';
392 s++;
393 while(s < str+len && isspaceW(*s))
394 s++;
395 }else {
396 *d++ = *s++;
397 }
398 }
399
400 buf->len = d - buf->buf;
401 *d = 0;
402 }
403
404 static void wstrbuf_append_node(wstrbuf_t *buf, nsIDOMNode *node, BOOL ignore_text)
405 {
406
407 switch(get_node_type(node)) {
408 case TEXT_NODE: {
409 nsIDOMText *nstext;
410 nsAString data_str;
411 const PRUnichar *data;
412
413 if(ignore_text)
414 break;
415
416 nsIDOMNode_QueryInterface(node, &IID_nsIDOMText, (void**)&nstext);
417
418 nsAString_Init(&data_str, NULL);
419 nsIDOMText_GetData(nstext, &data_str);
420 nsAString_GetData(&data_str, &data);
421 wstrbuf_append_nodetxt(buf, data, strlenW(data));
422 nsAString_Finish(&data_str);
423
424 nsIDOMText_Release(nstext);
425
426 break;
427 }
428 case ELEMENT_NODE:
429 if(is_elem_tag(node, brW)) {
430 static const WCHAR endlW[] = {'\r','\n'};
431 wstrbuf_append_len(buf, endlW, 2);
432 }else if(is_elem_tag(node, hrW)) {
433 static const WCHAR endl2W[] = {'\r','\n','\r','\n'};
434 wstrbuf_append_len(buf, endl2W, 4);
435 }
436 }
437 }
438
439 static void wstrbuf_append_node_rec(wstrbuf_t *buf, nsIDOMNode *node)
440 {
441 nsIDOMNode *iter, *tmp;
442
443 wstrbuf_append_node(buf, node, FALSE);
444
445 nsIDOMNode_GetFirstChild(node, &iter);
446 while(iter) {
447 wstrbuf_append_node_rec(buf, iter);
448 nsIDOMNode_GetNextSibling(iter, &tmp);
449 nsIDOMNode_Release(iter);
450 iter = tmp;
451 }
452 }
453
454 static void range_to_string(HTMLTxtRange *This, wstrbuf_t *buf)
455 {
456 rangepoint_t end_pos, iter;
457 cpp_bool collapsed;
458
459 nsIDOMRange_GetCollapsed(This->nsrange, &collapsed);
460 if(collapsed) {
461 wstrbuf_finish(buf);
462 buf->buf = NULL;
463 buf->size = 0;
464 return;
465 }
466
467 get_end_point(This, &end_pos);
468 get_start_point(This, &iter);
469
470 do {
471 if(iter.type == TEXT_NODE) {
472 const PRUnichar *str;
473 nsAString nsstr;
474
475 get_text_node_data(iter.node, &nsstr, &str);
476
477 if(iter.node == end_pos.node) {
478 wstrbuf_append_nodetxt(buf, str+iter.off, end_pos.off-iter.off);
479 nsAString_Finish(&nsstr);
480 break;
481 }
482
483 wstrbuf_append_nodetxt(buf, str+iter.off, strlenW(str+iter.off));
484 nsAString_Finish(&nsstr);
485 }else {
486 nsIDOMNode *node;
487
488 node = get_child_node(iter.node, iter.off);
489 if(node) {
490 wstrbuf_append_node(buf, node, TRUE);
491 nsIDOMNode_Release(node);
492 }
493 }
494
495 if(!rangepoint_next_node(&iter)) {
496 ERR("End of document?\n");
497 break;
498 }
499 }while(!rangepoint_cmp(&iter, &end_pos));
500
501 free_rangepoint(&iter);
502 free_rangepoint(&end_pos);
503
504 if(buf->len) {
505 WCHAR *p;
506
507 for(p = buf->buf+buf->len-1; p >= buf->buf && isspaceW(*p); p--);
508
509 p = strchrW(p, '\r');
510 if(p)
511 *p = 0;
512 }
513 }
514
515 HRESULT get_node_text(HTMLDOMNode *node, BSTR *ret)
516 {
517 wstrbuf_t buf;
518 HRESULT hres = S_OK;
519
520 if (!wstrbuf_init(&buf))
521 return E_OUTOFMEMORY;
522 wstrbuf_append_node_rec(&buf, node->nsnode);
523 if(buf.buf) {
524 *ret = SysAllocString(buf.buf);
525 if(!*ret)
526 hres = E_OUTOFMEMORY;
527 } else {
528 *ret = NULL;
529 }
530 wstrbuf_finish(&buf);
531
532 if(SUCCEEDED(hres))
533 TRACE("ret %s\n", debugstr_w(*ret));
534 return hres;
535 }
536
537 static WCHAR move_next_char(rangepoint_t *iter)
538 {
539 rangepoint_t last_space;
540 nsIDOMNode *node;
541 WCHAR cspace = 0;
542 const WCHAR *p;
543
544 do {
545 switch(iter->type) {
546 case TEXT_NODE: {
547 const PRUnichar *str;
548 nsAString nsstr;
549 WCHAR c;
550
551 get_text_node_data(iter->node, &nsstr, &str);
552 p = str+iter->off;
553 if(!*p) {
554 nsAString_Finish(&nsstr);
555 break;
556 }
557
558 c = *p;
559 if(isspaceW(c)) {
560 while(isspaceW(*p))
561 p++;
562
563 if(cspace)
564 free_rangepoint(&last_space);
565 else
566 cspace = ' ';
567
568 iter->off = p-str;
569 c = *p;
570 nsAString_Finish(&nsstr);
571 if(!c) { /* continue to skip spaces */
572 init_rangepoint(&last_space, iter->node, iter->off);
573 break;
574 }
575
576 return cspace;
577 }else {
578 nsAString_Finish(&nsstr);
579 }
580
581 /* If we have a non-space char and we're skipping spaces, stop and return the last found space. */
582 if(cspace) {
583 free_rangepoint(iter);
584 *iter = last_space;
585 return cspace;
586 }
587
588 iter->off++;
589 return c;
590 }
591 case ELEMENT_NODE:
592 node = get_child_node(iter->node, iter->off);
593 if(!node)
594 break;
595
596 if(is_elem_tag(node, brW)) {
597 if(cspace) {
598 nsIDOMNode_Release(node);
599 free_rangepoint(iter);
600 *iter = last_space;
601 return cspace;
602 }
603
604 cspace = '\n';
605 init_rangepoint(&last_space, iter->node, iter->off+1);
606 }else if(is_elem_tag(node, hrW)) {
607 nsIDOMNode_Release(node);
608 if(cspace) {
609 free_rangepoint(iter);
610 *iter = last_space;
611 return cspace;
612 }
613
614 iter->off++;
615 return '\n';
616 }
617
618 nsIDOMNode_Release(node);
619 }
620 }while(rangepoint_next_node(iter));
621
622 return cspace;
623 }
624
625 static WCHAR move_prev_char(rangepoint_t *iter)
626 {
627 rangepoint_t last_space;
628 nsIDOMNode *node;
629 WCHAR cspace = 0;
630 const WCHAR *p;
631
632 do {
633 switch(iter->type) {
634 case TEXT_NODE: {
635 const PRUnichar *str;
636 nsAString nsstr;
637 WCHAR c;
638
639 if(!iter->off)
640 break;
641
642 get_text_node_data(iter->node, &nsstr, &str);
643
644 p = str+iter->off-1;
645 c = *p;
646
647 if(isspaceW(c)) {
648 while(p > str && isspaceW(*(p-1)))
649 p--;
650
651 if(cspace)
652 free_rangepoint(&last_space);
653 else
654 cspace = ' ';
655
656 iter->off = p-str;
657 nsAString_Finish(&nsstr);
658 if(p == str) { /* continue to skip spaces */
659 init_rangepoint(&last_space, iter->node, iter->off);
660 break;
661 }
662
663 return cspace;
664 }else {
665 nsAString_Finish(&nsstr);
666 }
667
668 /* If we have a non-space char and we're skipping spaces, stop and return the last found space. */
669 if(cspace) {
670 free_rangepoint(iter);
671 *iter = last_space;
672 return cspace;
673 }
674
675 iter->off--;
676 return c;
677 }
678 case ELEMENT_NODE:
679 if(!iter->off)
680 break;
681
682 node = get_child_node(iter->node, iter->off-1);
683 if(!node)
684 break;
685
686 if(is_elem_tag(node, brW)) {
687 if(cspace)
688 free_rangepoint(&last_space);
689 cspace = '\n';
690 init_rangepoint(&last_space, iter->node, iter->off-1);
691 }else if(is_elem_tag(node, hrW)) {
692 nsIDOMNode_Release(node);
693 if(cspace) {
694 free_rangepoint(iter);
695 *iter = last_space;
696 return cspace;
697 }
698
699 iter->off--;
700 return '\n';
701 }
702
703 nsIDOMNode_Release(node);
704 }
705 }while(rangepoint_prev_node(iter));
706
707 if(cspace) {
708 free_rangepoint(iter);
709 *iter = last_space;
710 return cspace;
711 }
712
713 return 0;
714 }
715
716 static LONG move_by_chars(rangepoint_t *iter, LONG cnt)
717 {
718 LONG ret = 0;
719
720 if(cnt >= 0) {
721 while(ret < cnt && move_next_char(iter))
722 ret++;
723 }else {
724 while(ret > cnt && move_prev_char(iter))
725 ret--;
726 }
727
728 return ret;
729 }
730
731 static LONG find_prev_space(rangepoint_t *iter, BOOL first_space)
732 {
733 rangepoint_t prev;
734 WCHAR c;
735
736 init_rangepoint(&prev, iter->node, iter->off);
737 c = move_prev_char(&prev);
738 if(!c || (first_space && isspaceW(c)))
739 return FALSE;
740
741 do {
742 free_rangepoint(iter);
743 init_rangepoint(iter, prev.node, prev.off);
744 c = move_prev_char(&prev);
745 }while(c && !isspaceW(c));
746
747 free_rangepoint(&prev);
748 return TRUE;
749 }
750
751 static BOOL find_word_end(rangepoint_t *iter, BOOL is_collapsed)
752 {
753 rangepoint_t prev_iter;
754 WCHAR c;
755 BOOL ret = FALSE;
756
757 if(!is_collapsed) {
758 init_rangepoint(&prev_iter, iter->node, iter->off);
759 c = move_prev_char(&prev_iter);
760 free_rangepoint(&prev_iter);
761 if(isspaceW(c))
762 return FALSE;
763 }
764
765 do {
766 init_rangepoint(&prev_iter, iter->node, iter->off);
767 c = move_next_char(iter);
768 if(c == '\n') {
769 free_rangepoint(iter);
770 *iter = prev_iter;
771 return ret;
772 }
773 if(!c) {
774 if(!ret)
775 ret = !rangepoint_cmp(iter, &prev_iter);
776 }else {
777 ret = TRUE;
778 }
779 free_rangepoint(&prev_iter);
780 }while(c && !isspaceW(c));
781
782 return ret;
783 }
784
785 static LONG move_by_words(rangepoint_t *iter, LONG cnt)
786 {
787 LONG ret = 0;
788
789 if(cnt >= 0) {
790 WCHAR c;
791
792 while(ret < cnt && (c = move_next_char(iter))) {
793 if(isspaceW(c))
794 ret++;
795 }
796 }else {
797 while(ret > cnt && find_prev_space(iter, FALSE))
798 ret--;
799 }
800
801 return ret;
802 }
803
804 static inline HTMLTxtRange *impl_from_IHTMLTxtRange(IHTMLTxtRange *iface)
805 {
806 return CONTAINING_RECORD(iface, HTMLTxtRange, IHTMLTxtRange_iface);
807 }
808
809 static HRESULT WINAPI HTMLTxtRange_QueryInterface(IHTMLTxtRange *iface, REFIID riid, void **ppv)
810 {
811 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
812
813 TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
814
815 if(IsEqualGUID(&IID_IUnknown, riid)) {
816 *ppv = &This->IHTMLTxtRange_iface;
817 }else if(IsEqualGUID(&IID_IHTMLTxtRange, riid)) {
818 *ppv = &This->IHTMLTxtRange_iface;
819 }else if(IsEqualGUID(&IID_IOleCommandTarget, riid)) {
820 *ppv = &This->IOleCommandTarget_iface;
821 }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
822 return *ppv ? S_OK : E_NOINTERFACE;
823 }else {
824 *ppv = NULL;
825 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
826 return E_NOINTERFACE;
827 }
828
829 IUnknown_AddRef((IUnknown*)*ppv);
830 return S_OK;
831 }
832
833 static ULONG WINAPI HTMLTxtRange_AddRef(IHTMLTxtRange *iface)
834 {
835 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
836 LONG ref = InterlockedIncrement(&This->ref);
837
838 TRACE("(%p) ref=%d\n", This, ref);
839
840 return ref;
841 }
842
843 static ULONG WINAPI HTMLTxtRange_Release(IHTMLTxtRange *iface)
844 {
845 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
846 LONG ref = InterlockedDecrement(&This->ref);
847
848 TRACE("(%p) ref=%d\n", This, ref);
849
850 if(!ref) {
851 if(This->nsrange)
852 nsIDOMRange_Release(This->nsrange);
853 if(This->doc)
854 list_remove(&This->entry);
855 release_dispex(&This->dispex);
856 heap_free(This);
857 }
858
859 return ref;
860 }
861
862 static HRESULT WINAPI HTMLTxtRange_GetTypeInfoCount(IHTMLTxtRange *iface, UINT *pctinfo)
863 {
864 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
865
866 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
867 }
868
869 static HRESULT WINAPI HTMLTxtRange_GetTypeInfo(IHTMLTxtRange *iface, UINT iTInfo,
870 LCID lcid, ITypeInfo **ppTInfo)
871 {
872 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
873
874 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
875 }
876
877 static HRESULT WINAPI HTMLTxtRange_GetIDsOfNames(IHTMLTxtRange *iface, REFIID riid,
878 LPOLESTR *rgszNames, UINT cNames,
879 LCID lcid, DISPID *rgDispId)
880 {
881 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
882
883 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames,
884 cNames, lcid, rgDispId);
885 }
886
887 static HRESULT WINAPI HTMLTxtRange_Invoke(IHTMLTxtRange *iface, DISPID dispIdMember,
888 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
889 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
890 {
891 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
892
893 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid,
894 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
895 }
896
897 static HRESULT WINAPI HTMLTxtRange_get_htmlText(IHTMLTxtRange *iface, BSTR *p)
898 {
899 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
900
901 TRACE("(%p)->(%p)\n", This, p);
902
903 *p = NULL;
904
905 if(This->nsrange) {
906 nsIDOMDocumentFragment *fragment;
907 nsresult nsres;
908
909 nsres = nsIDOMRange_CloneContents(This->nsrange, &fragment);
910 if(NS_SUCCEEDED(nsres)) {
911 const PRUnichar *nstext;
912 nsAString nsstr;
913
914 nsAString_Init(&nsstr, NULL);
915 nsnode_to_nsstring((nsIDOMNode*)fragment, &nsstr);
916 nsIDOMDocumentFragment_Release(fragment);
917
918 nsAString_GetData(&nsstr, &nstext);
919 *p = SysAllocString(nstext);
920
921 nsAString_Finish(&nsstr);
922 }
923 }
924
925 if(!*p) {
926 const WCHAR emptyW[] = {0};
927 *p = SysAllocString(emptyW);
928 }
929
930 TRACE("return %s\n", debugstr_w(*p));
931 return S_OK;
932 }
933
934 static HRESULT WINAPI HTMLTxtRange_put_text(IHTMLTxtRange *iface, BSTR v)
935 {
936 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
937 nsIDOMText *text_node;
938 nsAString text_str;
939 nsresult nsres;
940
941 TRACE("(%p)->(%s)\n", This, debugstr_w(v));
942
943 if(!This->doc)
944 return MSHTML_E_NODOC;
945
946 nsAString_InitDepend(&text_str, v);
947 nsres = nsIDOMHTMLDocument_CreateTextNode(This->doc->nsdoc, &text_str, &text_node);
948 nsAString_Finish(&text_str);
949 if(NS_FAILED(nsres)) {
950 ERR("CreateTextNode failed: %08x\n", nsres);
951 return S_OK;
952 }
953 nsres = nsIDOMRange_DeleteContents(This->nsrange);
954 if(NS_FAILED(nsres))
955 ERR("DeleteContents failed: %08x\n", nsres);
956
957 nsres = nsIDOMRange_InsertNode(This->nsrange, (nsIDOMNode*)text_node);
958 if(NS_FAILED(nsres))
959 ERR("InsertNode failed: %08x\n", nsres);
960
961 nsres = nsIDOMRange_SetEndAfter(This->nsrange, (nsIDOMNode*)text_node);
962 if(NS_FAILED(nsres))
963 ERR("SetEndAfter failed: %08x\n", nsres);
964
965 return IHTMLTxtRange_collapse(&This->IHTMLTxtRange_iface, VARIANT_FALSE);
966 }
967
968 static HRESULT WINAPI HTMLTxtRange_get_text(IHTMLTxtRange *iface, BSTR *p)
969 {
970 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
971 wstrbuf_t buf;
972
973 TRACE("(%p)->(%p)\n", This, p);
974
975 *p = NULL;
976 if(!This->nsrange)
977 return S_OK;
978
979 if (!wstrbuf_init(&buf))
980 return E_OUTOFMEMORY;
981 range_to_string(This, &buf);
982 if (buf.buf)
983 *p = SysAllocString(buf.buf);
984 wstrbuf_finish(&buf);
985
986 TRACE("ret %s\n", debugstr_w(*p));
987 return S_OK;
988 }
989
990 static HRESULT WINAPI HTMLTxtRange_parentElement(IHTMLTxtRange *iface, IHTMLElement **parent)
991 {
992 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
993 nsIDOMNode *nsnode, *tmp;
994 HTMLDOMNode *node;
995 HRESULT hres;
996
997 TRACE("(%p)->(%p)\n", This, parent);
998
999 nsIDOMRange_GetCommonAncestorContainer(This->nsrange, &nsnode);
1000 while(nsnode && get_node_type(nsnode) != ELEMENT_NODE) {
1001 nsIDOMNode_GetParentNode(nsnode, &tmp);
1002 nsIDOMNode_Release(nsnode);
1003 nsnode = tmp;
1004 }
1005
1006 if(!nsnode) {
1007 *parent = NULL;
1008 return S_OK;
1009 }
1010
1011 hres = get_node(This->doc, nsnode, TRUE, &node);
1012 nsIDOMNode_Release(nsnode);
1013 if(FAILED(hres))
1014 return hres;
1015
1016 hres = IHTMLDOMNode_QueryInterface(&node->IHTMLDOMNode_iface, &IID_IHTMLElement, (void**)parent);
1017 node_release(node);
1018 return hres;
1019 }
1020
1021 static HRESULT WINAPI HTMLTxtRange_duplicate(IHTMLTxtRange *iface, IHTMLTxtRange **Duplicate)
1022 {
1023 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1024 nsIDOMRange *nsrange = NULL;
1025 HRESULT hres;
1026
1027 TRACE("(%p)->(%p)\n", This, Duplicate);
1028
1029 nsIDOMRange_CloneRange(This->nsrange, &nsrange);
1030 hres = HTMLTxtRange_Create(This->doc, nsrange, Duplicate);
1031 nsIDOMRange_Release(nsrange);
1032
1033 return hres;
1034 }
1035
1036 static HRESULT WINAPI HTMLTxtRange_inRange(IHTMLTxtRange *iface, IHTMLTxtRange *Range,
1037 VARIANT_BOOL *InRange)
1038 {
1039 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1040 HTMLTxtRange *src_range;
1041 short nsret = 0;
1042 nsresult nsres;
1043
1044 TRACE("(%p)->(%p %p)\n", This, Range, InRange);
1045
1046 *InRange = VARIANT_FALSE;
1047
1048 src_range = get_range_object(This->doc, Range);
1049 if(!src_range)
1050 return E_FAIL;
1051
1052 nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_START_TO_START,
1053 src_range->nsrange, &nsret);
1054 if(NS_SUCCEEDED(nsres) && nsret <= 0) {
1055 nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_END_TO_END,
1056 src_range->nsrange, &nsret);
1057 if(NS_SUCCEEDED(nsres) && nsret >= 0)
1058 *InRange = VARIANT_TRUE;
1059 }
1060
1061 if(NS_FAILED(nsres))
1062 ERR("CompareBoundaryPoints failed: %08x\n", nsres);
1063
1064 return S_OK;
1065 }
1066
1067 static HRESULT WINAPI HTMLTxtRange_isEqual(IHTMLTxtRange *iface, IHTMLTxtRange *Range,
1068 VARIANT_BOOL *IsEqual)
1069 {
1070 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1071 HTMLTxtRange *src_range;
1072 short nsret = 0;
1073 nsresult nsres;
1074
1075 TRACE("(%p)->(%p %p)\n", This, Range, IsEqual);
1076
1077 *IsEqual = VARIANT_FALSE;
1078
1079 src_range = get_range_object(This->doc, Range);
1080 if(!src_range)
1081 return E_FAIL;
1082
1083 nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_START_TO_START,
1084 src_range->nsrange, &nsret);
1085 if(NS_SUCCEEDED(nsres) && !nsret) {
1086 nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_END_TO_END,
1087 src_range->nsrange, &nsret);
1088 if(NS_SUCCEEDED(nsres) && !nsret)
1089 *IsEqual = VARIANT_TRUE;
1090 }
1091
1092 if(NS_FAILED(nsres))
1093 ERR("CompareBoundaryPoints failed: %08x\n", nsres);
1094
1095 return S_OK;
1096 }
1097
1098 static HRESULT WINAPI HTMLTxtRange_scrollIntoView(IHTMLTxtRange *iface, VARIANT_BOOL fStart)
1099 {
1100 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1101 FIXME("(%p)->(%x)\n", This, fStart);
1102 return E_NOTIMPL;
1103 }
1104
1105 static HRESULT WINAPI HTMLTxtRange_collapse(IHTMLTxtRange *iface, VARIANT_BOOL Start)
1106 {
1107 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1108
1109 TRACE("(%p)->(%x)\n", This, Start);
1110
1111 nsIDOMRange_Collapse(This->nsrange, Start != VARIANT_FALSE);
1112 return S_OK;
1113 }
1114
1115 static HRESULT WINAPI HTMLTxtRange_expand(IHTMLTxtRange *iface, BSTR Unit, VARIANT_BOOL *Success)
1116 {
1117 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1118 range_unit_t unit;
1119
1120 TRACE("(%p)->(%s %p)\n", This, debugstr_w(Unit), Success);
1121
1122 unit = string_to_unit(Unit);
1123 if(unit == RU_UNKNOWN)
1124 return E_INVALIDARG;
1125
1126 *Success = VARIANT_FALSE;
1127
1128 switch(unit) {
1129 case RU_WORD: {
1130 rangepoint_t end, start;
1131 cpp_bool is_collapsed;
1132
1133 get_start_point(This, &start);
1134 get_end_point(This, &end);
1135
1136 nsIDOMRange_GetCollapsed(This->nsrange, &is_collapsed);
1137
1138 if(find_word_end(&end, is_collapsed)) {
1139 set_end_point(This, &end);
1140 *Success = VARIANT_TRUE;
1141 }
1142
1143 if(find_prev_space(&start, TRUE)) {
1144 set_start_point(This, &start);
1145 *Success = VARIANT_TRUE;
1146 }
1147
1148 free_rangepoint(&end);
1149 free_rangepoint(&start);
1150 break;
1151 }
1152
1153 case RU_TEXTEDIT: {
1154 nsIDOMHTMLElement *nsbody = NULL;
1155 nsresult nsres;
1156
1157 nsres = nsIDOMHTMLDocument_GetBody(This->doc->nsdoc, &nsbody);
1158 if(NS_FAILED(nsres) || !nsbody) {
1159 ERR("Could not get body: %08x\n", nsres);
1160 break;
1161 }
1162
1163 nsres = nsIDOMRange_SelectNodeContents(This->nsrange, (nsIDOMNode*)nsbody);
1164 nsIDOMHTMLElement_Release(nsbody);
1165 if(NS_FAILED(nsres)) {
1166 ERR("Collapse failed: %08x\n", nsres);
1167 break;
1168 }
1169
1170 *Success = VARIANT_TRUE;
1171 break;
1172 }
1173
1174 default:
1175 FIXME("Unimplemented unit %s\n", debugstr_w(Unit));
1176 }
1177
1178 return S_OK;
1179 }
1180
1181 static HRESULT WINAPI HTMLTxtRange_move(IHTMLTxtRange *iface, BSTR Unit,
1182 LONG Count, LONG *ActualCount)
1183 {
1184 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1185 range_unit_t unit;
1186
1187 TRACE("(%p)->(%s %d %p)\n", This, debugstr_w(Unit), Count, ActualCount);
1188
1189 unit = string_to_unit(Unit);
1190 if(unit == RU_UNKNOWN)
1191 return E_INVALIDARG;
1192
1193 if(!Count) {
1194 *ActualCount = 0;
1195 return IHTMLTxtRange_collapse(&This->IHTMLTxtRange_iface, VARIANT_TRUE);
1196 }
1197
1198 switch(unit) {
1199 case RU_CHAR: {
1200 rangepoint_t start;
1201
1202 get_start_point(This, &start);
1203
1204 *ActualCount = move_by_chars(&start, Count);
1205
1206 set_start_point(This, &start);
1207 IHTMLTxtRange_collapse(&This->IHTMLTxtRange_iface, VARIANT_TRUE);
1208 free_rangepoint(&start);
1209 break;
1210 }
1211
1212 case RU_WORD: {
1213 rangepoint_t start;
1214
1215 get_start_point(This, &start);
1216
1217 *ActualCount = move_by_words(&start, Count);
1218
1219 set_start_point(This, &start);
1220 IHTMLTxtRange_collapse(&This->IHTMLTxtRange_iface, VARIANT_TRUE);
1221 free_rangepoint(&start);
1222 break;
1223 }
1224
1225 default:
1226 FIXME("unimplemented unit %s\n", debugstr_w(Unit));
1227 }
1228
1229 TRACE("ret %d\n", *ActualCount);
1230 return S_OK;
1231 }
1232
1233 static HRESULT WINAPI HTMLTxtRange_moveStart(IHTMLTxtRange *iface, BSTR Unit,
1234 LONG Count, LONG *ActualCount)
1235 {
1236 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1237 range_unit_t unit;
1238
1239 TRACE("(%p)->(%s %d %p)\n", This, debugstr_w(Unit), Count, ActualCount);
1240
1241 unit = string_to_unit(Unit);
1242 if(unit == RU_UNKNOWN)
1243 return E_INVALIDARG;
1244
1245 if(!Count) {
1246 *ActualCount = 0;
1247 return S_OK;
1248 }
1249
1250 switch(unit) {
1251 case RU_CHAR: {
1252 rangepoint_t start;
1253
1254 get_start_point(This, &start);
1255
1256 *ActualCount = move_by_chars(&start, Count);
1257
1258 set_start_point(This, &start);
1259 free_rangepoint(&start);
1260 break;
1261 }
1262
1263 default:
1264 FIXME("unimplemented unit %s\n", debugstr_w(Unit));
1265 return E_NOTIMPL;
1266 }
1267
1268 return S_OK;
1269 }
1270
1271 static HRESULT WINAPI HTMLTxtRange_moveEnd(IHTMLTxtRange *iface, BSTR Unit,
1272 LONG Count, LONG *ActualCount)
1273 {
1274 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1275 range_unit_t unit;
1276
1277 TRACE("(%p)->(%s %d %p)\n", This, debugstr_w(Unit), Count, ActualCount);
1278
1279 unit = string_to_unit(Unit);
1280 if(unit == RU_UNKNOWN)
1281 return E_INVALIDARG;
1282
1283 if(!Count) {
1284 *ActualCount = 0;
1285 return S_OK;
1286 }
1287
1288 switch(unit) {
1289 case RU_CHAR: {
1290 rangepoint_t end;
1291
1292 get_end_point(This, &end);
1293
1294 *ActualCount = move_by_chars(&end, Count);
1295
1296 set_end_point(This, &end);
1297 free_rangepoint(&end);
1298 break;
1299 }
1300
1301 default:
1302 FIXME("unimplemented unit %s\n", debugstr_w(Unit));
1303 }
1304
1305 return S_OK;
1306 }
1307
1308 static HRESULT WINAPI HTMLTxtRange_select(IHTMLTxtRange *iface)
1309 {
1310 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1311 nsISelection *nsselection;
1312 nsresult nsres;
1313
1314 TRACE("(%p)\n", This);
1315
1316 nsres = nsIDOMWindow_GetSelection(This->doc->basedoc.window->nswindow, &nsselection);
1317 if(NS_FAILED(nsres)) {
1318 ERR("GetSelection failed: %08x\n", nsres);
1319 return E_FAIL;
1320 }
1321
1322 nsISelection_RemoveAllRanges(nsselection);
1323 nsISelection_AddRange(nsselection, This->nsrange);
1324 nsISelection_Release(nsselection);
1325 return S_OK;
1326 }
1327
1328 static HRESULT WINAPI HTMLTxtRange_pasteHTML(IHTMLTxtRange *iface, BSTR html)
1329 {
1330 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1331 nsIDOMDocumentFragment *doc_frag;
1332 nsAString nsstr;
1333 nsresult nsres;
1334
1335 TRACE("(%p)->(%s)\n", This, debugstr_w(html));
1336
1337 nsres = nsIDOMRange_Collapse(This->nsrange, TRUE);
1338 assert(nsres == NS_OK);
1339
1340 nsAString_InitDepend(&nsstr, html);
1341 nsres = nsIDOMRange_CreateContextualFragment(This->nsrange, &nsstr, &doc_frag);
1342 nsAString_Finish(&nsstr);
1343 if(NS_FAILED(nsres)) {
1344 ERR("CreateContextualFragment failed: %08x\n", nsres);
1345 return E_FAIL;
1346 }
1347
1348 nsres = nsIDOMRange_InsertNode(This->nsrange, (nsIDOMNode*)doc_frag);
1349 nsIDOMDocumentFragment_Release(doc_frag);
1350 if(NS_FAILED(nsres)) {
1351 ERR("InsertNode failed: %08x\n", nsres);
1352 return E_FAIL;
1353 }
1354
1355 nsres = nsIDOMRange_Collapse(This->nsrange, FALSE);
1356 assert(nsres == NS_OK);
1357 return S_OK;
1358 }
1359
1360 static HRESULT WINAPI HTMLTxtRange_moveToElementText(IHTMLTxtRange *iface, IHTMLElement *element)
1361 {
1362 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1363 HTMLElement *elem;
1364 nsresult nsres;
1365
1366 TRACE("(%p)->(%p)\n", This, element);
1367
1368 elem = unsafe_impl_from_IHTMLElement(element);
1369 if(!elem)
1370 return E_INVALIDARG;
1371
1372 nsres = nsIDOMRange_SelectNodeContents(This->nsrange, elem->node.nsnode);
1373 if(NS_FAILED(nsres)) {
1374 ERR("SelectNodeContents failed: %08x\n", nsres);
1375 return E_FAIL;
1376 }
1377
1378 return S_OK;
1379 }
1380
1381 static HRESULT WINAPI HTMLTxtRange_setEndPoint(IHTMLTxtRange *iface, BSTR how,
1382 IHTMLTxtRange *SourceRange)
1383 {
1384 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1385 HTMLTxtRange *src_range;
1386 nsIDOMNode *ref_node;
1387 INT32 ref_offset;
1388 BOOL set_start;
1389 int how_type;
1390 INT16 cmp;
1391 nsresult nsres;
1392
1393 TRACE("(%p)->(%s %p)\n", This, debugstr_w(how), SourceRange);
1394
1395 how_type = string_to_nscmptype(how);
1396 if(how_type == -1)
1397 return E_INVALIDARG;
1398
1399 src_range = get_range_object(This->doc, SourceRange);
1400 if(!src_range)
1401 return E_FAIL;
1402
1403 switch(how_type) {
1404 case NS_START_TO_START:
1405 case NS_END_TO_START:
1406 nsres = nsIDOMRange_GetStartContainer(src_range->nsrange, &ref_node);
1407 assert(nsres == NS_OK);
1408
1409 nsres = nsIDOMRange_GetStartOffset(src_range->nsrange, &ref_offset);
1410 assert(nsres == NS_OK);
1411
1412 set_start = how_type == NS_START_TO_START;
1413 break;
1414 case NS_END_TO_END:
1415 case NS_START_TO_END:
1416 nsres = nsIDOMRange_GetEndContainer(src_range->nsrange, &ref_node);
1417 assert(nsres == NS_OK);
1418
1419 nsres = nsIDOMRange_GetEndOffset(src_range->nsrange, &ref_offset);
1420 assert(nsres == NS_OK);
1421
1422 set_start = how_type == NS_START_TO_END;
1423 break;
1424 DEFAULT_UNREACHABLE;
1425 }
1426
1427 nsres = nsIDOMRange_ComparePoint(This->nsrange, ref_node, ref_offset, &cmp);
1428 assert(nsres == NS_OK);
1429
1430 if(set_start) {
1431 if(cmp <= 0) {
1432 nsres = nsIDOMRange_SetStart(This->nsrange, ref_node, ref_offset);
1433 }else {
1434 nsres = nsIDOMRange_Collapse(This->nsrange, FALSE);
1435 assert(nsres == NS_OK);
1436
1437 nsres = nsIDOMRange_SetEnd(This->nsrange, ref_node, ref_offset);
1438 }
1439 }else {
1440 if(cmp >= 0) {
1441 nsres = nsIDOMRange_SetEnd(This->nsrange, ref_node, ref_offset);
1442 }else {
1443 nsres = nsIDOMRange_Collapse(This->nsrange, TRUE);
1444 assert(nsres == NS_OK);
1445
1446 nsres = nsIDOMRange_SetStart(This->nsrange, ref_node, ref_offset);
1447 }
1448 }
1449 assert(nsres == NS_OK);
1450
1451 nsIDOMNode_Release(ref_node);
1452 return S_OK;
1453 }
1454
1455 static HRESULT WINAPI HTMLTxtRange_compareEndPoints(IHTMLTxtRange *iface, BSTR how,
1456 IHTMLTxtRange *SourceRange, LONG *ret)
1457 {
1458 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1459 HTMLTxtRange *src_range;
1460 short nsret = 0;
1461 int nscmpt;
1462 nsresult nsres;
1463
1464 TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(how), SourceRange, ret);
1465
1466 nscmpt = string_to_nscmptype(how);
1467 if(nscmpt == -1)
1468 return E_INVALIDARG;
1469
1470 src_range = get_range_object(This->doc, SourceRange);
1471 if(!src_range)
1472 return E_FAIL;
1473
1474 nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, nscmpt, src_range->nsrange, &nsret);
1475 if(NS_FAILED(nsres))
1476 ERR("CompareBoundaryPoints failed: %08x\n", nsres);
1477
1478 *ret = nsret;
1479 return S_OK;
1480 }
1481
1482 static HRESULT WINAPI HTMLTxtRange_findText(IHTMLTxtRange *iface, BSTR String,
1483 LONG count, LONG Flags, VARIANT_BOOL *Success)
1484 {
1485 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1486 FIXME("(%p)->(%s %d %08x %p)\n", This, debugstr_w(String), count, Flags, Success);
1487 return E_NOTIMPL;
1488 }
1489
1490 static HRESULT WINAPI HTMLTxtRange_moveToPoint(IHTMLTxtRange *iface, LONG x, LONG y)
1491 {
1492 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1493 FIXME("(%p)->(%d %d)\n", This, x, y);
1494 return E_NOTIMPL;
1495 }
1496
1497 static HRESULT WINAPI HTMLTxtRange_getBookmark(IHTMLTxtRange *iface, BSTR *Bookmark)
1498 {
1499 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1500 FIXME("(%p)->(%p)\n", This, Bookmark);
1501 return E_NOTIMPL;
1502 }
1503
1504 static HRESULT WINAPI HTMLTxtRange_moveToBookmark(IHTMLTxtRange *iface, BSTR Bookmark,
1505 VARIANT_BOOL *Success)
1506 {
1507 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1508 FIXME("(%p)->(%s %p)\n", This, debugstr_w(Bookmark), Success);
1509 return E_NOTIMPL;
1510 }
1511
1512 static HRESULT WINAPI HTMLTxtRange_queryCommandSupported(IHTMLTxtRange *iface, BSTR cmdID,
1513 VARIANT_BOOL *pfRet)
1514 {
1515 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1516 FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1517 return E_NOTIMPL;
1518 }
1519
1520 static HRESULT WINAPI HTMLTxtRange_queryCommandEnabled(IHTMLTxtRange *iface, BSTR cmdID,
1521 VARIANT_BOOL *pfRet)
1522 {
1523 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1524 FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1525 return E_NOTIMPL;
1526 }
1527
1528 static HRESULT WINAPI HTMLTxtRange_queryCommandState(IHTMLTxtRange *iface, BSTR cmdID,
1529 VARIANT_BOOL *pfRet)
1530 {
1531 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1532 FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1533 return E_NOTIMPL;
1534 }
1535
1536 static HRESULT WINAPI HTMLTxtRange_queryCommandIndeterm(IHTMLTxtRange *iface, BSTR cmdID,
1537 VARIANT_BOOL *pfRet)
1538 {
1539 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1540 FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1541 return E_NOTIMPL;
1542 }
1543
1544 static HRESULT WINAPI HTMLTxtRange_queryCommandText(IHTMLTxtRange *iface, BSTR cmdID,
1545 BSTR *pcmdText)
1546 {
1547 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1548 FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pcmdText);
1549 return E_NOTIMPL;
1550 }
1551
1552 static HRESULT WINAPI HTMLTxtRange_queryCommandValue(IHTMLTxtRange *iface, BSTR cmdID,
1553 VARIANT *pcmdValue)
1554 {
1555 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1556 FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pcmdValue);
1557 return E_NOTIMPL;
1558 }
1559
1560 static HRESULT WINAPI HTMLTxtRange_execCommand(IHTMLTxtRange *iface, BSTR cmdID,
1561 VARIANT_BOOL showUI, VARIANT value, VARIANT_BOOL *pfRet)
1562 {
1563 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1564 FIXME("(%p)->(%s %x v %p)\n", This, debugstr_w(cmdID), showUI, pfRet);
1565 return E_NOTIMPL;
1566 }
1567
1568 static HRESULT WINAPI HTMLTxtRange_execCommandShowHelp(IHTMLTxtRange *iface, BSTR cmdID,
1569 VARIANT_BOOL *pfRet)
1570 {
1571 HTMLTxtRange *This = impl_from_IHTMLTxtRange(iface);
1572 FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1573 return E_NOTIMPL;
1574 }
1575
1576 static const IHTMLTxtRangeVtbl HTMLTxtRangeVtbl = {
1577 HTMLTxtRange_QueryInterface,
1578 HTMLTxtRange_AddRef,
1579 HTMLTxtRange_Release,
1580 HTMLTxtRange_GetTypeInfoCount,
1581 HTMLTxtRange_GetTypeInfo,
1582 HTMLTxtRange_GetIDsOfNames,
1583 HTMLTxtRange_Invoke,
1584 HTMLTxtRange_get_htmlText,
1585 HTMLTxtRange_put_text,
1586 HTMLTxtRange_get_text,
1587 HTMLTxtRange_parentElement,
1588 HTMLTxtRange_duplicate,
1589 HTMLTxtRange_inRange,
1590 HTMLTxtRange_isEqual,
1591 HTMLTxtRange_scrollIntoView,
1592 HTMLTxtRange_collapse,
1593 HTMLTxtRange_expand,
1594 HTMLTxtRange_move,
1595 HTMLTxtRange_moveStart,
1596 HTMLTxtRange_moveEnd,
1597 HTMLTxtRange_select,
1598 HTMLTxtRange_pasteHTML,
1599 HTMLTxtRange_moveToElementText,
1600 HTMLTxtRange_setEndPoint,
1601 HTMLTxtRange_compareEndPoints,
1602 HTMLTxtRange_findText,
1603 HTMLTxtRange_moveToPoint,
1604 HTMLTxtRange_getBookmark,
1605 HTMLTxtRange_moveToBookmark,
1606 HTMLTxtRange_queryCommandSupported,
1607 HTMLTxtRange_queryCommandEnabled,
1608 HTMLTxtRange_queryCommandState,
1609 HTMLTxtRange_queryCommandIndeterm,
1610 HTMLTxtRange_queryCommandText,
1611 HTMLTxtRange_queryCommandValue,
1612 HTMLTxtRange_execCommand,
1613 HTMLTxtRange_execCommandShowHelp
1614 };
1615
1616 static inline HTMLTxtRange *impl_from_IOleCommandTarget(IOleCommandTarget *iface)
1617 {
1618 return CONTAINING_RECORD(iface, HTMLTxtRange, IOleCommandTarget_iface);
1619 }
1620
1621 static HRESULT WINAPI RangeCommandTarget_QueryInterface(IOleCommandTarget *iface, REFIID riid, void **ppv)
1622 {
1623 HTMLTxtRange *This = impl_from_IOleCommandTarget(iface);
1624 return IHTMLTxtRange_QueryInterface(&This->IHTMLTxtRange_iface, riid, ppv);
1625 }
1626
1627 static ULONG WINAPI RangeCommandTarget_AddRef(IOleCommandTarget *iface)
1628 {
1629 HTMLTxtRange *This = impl_from_IOleCommandTarget(iface);
1630 return IHTMLTxtRange_AddRef(&This->IHTMLTxtRange_iface);
1631 }
1632
1633 static ULONG WINAPI RangeCommandTarget_Release(IOleCommandTarget *iface)
1634 {
1635 HTMLTxtRange *This = impl_from_IOleCommandTarget(iface);
1636 return IHTMLTxtRange_Release(&This->IHTMLTxtRange_iface);
1637 }
1638
1639 static HRESULT WINAPI RangeCommandTarget_QueryStatus(IOleCommandTarget *iface, const GUID *pguidCmdGroup,
1640 ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
1641 {
1642 HTMLTxtRange *This = impl_from_IOleCommandTarget(iface);
1643 FIXME("(%p)->(%s %d %p %p)\n", This, debugstr_guid(pguidCmdGroup), cCmds, prgCmds, pCmdText);
1644 return E_NOTIMPL;
1645 }
1646
1647 static HRESULT exec_indent(HTMLTxtRange *This, VARIANT *in, VARIANT *out)
1648 {
1649 nsIDOMHTMLElement *blockquote_elem, *p_elem;
1650 nsIDOMDocumentFragment *fragment;
1651 nsIDOMNode *tmp;
1652
1653 static const PRUnichar blockquoteW[] = {'B','L','O','C','K','Q','U','O','T','E',0};
1654 static const PRUnichar pW[] = {'P',0};
1655
1656 TRACE("(%p)->(%p %p)\n", This, in, out);
1657
1658 if(!This->doc->nsdoc) {
1659 WARN("NULL nsdoc\n");
1660 return E_NOTIMPL;
1661 }
1662
1663 create_nselem(This->doc, blockquoteW, &blockquote_elem);
1664 create_nselem(This->doc, pW, &p_elem);
1665
1666 nsIDOMRange_ExtractContents(This->nsrange, &fragment);
1667 nsIDOMHTMLElement_AppendChild(p_elem, (nsIDOMNode*)fragment, &tmp);
1668 nsIDOMDocumentFragment_Release(fragment);
1669 nsIDOMNode_Release(tmp);
1670
1671 nsIDOMHTMLElement_AppendChild(blockquote_elem, (nsIDOMNode*)p_elem, &tmp);
1672 nsIDOMHTMLElement_Release(p_elem);
1673 nsIDOMNode_Release(tmp);
1674
1675 nsIDOMRange_InsertNode(This->nsrange, (nsIDOMNode*)blockquote_elem);
1676 nsIDOMHTMLElement_Release(blockquote_elem);
1677
1678 return S_OK;
1679 }
1680
1681 static HRESULT WINAPI RangeCommandTarget_Exec(IOleCommandTarget *iface, const GUID *pguidCmdGroup,
1682 DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
1683 {
1684 HTMLTxtRange *This = impl_from_IOleCommandTarget(iface);
1685
1686 TRACE("(%p)->(%s %d %x %p %p)\n", This, debugstr_guid(pguidCmdGroup), nCmdID,
1687 nCmdexecopt, pvaIn, pvaOut);
1688
1689 if(pguidCmdGroup && IsEqualGUID(&CGID_MSHTML, pguidCmdGroup)) {
1690 switch(nCmdID) {
1691 case IDM_INDENT:
1692 return exec_indent(This, pvaIn, pvaOut);
1693 default:
1694 FIXME("Unsupported cmdid %d of CGID_MSHTML\n", nCmdID);
1695 }
1696 }else {
1697 FIXME("Unsupported cmd %d of group %s\n", nCmdID, debugstr_guid(pguidCmdGroup));
1698 }
1699
1700 return E_NOTIMPL;
1701 }
1702
1703 static const IOleCommandTargetVtbl OleCommandTargetVtbl = {
1704 RangeCommandTarget_QueryInterface,
1705 RangeCommandTarget_AddRef,
1706 RangeCommandTarget_Release,
1707 RangeCommandTarget_QueryStatus,
1708 RangeCommandTarget_Exec
1709 };
1710
1711 static const tid_t HTMLTxtRange_iface_tids[] = {
1712 IHTMLTxtRange_tid,
1713 0
1714 };
1715 static dispex_static_data_t HTMLTxtRange_dispex = {
1716 NULL,
1717 IHTMLTxtRange_tid,
1718 NULL,
1719 HTMLTxtRange_iface_tids
1720 };
1721
1722 HRESULT HTMLTxtRange_Create(HTMLDocumentNode *doc, nsIDOMRange *nsrange, IHTMLTxtRange **p)
1723 {
1724 HTMLTxtRange *ret;
1725
1726 ret = heap_alloc(sizeof(HTMLTxtRange));
1727 if(!ret)
1728 return E_OUTOFMEMORY;
1729
1730 init_dispex(&ret->dispex, (IUnknown*)&ret->IHTMLTxtRange_iface, &HTMLTxtRange_dispex);
1731
1732 ret->IHTMLTxtRange_iface.lpVtbl = &HTMLTxtRangeVtbl;
1733 ret->IOleCommandTarget_iface.lpVtbl = &OleCommandTargetVtbl;
1734 ret->ref = 1;
1735
1736 if(nsrange)
1737 nsIDOMRange_AddRef(nsrange);
1738 ret->nsrange = nsrange;
1739
1740 ret->doc = doc;
1741 list_add_head(&doc->range_list, &ret->entry);
1742
1743 *p = &ret->IHTMLTxtRange_iface;
1744 return S_OK;
1745 }
1746
1747 void detach_ranges(HTMLDocumentNode *This)
1748 {
1749 HTMLTxtRange *iter;
1750
1751 LIST_FOR_EACH_ENTRY(iter, &This->range_list, HTMLTxtRange, entry) {
1752 iter->doc = NULL;
1753 }
1754 }