AlbumArtDownloader v.1.0.0 released

I just released AlbumArtDownloader version 1.0.0.
It is a command line tool that downloads album art (using the last.fm service) and tags audio files.
It can scan also subfolders, and it can be used to tag a whole music library. ;)

Updating a GitHub fork

To update a GitHub fork with the changes done in the original repository:

  • Add the original source to your fork
    git remote add original git://url-to-original-repo
    You can verify the remotes with the command
    git remote -v
  • Fetch the changes from the original repo
    git fetch original
  • Merge the changes
    git merge original/master
  • Push the changes to your repo
    git push

Wpf and emoticons

How to display emoticons/emoji in a Wpf TextBox/TextBlock?

I made a test project to show how to do this.
It is all about playing with Inlines in the TextBox’s FlowDocument and in the TextBlock.
The main code is this:

private static int FindFirstEmoticon(string text, int startIndex, out string emoticonFound)
  1. {
  2.   InitEmoticons();
  3.   emoticonFound = string.Empty;
  4.   int minIndex = -1;
  5.   foreach (string e in m_Emoticons.Keys) {
  6.     int index = text.IndexOf(e, startIndex);
  7.     if (index >= 0) {
  8.       if (minIndex < 0 || index < minIndex) {
  9.         minIndex = index;
  10.         emoticonFound = e;
  11.       }
  12.     }
  13.   }
  14.   return minIndex;
  15. } // FindFirstEmoticon
  16.  
  17. public static void ParseText(FrameworkElement element)
  18. {
  19.   InitEmoticons();
  20.   TextBlock textBlock = null;
  21.   RichTextBox textBox = element as RichTextBox;
  22.   if (textBox == null)
  23.     textBlock = element as TextBlock;
  24.  
  25.   if (textBox == null && textBlock == null)
  26.     return;
  27.  
  28.   if (textBox != null){
  29.     FlowDocument doc = textBox.Document;
  30.     for (int blockIndex=0; blockIndex < doc.Blocks.Count; blockIndex++){
  31.       Block b = doc.Blocks.ElementAt(blockIndex);
  32.       Paragraph p = b as Paragraph;
  33.       if (p != null) {          
  34.         ProcessInlines(textBox, p.Inlines);
  35.       }
  36.     }
  37.   }else{
  38.     ProcessInlines(null, textBlock.Inlines);
  39.   }
  40. } // ParseText
  41.  
  42. private static void ProcessInlines(RichTextBox textBox, InlineCollection inlines)
  43. {
  44.   for (int inlineIndex=0; inlineIndex < inlines.Count; inlineIndex++){
  45.     Inline i = inlines.ElementAt(inlineIndex);
  46.     if (i is Run) {
  47.       Run r = i as Run;
  48.       string text = r.Text;
  49.       string emoticonFound = string.Empty;
  50.       int index = FindFirstEmoticon(text, 0, out emoticonFound);
  51.       if (index >= 0) {
  52.         TextPointer tp = i.ContentStart;
  53.         bool reposition = false;                
  54.         while (!tp.GetTextInRun(LogicalDirection.Forward).StartsWith(emoticonFound))
  55.           tp = tp.GetNextInsertionPosition(LogicalDirection.Forward);
  56.         TextPointer end = tp;
  57.         for (int j=0; j<emoticonFound.Length; j++)
  58.           end = end.GetNextInsertionPosition(LogicalDirection.Forward);
  59.         TextRange tr = new TextRange(tp, end);
  60.         if (textBox != null)
  61.           reposition = textBox.CaretPosition.CompareTo(tr.End) == 0;
  62.         tr.Text = string.Empty;
  63.  
  64.         string imageFile = m_Emoticons[emoticonFound];
  65.         Image image = new Image();
  66.         BitmapImage bimg = new BitmapImage();
  67.         bimg.BeginInit();
  68.         bimg.UriSource = new Uri(imageFile, UriKind.Relative);
  69.         bimg.EndInit();
  70.         image.Source = bimg;
  71.         image.Width = 24;
  72.  
  73.         InlineUIContainer iui = new InlineUIContainer(image, tp);
  74.         iui.BaselineAlignment = BaselineAlignment.TextBottom;
  75.  
  76.         if (textBox != null && reposition)
  77.           textBox.CaretPosition = tp.GetNextInsertionPosition(LogicalDirection.Forward);
  78.       }
  79.     }
  80.   }
  81. } // ProcessInlines

The code parses the current content of the TextBox/TextBlock and replaces the emoticons text (initialized int the InitEmoticons method) with the correct image.
If the element passed is a TextBox it also repositions the cursor in the correct position.

You can download the test project here.