2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / textblock.cpp
blobaf1c5ae1885e37a7ad659c8443736159f2d5c426
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"
32 // Unicode Line Separator (\u2028)
33 static const char utf8_linebreak[3] = { 0xe2, 0x80, 0xa8 };
34 #define utf8_linebreak_len 3
38 // Inline
41 Inline::Inline ()
43 SetObjectType (Type::INLINE);
44 font = new TextFontDescription ();
45 downloaders = g_ptr_array_new ();
46 autogen = false;
49 Inline::~Inline ()
51 CleanupDownloaders ();
52 g_ptr_array_free (downloaders, true);
53 delete font;
56 void
57 Inline::CleanupDownloaders ()
59 Downloader *downloader;
60 guint i;
62 for (i = 0; i < downloaders->len; i++) {
63 downloader = (Downloader *) downloaders->pdata[i];
64 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
65 downloader->Abort ();
66 downloader->unref ();
69 g_ptr_array_set_size (downloaders, 0);
72 void
73 Inline::AddFontSource (Downloader *downloader)
75 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
76 g_ptr_array_add (downloaders, downloader);
77 downloader->ref ();
79 if (downloader->Started () || downloader->Completed ()) {
80 if (downloader->Completed ())
81 DownloaderComplete (downloader);
82 } else {
83 // This is what actually triggers the download
84 downloader->Send ();
88 void
89 Inline::AddFontResource (const char *resource)
91 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
92 Application *application = Application::GetCurrent ();
93 Downloader *downloader;
94 Surface *surface;
95 char *path;
96 Uri *uri;
98 uri = new Uri ();
100 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
101 if ((surface = GetSurface ()) && (downloader = surface->CreateDownloader ())) {
102 downloader->Open ("GET", resource, FontPolicy);
103 AddFontSource (downloader);
104 downloader->unref ();
107 delete uri;
109 return;
112 manager->AddResource (resource, path);
113 g_free (path);
114 delete uri;
117 void
118 Inline::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
120 if (args->GetProperty ()->GetOwnerType () != Type::INLINE) {
121 DependencyObject::OnPropertyChanged (args, error);
122 return;
125 if (args->GetId () == Inline::FontFamilyProperty) {
126 FontFamily *family = args->GetNewValue () ? args->GetNewValue ()->AsFontFamily () : NULL;
127 char **families, *fragment;
128 int i;
130 CleanupDownloaders ();
132 if (family && family->source) {
133 families = g_strsplit (family->source, ",", -1);
134 for (i = 0; families[i]; i++) {
135 g_strstrip (families[i]);
136 if ((fragment = strchr (families[i], '#'))) {
137 // the first portion of this string is the resource name...
138 *fragment = '\0';
139 AddFontResource (families[i]);
142 g_strfreev (families);
146 NotifyListenersOfPropertyChange (args, error);
149 void
150 Inline::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
152 if (prop && prop->GetId () == Inline::ForegroundProperty) {
153 // this isn't exactly what we want, I don't
154 // think... but it'll have to do.
155 NotifyListenersOfPropertyChange (prop, NULL);
156 } else {
157 DependencyObject::OnSubPropertyChanged (prop, obj, subobj_args);
161 bool
162 Inline::Equals (Inline *item)
164 const char *lang0, *lang1;
166 if (item->GetObjectType () != GetObjectType ())
167 return false;
169 if (*item->GetFontFamily () != *GetFontFamily ())
170 return false;
172 if (item->GetFontSize () != GetFontSize ())
173 return false;
175 if (item->GetFontStyle () != GetFontStyle ())
176 return false;
178 if (item->GetFontWeight () != GetFontWeight ())
179 return false;
181 if (item->GetFontStretch () != GetFontStretch ())
182 return false;
184 if (item->GetTextDecorations () != GetTextDecorations ())
185 return false;
187 lang0 = item->GetLanguage ();
188 lang1 = GetLanguage ();
190 if ((lang0 && !lang1) || (!lang0 && lang1))
191 return false;
193 if (lang0 && lang1 && strcmp (lang0, lang1) != 0)
194 return false;
196 // this isn't really correct - we should be checking
197 // the innards of the foreground brushes, but we're
198 // guaranteed to never have a false positive here.
199 if (item->GetForeground () != GetForeground ())
200 return false;
202 // OK, as best we can tell - they are equal
203 return true;
206 bool
207 Inline::UpdateFontDescription (const char *source, bool force)
209 FontFamily *family = GetFontFamily ();
210 bool changed = false;
212 if (font->SetSource (source))
213 changed = true;
215 if (font->SetFamily (family ? family->source : NULL))
216 changed = true;
218 if (font->SetStretch (GetFontStretch ()->stretch))
219 changed = true;
221 if (font->SetWeight (GetFontWeight ()->weight))
222 changed = true;
224 if (font->SetStyle (GetFontStyle ()->style))
225 changed = true;
227 if (font->SetSize (GetFontSize ()))
228 changed = true;
230 if (font->SetLanguage (GetLanguage ()))
231 changed = true;
233 if (force) {
234 font->Reload ();
235 return true;
238 return changed;
241 void
242 Inline::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
244 ((Inline *) closure)->DownloaderComplete ((Downloader *) sender);
247 void
248 Inline::DownloaderComplete (Downloader *downloader)
250 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
251 char *resource, *filename;
252 InternalDownloader *idl;
253 const char *path;
254 Uri *uri;
256 // get the downloaded file path (enforces a mozilla workaround for files smaller than 64k)
257 if (!(filename = downloader->GetDownloadedFilename (NULL)))
258 return;
260 g_free (filename);
262 if (!(idl = downloader->GetInternalDownloader ()))
263 return;
265 if (!(idl->GetObjectType () == Type::FILEDOWNLOADER))
266 return;
268 uri = downloader->GetUri ();
270 // If the downloaded file was a zip file, this'll get the path to the
271 // extracted zip directory, else it will simply be the path to the
272 // downloaded file.
273 if (!(path = ((FileDownloader *) idl)->GetUnzippedPath ()))
274 return;
276 resource = uri->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
277 manager->AddResource (resource, path);
278 g_free (resource);
283 // Run
286 bool
287 Run::Equals (Inline *item)
289 const char *text, *itext;
291 if (!Inline::Equals (item))
292 return false;
294 itext = ((Run *) item)->GetText ();
295 text = GetText ();
297 // compare the text
298 if (text && itext && strcmp (text, itext) != 0)
299 return false;
300 else if ((text && !itext) || (!text && itext))
301 return false;
303 return true;
308 // TextBlock
311 TextBlock::TextBlock ()
313 SetObjectType (Type::TEXTBLOCK);
315 font = new TextFontDescription ();
317 downloaders = g_ptr_array_new ();
318 layout = new TextLayout ();
319 font_source = NULL;
320 source = NULL;
322 actual_height = 0.0;
323 actual_width = 0.0;
324 setvalue = true;
325 was_set = false;
326 dirty = true;
329 TextBlock::~TextBlock ()
331 CleanupDownloaders (true);
332 g_ptr_array_free (downloaders, true);
334 delete layout;
335 delete font;
338 void
339 TextBlock::CleanupDownloaders (bool all)
341 Downloader *downloader;
342 guint i;
344 for (i = 0; i < downloaders->len; i++) {
345 downloader = (Downloader *) downloaders->pdata[i];
347 if (all || downloader != source) {
348 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
349 downloader->Abort ();
350 downloader->unref ();
354 g_ptr_array_set_size (downloaders, 0);
356 if (source && !all) {
357 g_ptr_array_add (downloaders, source);
358 } else {
359 source = NULL;
362 if (all) {
363 g_free (font_source);
364 font_source = NULL;
368 void
369 TextBlock::AddFontSource (Downloader *downloader)
371 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
372 g_ptr_array_add (downloaders, downloader);
373 downloader->ref ();
375 if (downloader->Started () || downloader->Completed ()) {
376 if (downloader->Completed ())
377 DownloaderComplete (downloader);
378 } else {
379 // This is what actually triggers the download
380 downloader->Send ();
384 void
385 TextBlock::SetFontSource (Downloader *downloader)
387 CleanupDownloaders (true);
388 source = downloader;
390 if (downloader) {
391 font_source = downloader->GetUri ()->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
392 AddFontSource (downloader);
393 return;
396 UpdateFontDescriptions (true);
397 UpdateBounds (true);
398 Invalidate ();
399 dirty = true;
402 void
403 TextBlock::AddFontResource (const char *resource)
405 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
406 Application *application = Application::GetCurrent ();
407 Downloader *downloader;
408 Surface *surface;
409 char *path;
410 Uri *uri;
412 uri = new Uri ();
414 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
415 if ((surface = GetSurface ()) && (downloader = surface->CreateDownloader ())) {
416 downloader->Open ("GET", resource, FontPolicy);
417 AddFontSource (downloader);
418 downloader->unref ();
421 delete uri;
423 return;
426 manager->AddResource (resource, path);
427 g_free (path);
428 delete uri;
431 void
432 TextBlock::Render (cairo_t *cr, Region *region, bool path_only)
434 cairo_save (cr);
435 cairo_set_matrix (cr, &absolute_xform);
437 if (!path_only)
438 RenderLayoutClip (cr);
440 Paint (cr);
442 cairo_restore (cr);
445 void
446 TextBlock::ComputeBounds ()
448 Size actual (GetActualWidth (), GetActualHeight ());
449 Size framework = ApplySizeConstraints (actual);
451 framework = framework.Max (actual);
453 Rect extents = Rect (0,0,framework.width, framework.height);
455 bounds = bounds_with_children = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
458 void
459 TextBlock::GetSizeForBrush (cairo_t *cr, double *width, double *height)
461 *width = actual_width;
462 *height = actual_height;
465 Point
466 TextBlock::GetTransformOrigin ()
468 Point *user_xform_origin = GetRenderTransformOrigin ();
469 return Point (actual_width * user_xform_origin->x,
470 actual_height * user_xform_origin->y);
473 Size
474 TextBlock::ComputeActualSize ()
476 Thickness padding = *GetPadding ();
477 Size result = FrameworkElement::ComputeActualSize ();
479 if (!LayoutInformation::GetPreviousConstraint (this)) {
480 Size constraint = Size (INFINITY, INFINITY);
482 constraint = ApplySizeConstraints (constraint);
484 constraint = constraint.GrowBy (-padding);
485 Layout (constraint);
488 result = Size (actual_width, actual_height);
489 result = result.GrowBy (padding);
491 return result;
494 Size
495 TextBlock::MeasureOverride (Size availableSize)
497 Thickness padding = *GetPadding ();
498 Size constraint;
499 Size desired;
501 constraint = availableSize.GrowBy (-padding);
502 Layout (constraint);
504 desired = Size (actual_width, actual_height).GrowBy (padding);
506 SetActualHeight (desired.height);
507 SetActualWidth (desired.width);
509 if (GetUseLayoutRounding ())
510 desired.width = ceil (desired.width);
512 desired = desired.Min (availableSize);
514 return desired;
517 Size
518 TextBlock::ArrangeOverride (Size finalSize)
520 Thickness padding = *GetPadding ();
521 Size constraint;
522 Size arranged;
524 constraint = finalSize.GrowBy (-padding);
525 Layout (constraint);
527 arranged = Size (actual_width, actual_height).GrowBy (padding);
529 arranged = arranged.Max (finalSize);
530 arranged = ApplySizeConstraints (arranged);
532 layout->SetAvailableWidth (arranged.GrowBy (-padding).width);
534 return arranged;
537 void
538 TextBlock::UpdateLayoutAttributes ()
540 InlineCollection *inlines = GetInlines ();
541 TextLayoutAttributes *attrs;
542 const char *text;
543 int length = 0;
544 Inline *item;
545 List *runs;
547 InvalidateMeasure ();
548 InvalidateArrange ();
549 runs = new List ();
551 UpdateFontDescription (false);
553 if (inlines != NULL) {
554 for (int i = 0; i < inlines->GetCount (); i++) {
555 item = inlines->GetValueAt (i)->AsInline ();
556 item->UpdateFontDescription (font_source, false);
558 switch (item->GetObjectType ()) {
559 case Type::RUN:
560 text = ((Run *) item)->GetText ();
562 if (text && text[0]) {
563 attrs = new TextLayoutAttributes ((ITextAttributes *) item, length);
564 runs->Append (attrs);
566 length += strlen (text);
569 break;
570 case Type::LINEBREAK:
571 attrs = new TextLayoutAttributes ((ITextAttributes *) item, length);
572 runs->Append (attrs);
574 length += utf8_linebreak_len;
575 break;
576 default:
577 break;
581 if (inlines->GetCount () > 0)
582 was_set = true;
585 layout->SetText (GetText (), length);
586 layout->SetTextAttributes (runs);
589 bool
590 TextBlock::UpdateFontDescription (bool force)
592 FontFamily *family = GetFontFamily ();
593 bool changed = false;
595 if (font->SetSource (font_source))
596 changed = true;
598 if (font->SetFamily (family ? family->source : NULL))
599 changed = true;
601 if (font->SetStretch (GetFontStretch ()->stretch))
602 changed = true;
604 if (font->SetWeight (GetFontWeight ()->weight))
605 changed = true;
607 if (font->SetStyle (GetFontStyle ()->style))
608 changed = true;
610 if (font->SetSize (GetFontSize ()))
611 changed = true;
613 if (font->SetLanguage (GetLanguage ()))
614 changed = true;
616 if (force) {
617 font->Reload ();
618 changed = true;
621 if (changed)
622 layout->SetBaseFont (font->GetFont ());
624 return changed;
627 bool
628 TextBlock::UpdateFontDescriptions (bool force)
630 InlineCollection *inlines = GetInlines ();
631 bool changed = false;
632 Inline *item;
634 changed = UpdateFontDescription (force);
636 if (inlines != NULL) {
637 for (int i = 0; i < inlines->GetCount (); i++) {
638 item = inlines->GetValueAt (i)->AsInline ();
639 if (item->UpdateFontDescription (font_source, force))
640 changed = true;
643 if (changed)
644 layout->ResetState ();
647 if (changed) {
648 InvalidateMeasure ();
649 InvalidateArrange ();
650 UpdateBounds (true);
651 dirty = true;
654 return changed;
657 void
658 TextBlock::Layout (Size constraint)
660 if (was_set && !GetValueNoDefault (TextBlock::TextProperty)) {
661 // If the Text property had been set once upon a time,
662 // but is currently empty, Silverlight seems to set
663 // the ActualHeight property to the font height. See
664 // bug #405514 for details.
665 TextFontDescription *desc = new TextFontDescription ();
666 FontFamily *family = GetFontFamily ();
667 TextFont *font;
669 desc->SetFamily (family ? family->source : NULL);
670 desc->SetStretch (GetFontStretch ()->stretch);
671 desc->SetWeight (GetFontWeight ()->weight);
672 desc->SetStyle (GetFontStyle ()->style);
673 desc->SetSize (GetFontSize ());
675 font = desc->GetFont ();
676 actual_height = font->Height ();
677 actual_width = 0.0;
678 delete desc;
679 } else if (!was_set) {
680 // If the Text property has never been set, then its
681 // extents should both be 0.0. See bug #435798 for
682 // details.
683 actual_height = 0.0;
684 actual_width = 0.0;
685 } else {
686 layout->SetMaxWidth (constraint.width);
687 layout->Layout ();
689 layout->GetActualExtents (&actual_width, &actual_height);
692 dirty = false;
695 void
696 TextBlock::Paint (cairo_t *cr)
698 Thickness *padding = GetPadding ();
699 Point offset (padding->left, padding->top);
701 cairo_set_matrix (cr, &absolute_xform);
702 layout->Render (cr, GetOriginPoint (), offset);
704 if (moonlight_flags & RUNTIME_INIT_SHOW_TEXTBOXES) {
705 cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 1.0);
706 cairo_set_line_width (cr, 1);
707 cairo_rectangle (cr, padding->left, padding->top, actual_width, actual_height);
708 cairo_stroke (cr);
712 char *
713 TextBlock::GetTextInternal (InlineCollection *inlines)
715 const char *text;
716 GString *block;
717 Inline *item;
718 char *str;
720 if (!inlines)
721 return g_strdup ("");
723 block = g_string_new ("");
725 for (int i = 0; i < inlines->GetCount (); i++) {
726 item = inlines->GetValueAt (i)->AsInline ();
728 switch (item->GetObjectType ()) {
729 case Type::RUN:
730 text = ((Run *) item)->GetText ();
732 if (text && text[0])
733 g_string_append (block, text);
734 break;
735 case Type::LINEBREAK:
736 g_string_append_len (block, utf8_linebreak, utf8_linebreak_len);
737 break;
738 default:
739 break;
743 str = block->str;
744 g_string_free (block, false);
746 return str;
749 void
750 TextBlock::SetTextInternal (const char *text)
752 InlineCollection *inlines;
753 Value *value;
754 Run *run;
756 // Note: calling GetValue() may cause the InlineCollection to be
757 // autocreated, so we need to prevent reentrancy here.
758 setvalue = false;
760 value = GetValue (TextBlock::InlinesProperty);
761 inlines = value->AsInlineCollection ();
762 inlines->Clear ();
764 if (text) {
765 run = new Run ();
766 run->SetAutogenerated (true);
767 run->SetText (text);
768 inlines->Add (run);
769 run->unref ();
770 } else {
771 // setting text to null results in String.Empty
772 SetValue (TextBlock::TextProperty, Value (""));
775 setvalue = true;
778 void
779 TextBlock::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
781 bool invalidate = true;
783 if (args->GetProperty ()->GetOwnerType () != Type::TEXTBLOCK) {
784 FrameworkElement::OnPropertyChanged (args, error);
786 if (args->GetId () == FrameworkElement::LanguageProperty) {
787 // a change in xml:lang might change font characteristics
788 if (UpdateFontDescriptions (false)) {
789 InvalidateMeasure ();
790 InvalidateArrange ();
791 UpdateBounds (true);
792 dirty = true;
797 if (args->GetId () == FrameworkElement::WidthProperty) {
798 //if (layout->SetMaxWidth (args->GetNewValue()->AsDouble ()))
799 // dirty = true;
801 UpdateBounds (true);
804 return;
807 if (args->GetId () == TextBlock::FontFamilyProperty) {
808 FontFamily *family = args->GetNewValue () ? args->GetNewValue ()->AsFontFamily () : NULL;
809 char **families, *fragment;
810 int i;
812 CleanupDownloaders (false);
814 if (family && family->source) {
815 families = g_strsplit (family->source, ",", -1);
816 for (i = 0; families[i]; i++) {
817 g_strstrip (families[i]);
818 if ((fragment = strchr (families[i], '#'))) {
819 // the first portion of this string is the resource name...
820 *fragment = '\0';
821 AddFontResource (families[i]);
824 g_strfreev (families);
827 if (UpdateFontDescriptions (false))
828 dirty = true;
829 } else if (args->GetId () == TextBlock::FontSizeProperty) {
830 if (UpdateFontDescriptions (false))
831 dirty = true;
832 } else if (args->GetId () == TextBlock::FontStretchProperty) {
833 if (UpdateFontDescriptions (false))
834 dirty = true;
835 } else if (args->GetId () == TextBlock::FontStyleProperty) {
836 if (UpdateFontDescriptions (false))
837 dirty = true;
838 } else if (args->GetId () == TextBlock::FontWeightProperty) {
839 if (UpdateFontDescriptions (false))
840 dirty = true;
841 } else if (args->GetId () == TextBlock::TextProperty) {
842 if (setvalue) {
843 // result of a change to the TextBlock.Text property
844 const char *text = args->GetNewValue() ? args->GetNewValue()->AsString () : NULL;
846 SetTextInternal (text);
847 UpdateLayoutAttributes ();
848 dirty = true;
849 } else {
850 // result of a change to the TextBlock.Inlines property
851 UpdateLayoutAttributes ();
852 invalidate = false;
854 } else if (args->GetId () == TextBlock::TextDecorationsProperty) {
855 dirty = true;
856 } else if (args->GetId () == TextBlock::TextWrappingProperty) {
857 dirty = layout->SetTextWrapping ((TextWrapping) args->GetNewValue()->AsInt32 ());
858 } else if (args->GetId () == TextBlock::InlinesProperty) {
859 if (setvalue) {
860 // result of a change to the TextBlock.Inlines property
861 InlineCollection *inlines = args->GetNewValue() ? args->GetNewValue()->AsInlineCollection () : NULL;
863 setvalue = false;
864 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
865 setvalue = true;
867 UpdateLayoutAttributes ();
868 dirty = true;
869 } else {
870 // this should be the result of Inlines being autocreated
871 UpdateLayoutAttributes ();
872 invalidate = false;
874 } else if (args->GetId () == TextBlock::LineStackingStrategyProperty) {
875 dirty = layout->SetLineStackingStrategy ((LineStackingStrategy) args->GetNewValue()->AsInt32 ());
876 } else if (args->GetId () == TextBlock::LineHeightProperty) {
877 dirty = layout->SetLineHeight (args->GetNewValue()->AsDouble ());
878 } else if (args->GetId () == TextBlock::TextAlignmentProperty) {
879 dirty = layout->SetTextAlignment ((TextAlignment) args->GetNewValue()->AsInt32 ());
880 } else if (args->GetId () == TextBlock::PaddingProperty) {
881 dirty = true;
882 } else if (args->GetId () == TextBlock::FontSourceProperty) {
883 FontSource *source = args->GetNewValue () ? args->GetNewValue ()->AsFontSource () : NULL;
884 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
886 // FIXME: ideally we'd remove the old item from the cache (or,
887 // rather, 'unref' it since some other textblocks/boxes might
888 // still be using it).
890 g_free (font_source);
892 if (source && source->stream)
893 font_source = manager->AddResource (source->stream);
894 else
895 font_source = NULL;
897 UpdateFontDescriptions (true);
898 dirty = true;
901 if (invalidate) {
902 if (dirty) {
903 InvalidateMeasure ();
904 InvalidateArrange ();
905 UpdateBounds (true);
908 Invalidate ();
911 NotifyListenersOfPropertyChange (args, error);
914 void
915 TextBlock::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
917 if (prop && prop->GetId () == TextBlock::ForegroundProperty) {
918 Invalidate ();
919 } else {
920 FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
924 void
925 TextBlock::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
927 InlineCollection *inlines = GetInlines ();
929 if (col != inlines) {
930 FrameworkElement::OnCollectionChanged (col, args);
931 return;
934 if (args->GetChangedAction () == CollectionChangedActionClearing)
935 return;
937 if (!setvalue) {
938 // changes being handled elsewhere...
939 return;
942 setvalue = false;
943 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
944 setvalue = true;
946 UpdateLayoutAttributes ();
947 InvalidateMeasure ();
948 InvalidateArrange ();
949 UpdateBounds (true);
950 Invalidate ();
953 void
954 TextBlock::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
956 InlineCollection *inlines = GetInlines ();
958 if (col != inlines) {
959 FrameworkElement::OnCollectionItemChanged (col, obj, args);
960 return;
963 if (args->GetId () != Inline::ForegroundProperty) {
964 if (args->GetId () == Run::TextProperty) {
965 // update our TextProperty
966 setvalue = false;
967 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
968 setvalue = true;
970 UpdateLayoutAttributes ();
971 } else {
972 // likely a font property change...
973 ((Inline *) obj)->UpdateFontDescription (font_source, true);
976 // All non-Foreground property changes require
977 // recalculating layout which can change the bounds.
978 InvalidateMeasure ();
979 InvalidateArrange ();
980 UpdateBounds (true);
981 dirty = true;
982 } else {
983 // A simple Foreground brush change does not require
984 // recalculating layout. Invalidate() and we're done.
987 Invalidate ();
990 void
991 TextBlock::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
993 ((TextBlock *) closure)->DownloaderComplete ((Downloader *) sender);
996 void
997 TextBlock::DownloaderComplete (Downloader *downloader)
999 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
1000 char *resource, *filename;
1001 InternalDownloader *idl;
1002 const char *path;
1003 Uri *uri;
1005 dirty = true;
1006 InvalidateMeasure ();
1007 InvalidateArrange ();
1009 // get the downloaded file path (enforces a mozilla workaround for files smaller than 64k)
1010 if (!(filename = downloader->GetDownloadedFilename (NULL)))
1011 return;
1013 g_free (filename);
1015 if (!(idl = downloader->GetInternalDownloader ()))
1016 return;
1018 if (!(idl->GetObjectType () == Type::FILEDOWNLOADER))
1019 return;
1021 uri = downloader->GetUri ();
1023 // If the downloaded file was a zip file, this'll get the path to the
1024 // extracted zip directory, else it will simply be the path to the
1025 // downloaded file.
1026 if (!(path = ((FileDownloader *) idl)->GetUnzippedPath ()))
1027 return;
1029 resource = uri->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
1030 manager->AddResource (resource, path);
1031 g_free (resource);
1033 if (UpdateFontDescriptions (true)) {
1034 dirty = true;
1036 UpdateBounds (true);
1037 Invalidate ();