2009-10-20 Chris Toshok <toshok@ximian.com>
[moon.git] / src / textblock.cpp
blob0311d6a9dae69b81f924b28b3219a66cd5d1d22d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * textblock.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include <cairo.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <errno.h>
22 #include "file-downloader.h"
23 #include "textblock.h"
24 #include "control.h"
25 #include "runtime.h"
26 #include "color.h"
27 #include "utils.h"
28 #include "debug.h"
29 #include "uri.h"
30 #include "geometry.h"
31 #include "deployment.h"
33 // Unicode Line Separator (\u2028)
34 static const char utf8_linebreak[3] = { 0xe2, 0x80, 0xa8 };
35 #define utf8_linebreak_len 3
39 // Inline
42 Inline::Inline ()
44 SetObjectType (Type::INLINE);
45 font = new TextFontDescription ();
46 downloaders = g_ptr_array_new ();
47 autogen = false;
50 Inline::~Inline ()
52 CleanupDownloaders ();
53 g_ptr_array_free (downloaders, true);
54 delete font;
57 void
58 Inline::CleanupDownloaders ()
60 Downloader *downloader;
61 guint i;
63 for (i = 0; i < downloaders->len; i++) {
64 downloader = (Downloader *) downloaders->pdata[i];
65 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
66 downloader->Abort ();
67 downloader->unref ();
70 g_ptr_array_set_size (downloaders, 0);
73 void
74 Inline::AddFontSource (Downloader *downloader)
76 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
77 g_ptr_array_add (downloaders, downloader);
78 downloader->ref ();
80 if (downloader->Started () || downloader->Completed ()) {
81 if (downloader->Completed ())
82 DownloaderComplete (downloader);
83 } else {
84 // This is what actually triggers the download
85 downloader->Send ();
89 void
90 Inline::AddFontResource (const char *resource)
92 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
93 Application *application = Application::GetCurrent ();
94 Downloader *downloader;
95 Surface *surface;
96 char *path;
97 Uri *uri;
99 uri = new Uri ();
101 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
102 if ((surface = GetSurface ()) && (downloader = surface->CreateDownloader ())) {
103 downloader->Open ("GET", resource, FontPolicy);
104 AddFontSource (downloader);
105 downloader->unref ();
108 delete uri;
110 return;
113 manager->AddResource (resource, path);
114 g_free (path);
115 delete uri;
118 void
119 Inline::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
121 if (args->GetProperty ()->GetOwnerType () != Type::INLINE) {
122 DependencyObject::OnPropertyChanged (args, error);
123 return;
126 if (args->GetId () == Inline::FontFamilyProperty) {
127 FontFamily *family = args->GetNewValue () ? args->GetNewValue ()->AsFontFamily () : NULL;
128 char **families, *fragment;
129 int i;
131 CleanupDownloaders ();
133 if (family && family->source) {
134 families = g_strsplit (family->source, ",", -1);
135 for (i = 0; families[i]; i++) {
136 g_strstrip (families[i]);
137 if ((fragment = strchr (families[i], '#'))) {
138 // the first portion of this string is the resource name...
139 *fragment = '\0';
140 AddFontResource (families[i]);
143 g_strfreev (families);
147 NotifyListenersOfPropertyChange (args, error);
150 void
151 Inline::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
153 if (prop && prop->GetId () == Inline::ForegroundProperty) {
154 // this isn't exactly what we want, I don't
155 // think... but it'll have to do.
156 NotifyListenersOfPropertyChange (prop, NULL);
157 } else {
158 DependencyObject::OnSubPropertyChanged (prop, obj, subobj_args);
162 bool
163 Inline::Equals (Inline *item)
165 const char *lang0, *lang1;
167 if (item->GetObjectType () != GetObjectType ())
168 return false;
170 if (*item->GetFontFamily () != *GetFontFamily ())
171 return false;
173 if (item->GetFontSize () != GetFontSize ())
174 return false;
176 if (item->GetFontStyle () != GetFontStyle ())
177 return false;
179 if (item->GetFontWeight () != GetFontWeight ())
180 return false;
182 if (item->GetFontStretch () != GetFontStretch ())
183 return false;
185 if (item->GetTextDecorations () != GetTextDecorations ())
186 return false;
188 lang0 = item->GetLanguage ();
189 lang1 = GetLanguage ();
191 if ((lang0 && !lang1) || (!lang0 && lang1))
192 return false;
194 if (lang0 && lang1 && strcmp (lang0, lang1) != 0)
195 return false;
197 // this isn't really correct - we should be checking
198 // the innards of the foreground brushes, but we're
199 // guaranteed to never have a false positive here.
200 if (item->GetForeground () != GetForeground ())
201 return false;
203 // OK, as best we can tell - they are equal
204 return true;
207 bool
208 Inline::UpdateFontDescription (const char *source, bool force)
210 FontFamily *family = GetFontFamily ();
211 bool changed = false;
213 if (font->SetSource (source))
214 changed = true;
216 if (font->SetFamily (family ? family->source : NULL))
217 changed = true;
219 if (font->SetStretch (GetFontStretch ()->stretch))
220 changed = true;
222 if (font->SetWeight (GetFontWeight ()->weight))
223 changed = true;
225 if (font->SetStyle (GetFontStyle ()->style))
226 changed = true;
228 if (font->SetSize (GetFontSize ()))
229 changed = true;
231 if (font->SetLanguage (GetLanguage ()))
232 changed = true;
234 if (force) {
235 font->Reload ();
236 return true;
239 return changed;
242 void
243 Inline::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
245 ((Inline *) closure)->DownloaderComplete ((Downloader *) sender);
248 void
249 Inline::DownloaderComplete (Downloader *downloader)
251 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
252 char *resource, *filename;
253 InternalDownloader *idl;
254 const char *path;
255 Uri *uri;
257 // get the downloaded file path (enforces a mozilla workaround for files smaller than 64k)
258 if (!(filename = downloader->GetDownloadedFilename (NULL)))
259 return;
261 g_free (filename);
263 if (!(idl = downloader->GetInternalDownloader ()))
264 return;
266 if (!(idl->GetObjectType () == Type::FILEDOWNLOADER))
267 return;
269 uri = downloader->GetUri ();
271 // If the downloaded file was a zip file, this'll get the path to the
272 // extracted zip directory, else it will simply be the path to the
273 // downloaded file.
274 if (!(path = ((FileDownloader *) idl)->GetUnzippedPath ()))
275 return;
277 resource = uri->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
278 manager->AddResource (resource, path);
279 g_free (resource);
284 // Run
287 bool
288 Run::Equals (Inline *item)
290 const char *text, *itext;
292 if (!Inline::Equals (item))
293 return false;
295 itext = ((Run *) item)->GetText ();
296 text = GetText ();
298 // compare the text
299 if (text && itext && strcmp (text, itext) != 0)
300 return false;
301 else if ((text && !itext) || (!text && itext))
302 return false;
304 return true;
309 // TextBlock
312 TextBlock::TextBlock ()
314 SetObjectType (Type::TEXTBLOCK);
316 font = new TextFontDescription ();
318 downloaders = g_ptr_array_new ();
319 layout = new TextLayout ();
320 font_source = NULL;
321 source = NULL;
323 actual_height = 0.0;
324 actual_width = 0.0;
325 setvalue = true;
326 was_set = false;
327 dirty = true;
330 TextBlock::~TextBlock ()
332 CleanupDownloaders (true);
333 g_ptr_array_free (downloaders, true);
335 delete layout;
336 delete font;
339 void
340 TextBlock::CleanupDownloaders (bool all)
342 Downloader *downloader;
343 guint i;
345 for (i = 0; i < downloaders->len; i++) {
346 downloader = (Downloader *) downloaders->pdata[i];
348 if (all || downloader != source) {
349 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
350 downloader->Abort ();
351 downloader->unref ();
355 g_ptr_array_set_size (downloaders, 0);
357 if (source && !all) {
358 g_ptr_array_add (downloaders, source);
359 } else {
360 source = NULL;
363 if (all) {
364 g_free (font_source);
365 font_source = NULL;
369 void
370 TextBlock::AddFontSource (Downloader *downloader)
372 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
373 g_ptr_array_add (downloaders, downloader);
374 downloader->ref ();
376 if (downloader->Started () || downloader->Completed ()) {
377 if (downloader->Completed ())
378 DownloaderComplete (downloader);
379 } else {
380 // This is what actually triggers the download
381 downloader->Send ();
385 void
386 TextBlock::SetFontSource (Downloader *downloader)
388 CleanupDownloaders (true);
389 source = downloader;
391 if (downloader) {
392 font_source = downloader->GetUri ()->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
393 AddFontSource (downloader);
394 return;
397 UpdateFontDescriptions (true);
398 UpdateBounds (true);
399 Invalidate ();
400 dirty = true;
403 void
404 TextBlock::AddFontResource (const char *resource)
406 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
407 Application *application = Application::GetCurrent ();
408 Downloader *downloader;
409 Surface *surface;
410 char *path;
411 Uri *uri;
413 uri = new Uri ();
415 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
416 if ((surface = GetSurface ()) && (downloader = surface->CreateDownloader ())) {
417 downloader->Open ("GET", resource, FontPolicy);
418 AddFontSource (downloader);
419 downloader->unref ();
422 delete uri;
424 return;
427 manager->AddResource (resource, path);
428 g_free (path);
429 delete uri;
432 void
433 TextBlock::Render (cairo_t *cr, Region *region, bool path_only)
435 cairo_save (cr);
436 cairo_set_matrix (cr, &absolute_xform);
438 if (!path_only)
439 RenderLayoutClip (cr);
441 Paint (cr);
443 cairo_restore (cr);
446 void
447 TextBlock::ComputeBounds ()
449 Size actual (GetActualWidth (), GetActualHeight ());
450 Size framework = ApplySizeConstraints (actual);
452 framework = framework.Max (actual);
454 Rect extents = Rect (0,0,framework.width, framework.height);
456 bounds = bounds_with_children = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
459 void
460 TextBlock::GetSizeForBrush (cairo_t *cr, double *width, double *height)
462 *width = actual_width;
463 *height = actual_height;
466 Point
467 TextBlock::GetTransformOrigin ()
469 Point *user_xform_origin = GetRenderTransformOrigin ();
470 return Point (actual_width * user_xform_origin->x,
471 actual_height * user_xform_origin->y);
474 Size
475 TextBlock::ComputeActualSize ()
477 Thickness padding = *GetPadding ();
478 Size result = FrameworkElement::ComputeActualSize ();
480 if (!LayoutInformation::GetPreviousConstraint (this)) {
481 Size constraint = Size (INFINITY, INFINITY);
483 constraint = ApplySizeConstraints (constraint);
485 constraint = constraint.GrowBy (-padding);
486 Layout (constraint);
489 result = Size (actual_width, actual_height);
490 result = result.GrowBy (padding);
492 return result;
495 Size
496 TextBlock::MeasureOverride (Size availableSize)
498 Thickness padding = *GetPadding ();
499 Size constraint;
500 Size desired;
502 constraint = availableSize.GrowBy (-padding);
503 Layout (constraint);
505 desired = Size (actual_width, actual_height).GrowBy (padding);
507 SetActualHeight (desired.height);
508 SetActualWidth (desired.width);
510 if (GetUseLayoutRounding ())
511 desired.width = ceil (desired.width);
513 desired = desired.Min (availableSize);
515 return desired;
518 Size
519 TextBlock::ArrangeOverride (Size finalSize)
521 Thickness padding = *GetPadding ();
522 Size constraint;
523 Size arranged;
525 constraint = finalSize.GrowBy (-padding);
526 Layout (constraint);
528 arranged = Size (actual_width, actual_height).GrowBy (padding);
530 arranged = arranged.Max (finalSize);
531 arranged = ApplySizeConstraints (arranged);
533 layout->SetAvailableWidth (arranged.GrowBy (-padding).width);
535 return arranged;
538 void
539 TextBlock::UpdateLayoutAttributes ()
541 InlineCollection *inlines = GetInlines ();
542 TextLayoutAttributes *attrs;
543 const char *text;
544 int length = 0;
545 Inline *item;
546 List *runs;
548 InvalidateMeasure ();
549 InvalidateArrange ();
550 runs = new List ();
552 UpdateFontDescription (false);
554 if (inlines != NULL) {
555 for (int i = 0; i < inlines->GetCount (); i++) {
556 item = inlines->GetValueAt (i)->AsInline ();
557 item->UpdateFontDescription (font_source, false);
559 switch (item->GetObjectType ()) {
560 case Type::RUN:
561 text = ((Run *) item)->GetText ();
563 if (text && text[0]) {
564 attrs = new TextLayoutAttributes ((ITextAttributes *) item, length);
565 runs->Append (attrs);
567 length += strlen (text);
570 break;
571 case Type::LINEBREAK:
572 attrs = new TextLayoutAttributes ((ITextAttributes *) item, length);
573 runs->Append (attrs);
575 length += utf8_linebreak_len;
576 break;
577 default:
578 break;
582 if (inlines->GetCount () > 0)
583 was_set = true;
586 layout->SetText (GetText (), length);
587 layout->SetTextAttributes (runs);
590 bool
591 TextBlock::UpdateFontDescription (bool force)
593 FontFamily *family = GetFontFamily ();
594 bool changed = false;
596 if (font->SetSource (font_source))
597 changed = true;
599 if (font->SetFamily (family ? family->source : NULL))
600 changed = true;
602 if (font->SetStretch (GetFontStretch ()->stretch))
603 changed = true;
605 if (font->SetWeight (GetFontWeight ()->weight))
606 changed = true;
608 if (font->SetStyle (GetFontStyle ()->style))
609 changed = true;
611 if (font->SetSize (GetFontSize ()))
612 changed = true;
614 if (font->SetLanguage (GetLanguage ()))
615 changed = true;
617 if (force) {
618 font->Reload ();
619 changed = true;
622 if (changed)
623 layout->SetBaseFont (font->GetFont ());
625 return changed;
628 bool
629 TextBlock::UpdateFontDescriptions (bool force)
631 InlineCollection *inlines = GetInlines ();
632 bool changed = false;
633 Inline *item;
635 changed = UpdateFontDescription (force);
637 if (inlines != NULL) {
638 for (int i = 0; i < inlines->GetCount (); i++) {
639 item = inlines->GetValueAt (i)->AsInline ();
640 if (item->UpdateFontDescription (font_source, force))
641 changed = true;
644 if (changed)
645 layout->ResetState ();
648 if (changed) {
649 InvalidateMeasure ();
650 InvalidateArrange ();
651 UpdateBounds (true);
652 dirty = true;
655 return changed;
658 void
659 TextBlock::Layout (Size constraint)
661 if (was_set && !GetValueNoDefault (TextBlock::TextProperty)) {
662 // If the Text property had been set once upon a time,
663 // but is currently empty, Silverlight seems to set
664 // the ActualHeight property to the font height. See
665 // bug #405514 for details.
666 TextFontDescription *desc = new TextFontDescription ();
667 FontFamily *family = GetFontFamily ();
668 TextFont *font;
670 desc->SetFamily (family ? family->source : NULL);
671 desc->SetStretch (GetFontStretch ()->stretch);
672 desc->SetWeight (GetFontWeight ()->weight);
673 desc->SetStyle (GetFontStyle ()->style);
674 desc->SetSize (GetFontSize ());
676 font = desc->GetFont ();
677 actual_height = font->Height ();
678 actual_width = 0.0;
679 delete desc;
680 } else if (!was_set) {
681 // If the Text property has never been set, then its
682 // extents should both be 0.0. See bug #435798 for
683 // details.
684 actual_height = 0.0;
685 actual_width = 0.0;
686 } else {
687 layout->SetMaxWidth (constraint.width);
688 layout->Layout ();
690 layout->GetActualExtents (&actual_width, &actual_height);
693 dirty = false;
696 void
697 TextBlock::Paint (cairo_t *cr)
699 Thickness *padding = GetPadding ();
700 Point offset (padding->left, padding->top);
702 cairo_set_matrix (cr, &absolute_xform);
703 layout->Render (cr, GetOriginPoint (), offset);
705 if (moonlight_flags & RUNTIME_INIT_SHOW_TEXTBOXES) {
706 cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 1.0);
707 cairo_set_line_width (cr, 1);
708 cairo_rectangle (cr, padding->left, padding->top, actual_width, actual_height);
709 cairo_stroke (cr);
713 char *
714 TextBlock::GetTextInternal (InlineCollection *inlines)
716 const char *text;
717 GString *block;
718 Inline *item;
719 char *str;
721 if (!inlines)
722 return g_strdup ("");
724 block = g_string_new ("");
726 for (int i = 0; i < inlines->GetCount (); i++) {
727 item = inlines->GetValueAt (i)->AsInline ();
729 switch (item->GetObjectType ()) {
730 case Type::RUN:
731 text = ((Run *) item)->GetText ();
733 if (text && text[0])
734 g_string_append (block, text);
735 break;
736 case Type::LINEBREAK:
737 g_string_append_len (block, utf8_linebreak, utf8_linebreak_len);
738 break;
739 default:
740 break;
744 str = block->str;
745 g_string_free (block, false);
747 return str;
750 void
751 TextBlock::SetTextInternal (const char *text)
753 InlineCollection *inlines;
754 Value *value;
755 Run *run;
757 // Note: calling GetValue() may cause the InlineCollection to be
758 // autocreated, so we need to prevent reentrancy here.
759 setvalue = false;
761 value = GetValue (TextBlock::InlinesProperty);
762 inlines = value->AsInlineCollection ();
763 inlines->Clear ();
765 if (text) {
766 run = new Run ();
767 run->SetAutogenerated (true);
768 run->SetText (text);
769 inlines->Add (run);
770 run->unref ();
771 } else {
772 // setting text to null results in String.Empty
773 SetValue (TextBlock::TextProperty, Value (""));
776 setvalue = true;
779 void
780 TextBlock::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
782 bool invalidate = true;
784 if (args->GetProperty ()->GetOwnerType () != Type::TEXTBLOCK) {
785 FrameworkElement::OnPropertyChanged (args, error);
787 if (args->GetId () == FrameworkElement::LanguageProperty) {
788 // a change in xml:lang might change font characteristics
789 if (UpdateFontDescriptions (false)) {
790 InvalidateMeasure ();
791 InvalidateArrange ();
792 UpdateBounds (true);
793 dirty = true;
798 if (args->GetId () == FrameworkElement::WidthProperty) {
799 //if (layout->SetMaxWidth (args->GetNewValue()->AsDouble ()))
800 // dirty = true;
802 UpdateBounds (true);
805 return;
808 if (args->GetId () == TextBlock::FontFamilyProperty) {
809 FontFamily *family = args->GetNewValue () ? args->GetNewValue ()->AsFontFamily () : NULL;
810 char **families, *fragment;
811 int i;
813 CleanupDownloaders (false);
815 if (family && family->source) {
816 families = g_strsplit (family->source, ",", -1);
817 for (i = 0; families[i]; i++) {
818 g_strstrip (families[i]);
819 if ((fragment = strchr (families[i], '#'))) {
820 // the first portion of this string is the resource name...
821 *fragment = '\0';
822 AddFontResource (families[i]);
825 g_strfreev (families);
828 if (UpdateFontDescriptions (false))
829 dirty = true;
830 } else if (args->GetId () == TextBlock::FontSizeProperty) {
831 if (UpdateFontDescriptions (false))
832 dirty = true;
833 } else if (args->GetId () == TextBlock::FontStretchProperty) {
834 if (UpdateFontDescriptions (false))
835 dirty = true;
836 } else if (args->GetId () == TextBlock::FontStyleProperty) {
837 if (UpdateFontDescriptions (false))
838 dirty = true;
839 } else if (args->GetId () == TextBlock::FontWeightProperty) {
840 if (UpdateFontDescriptions (false))
841 dirty = true;
842 } else if (args->GetId () == TextBlock::TextProperty) {
843 if (setvalue) {
844 // result of a change to the TextBlock.Text property
845 const char *text = args->GetNewValue() ? args->GetNewValue()->AsString () : NULL;
847 SetTextInternal (text);
848 UpdateLayoutAttributes ();
849 dirty = true;
850 } else {
851 // result of a change to the TextBlock.Inlines property
852 UpdateLayoutAttributes ();
853 invalidate = false;
855 } else if (args->GetId () == TextBlock::TextDecorationsProperty) {
856 dirty = true;
857 } else if (args->GetId () == TextBlock::TextWrappingProperty) {
858 dirty = layout->SetTextWrapping ((TextWrapping) args->GetNewValue()->AsInt32 ());
859 } else if (args->GetId () == TextBlock::InlinesProperty) {
860 if (setvalue) {
861 // result of a change to the TextBlock.Inlines property
862 InlineCollection *inlines = args->GetNewValue() ? args->GetNewValue()->AsInlineCollection () : NULL;
864 setvalue = false;
865 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
866 setvalue = true;
868 UpdateLayoutAttributes ();
869 dirty = true;
870 } else {
871 // this should be the result of Inlines being autocreated
872 UpdateLayoutAttributes ();
873 invalidate = false;
875 } else if (args->GetId () == TextBlock::LineStackingStrategyProperty) {
876 dirty = layout->SetLineStackingStrategy ((LineStackingStrategy) args->GetNewValue()->AsInt32 ());
877 } else if (args->GetId () == TextBlock::LineHeightProperty) {
878 dirty = layout->SetLineHeight (args->GetNewValue()->AsDouble ());
879 } else if (args->GetId () == TextBlock::TextAlignmentProperty) {
880 dirty = layout->SetTextAlignment ((TextAlignment) args->GetNewValue()->AsInt32 ());
881 } else if (args->GetId () == TextBlock::PaddingProperty) {
882 dirty = true;
883 } else if (args->GetId () == TextBlock::FontSourceProperty) {
884 FontSource *source = args->GetNewValue () ? args->GetNewValue ()->AsFontSource () : NULL;
885 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
887 // FIXME: ideally we'd remove the old item from the cache (or,
888 // rather, 'unref' it since some other textblocks/boxes might
889 // still be using it).
891 g_free (font_source);
893 if (source && source->stream)
894 font_source = manager->AddResource (source->stream);
895 else
896 font_source = NULL;
898 UpdateFontDescriptions (true);
899 dirty = true;
902 if (invalidate) {
903 if (dirty) {
904 InvalidateMeasure ();
905 InvalidateArrange ();
906 UpdateBounds (true);
909 Invalidate ();
912 NotifyListenersOfPropertyChange (args, error);
915 void
916 TextBlock::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
918 if (prop && prop->GetId () == TextBlock::ForegroundProperty) {
919 Invalidate ();
920 } else {
921 FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
925 void
926 TextBlock::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
928 InlineCollection *inlines = GetInlines ();
930 if (col != inlines) {
931 FrameworkElement::OnCollectionChanged (col, args);
932 return;
935 if (args->GetChangedAction () == CollectionChangedActionClearing)
936 return;
938 if (!setvalue) {
939 // changes being handled elsewhere...
940 return;
943 setvalue = false;
944 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
945 setvalue = true;
947 UpdateLayoutAttributes ();
948 InvalidateMeasure ();
949 InvalidateArrange ();
950 UpdateBounds (true);
951 Invalidate ();
954 void
955 TextBlock::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
957 InlineCollection *inlines = GetInlines ();
959 if (col != inlines) {
960 FrameworkElement::OnCollectionItemChanged (col, obj, args);
961 return;
964 if (args->GetId () != Inline::ForegroundProperty) {
965 if (args->GetId () == Run::TextProperty) {
966 // update our TextProperty
967 setvalue = false;
968 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
969 setvalue = true;
971 UpdateLayoutAttributes ();
972 } else {
973 // likely a font property change...
974 ((Inline *) obj)->UpdateFontDescription (font_source, true);
977 // All non-Foreground property changes require
978 // recalculating layout which can change the bounds.
979 InvalidateMeasure ();
980 InvalidateArrange ();
981 UpdateBounds (true);
982 dirty = true;
983 } else {
984 // A simple Foreground brush change does not require
985 // recalculating layout. Invalidate() and we're done.
988 Invalidate ();
991 void
992 TextBlock::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
994 ((TextBlock *) closure)->DownloaderComplete ((Downloader *) sender);
997 void
998 TextBlock::DownloaderComplete (Downloader *downloader)
1000 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
1001 char *resource, *filename;
1002 InternalDownloader *idl;
1003 const char *path;
1004 Uri *uri;
1006 dirty = true;
1007 InvalidateMeasure ();
1008 InvalidateArrange ();
1010 // get the downloaded file path (enforces a mozilla workaround for files smaller than 64k)
1011 if (!(filename = downloader->GetDownloadedFilename (NULL)))
1012 return;
1014 g_free (filename);
1016 if (!(idl = downloader->GetInternalDownloader ()))
1017 return;
1019 if (!(idl->GetObjectType () == Type::FILEDOWNLOADER))
1020 return;
1022 uri = downloader->GetUri ();
1024 // If the downloaded file was a zip file, this'll get the path to the
1025 // extracted zip directory, else it will simply be the path to the
1026 // downloaded file.
1027 if (!(path = ((FileDownloader *) idl)->GetUnzippedPath ()))
1028 return;
1030 resource = uri->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
1031 manager->AddResource (resource, path);
1032 g_free (resource);
1034 if (UpdateFontDescriptions (true)) {
1035 dirty = true;
1037 UpdateBounds (true);
1038 Invalidate ();