<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -21,6 +21,11 @@ i18n() calls so that xgettext can parse them.
 
 --context=name    : Give i18n calls a context name: i18nc(&quot;name&quot;, ...)
 --lines           : Include source line numbers in comments (deprecated, it is switched on by default now)
+--cstart=chars    : Start of to-EOL style comments in output, defaults to //
+--language=lang   : Create i18n calls appropriate for KDE bindings
+                    in the given language. Currently known languages:
+                    C++ (default), Python
+--ignore-no-input : Do not warn if there were no filenames specified
 --help|?          : Display this summary
 
 EOF
@@ -33,6 +38,7 @@ EOF
 use strict;
 use warnings;
 use Getopt::Long;
+use Data::Dumper;
 
 use constant TAG_GROUP =&gt; 
 {
@@ -43,6 +49,54 @@ use constant TAG_GROUP =&gt;
 
 use constant TAG_GROUPS =&gt; join &quot;, &quot;, map &quot;'$_'&quot;, sort keys %{&amp;TAG_GROUP};
 
+# Specification to extract nice element-context for strings.
+use constant ECTX_SPEC =&gt;
+{
+  # Data structure: extension =&gt; {tag =&gt; [ctxlevel, [attribute, ...]], ...}
+  # Order of attributes determines their order in the extracted comment.
+  &quot;ui&quot; =&gt; {
+    &quot;widget&quot; =&gt; [10, [&quot;class&quot;, &quot;name&quot;]],
+    &quot;item&quot; =&gt; [15, []],
+    &quot;property&quot; =&gt; [20, [&quot;name&quot;]],
+    &quot;attribute&quot; =&gt; [20, [&quot;name&quot;]],
+  },
+  &quot;rc&quot; =&gt; {
+    &quot;Menu&quot; =&gt; [10, [&quot;name&quot;]],
+    &quot;ToolBar&quot; =&gt; [10, [&quot;name&quot;]],
+  },
+  &quot;kcfg&quot; =&gt; {
+    &quot;group&quot; =&gt; [10, [&quot;name&quot;]],
+    &quot;entry&quot; =&gt; [20, [&quot;name&quot;]],
+    &quot;whatsthis&quot; =&gt; [30, []],
+    &quot;tooltip&quot; =&gt; [30, []],
+    &quot;label&quot; =&gt; [30, []],
+  },
+};
+
+# Specification to exclude strings by trailing section of element-context.
+use constant ECTX_EXCLUDE =&gt;
+[
+    # Data structure: [[tag, attribute, attrvalue], [...]]
+    # Empty (&quot;&quot;) attribute means all elements with given tag,
+    # empty attrvalue means element with given tag and attribute of any value.
+    [[&quot;widget&quot;, &quot;class&quot;, &quot;KFontComboBox&quot;], [&quot;item&quot;, &quot;&quot;, &quot;&quot;], [&quot;property&quot;, &quot;&quot;, &quot;&quot;]],
+    [[&quot;widget&quot;, &quot;class&quot;, &quot;KPushButton&quot;], [&quot;attribute&quot;, &quot;name&quot;, &quot;buttonGroup&quot;]],
+    [[&quot;widget&quot;, &quot;class&quot;, &quot;QRadioButton&quot;], [&quot;attribute&quot;, &quot;name&quot;, &quot;buttonGroup&quot;]],
+];
+
+# The parts between the tags of the extensions will be copied
+# verbatim
+my %EXTENSION_VERBATIM_TAGS = (
+       &quot;kcfg&quot;               =&gt; [ &quot;code&quot; ],
+     );
+
+# Add attribute lists as hashes, for membership checks.
+for my $ext ( keys %{&amp;ECTX_SPEC} ) {
+  for my $tag ( keys %{ECTX_SPEC-&gt;{$ext}} ) {
+    my $arr = ECTX_SPEC-&gt;{$ext}{$tag}[1];
+    ECTX_SPEC-&gt;{$ext}{$tag}[2] = {map {$_ =&gt; 1} @{$arr}};
+  }
+}
 
 ###########################################################################################
 # Add options here as necessary - perldoc Getopt::Long for details on GetOptions
@@ -51,11 +105,14 @@ GetOptions ( &quot;tag=s&quot;       =&gt; \my @opt_extra_tags,
              &quot;tag-group=s&quot; =&gt; \my $opt_tag_group,
              &quot;context=s&quot;   =&gt; \my $opt_context,       # I18N context
              &quot;lines&quot;       =&gt; \my $opt_lines,
+             &quot;cstart=s&quot;    =&gt; \my $opt_cstart,
+             &quot;language=s&quot;  =&gt; \my $opt_language,
+             &quot;ignore-no-input&quot; =&gt; \my $opt_ignore_no_input,
              &quot;help|?&quot;      =&gt; \&amp;usage );
 
 unless( @ARGV )
 {
-  warn &quot;No filename specified&quot;;
+  warn &quot;No filename specified&quot; unless $opt_ignore_no_input;
   exit;
 }
 
@@ -67,7 +124,9 @@ die &quot;Unknown tag group: '$opt_tag_group', should be one of &quot; . TAG_GROUPS
 my $tags = TAG_GROUP-&gt;{$opt_tag_group};
 my $extra_tags  = join &quot;&quot;, map &quot;|&quot; . quotemeta, @opt_extra_tags;
 my $text_string = qr/($tags$extra_tags)( [^&gt;]*)?&gt;/;    # Precompile regexp
-
+my $cstart = $opt_cstart; # no default, selected by language if not given
+my $language = $opt_language || &quot;C++&quot;;
+my $ectx_known_exts = join &quot;|&quot;, keys %{&amp;ECTX_SPEC};
 
 ###########################################################################################
 #  Escape characters in string exactly like uic does.
@@ -88,6 +147,35 @@ sub escape_like_uic ($) {
 }
 
 ###########################################################################################
+
+sub dummy_call_infix {
+    my ($cstart, $stend, $ctxt, $text, @cmnts) = @_;
+    for my $cmnt (@cmnts) {
+        print qq|$cstart $cmnt\n|;
+    }
+    if (defined $text) {
+        $text = escape_like_uic($text);
+        if (defined $ctxt) {
+            $ctxt = escape_like_uic($ctxt);
+            print qq|i18nc(&quot;$ctxt&quot;, &quot;$text&quot;)$stend\n|;
+        } else {
+            print qq|i18n(&quot;$text&quot;)$stend\n|;
+        }
+    }
+}
+
+my %dummy_calls = (
+    &quot;C++&quot; =&gt; sub {
+        dummy_call_infix($cstart || &quot;//&quot;, &quot;;&quot;, @_);
+    },
+    &quot;Python&quot; =&gt; sub {
+        dummy_call_infix($cstart || &quot;#&quot;, &quot;&quot;, @_);
+    },
+);
+
+die &quot;unknown language '$language'&quot; if not defined $dummy_calls{$language};
+my $dummy_call = $dummy_calls{$language};
+
 # Program start proper - NB $. is the current line number
 
 for my $file_name ( @ARGV )
@@ -100,6 +188,15 @@ for my $file_name ( @ARGV )
     next;
   }
 
+  # Ready element-context extraction.
+  my $ectx_ext;
+  my $ectx_string;
+  if ( $file_name =~ /\.($ectx_known_exts)(\.(in|cmake))?$/ ) {
+    $ectx_ext = $1;
+    my $ectx_tag_gr = join &quot;|&quot;, keys %{ECTX_SPEC-&gt;{$ectx_ext}};
+    $ectx_string = qr/($ectx_tag_gr)( [^&gt;]*)?&gt;/; # precompile regexp
+  }
+
   my $string          = &quot;&quot;;
   my $in_text         = 0;
   my $start_line_no   = 0;
@@ -107,6 +204,14 @@ for my $file_name ( @ARGV )
   my $tag = &quot;&quot;;
   my $attr = &quot;&quot;;
   my $context = &quot;&quot;;
+  my $notr = &quot;&quot;;
+
+  # Element-context data: [[level, tag, [[attribute, value], ...]], ...]
+  # such that subarrays are ordered increasing by level.
+  my @ectx = ();
+
+  # All comments to pending dummy call.
+  my @comments = ();
 
   while ( &lt;$fh&gt; )
   {
@@ -129,9 +234,38 @@ for my $file_name ( @ARGV )
      }
 
      $context = $opt_context unless $in_text;
+     $notr = &quot;&quot; unless $in_text;
 
      unless ( $in_skipped_prop or $in_text )
      {
+       # Check if this line contains context-worthy element.
+       if (    $ectx_ext
+           and ( ($tag, $attr) = $string =~ /&lt;$ectx_string/ ) # no /o here
+           and exists ECTX_SPEC-&gt;{$ectx_ext}{$tag} )
+       {
+         my @atts;
+         for my $ectx_att ( @{ECTX_SPEC-&gt;{$ectx_ext}{$tag}[1]} )
+         {
+           if ( $attr and $attr =~ /\b$ectx_att\s*=\s*([&quot;'])([^&quot;']*?)\1/ )
+           {
+             my $aval = $2;
+             push @atts, [$ectx_att, $aval];
+           }
+         }
+         # Kill all tags in element-context with level higer or equal to this,
+         # and add it to the end.
+         my $clevel = ECTX_SPEC-&gt;{$ectx_ext}{$tag}[0];
+         for ( my $i = 0; $i &lt; @ectx; ++$i )
+         {
+           if ( $clevel &lt;= $ectx[$i][0] )
+           {
+             @ectx = @ectx[0 .. ($i - 1)];
+             last;
+           }
+         }
+         push @ectx, [$clevel, $tag, [@atts]];
+       }
+
        if ( ($tag, $attr) = $string =~ /&lt;$text_string/o )
        {
          my ($attr_comment) = $attr =~ /\w*comment=\&quot;([^\&quot;]*)\&quot;/ if $attr;
@@ -141,9 +275,15 @@ for my $file_name ( @ARGV )
          # It is unlikely that both attributes 'context' and 'comment'
          # will be present, but if so happens, 'context' has priority.
 
+         my ($attr_notr) = $attr =~ /\bnotr=\&quot;([^\&quot;]*)\&quot;/ if $attr;
+         $notr = $attr_notr if $attr_notr;
+
          $string        =~ s/^.*&lt;$text_string//so;
-         $in_text       =  1;
-         $start_line_no =  $.;
+         if ( not $attr or $attr !~ /\/ *$/ )
+         {
+           $in_text       =  1;
+           $start_line_no =  $.;
+         }
        }
        else
        {
@@ -159,30 +299,74 @@ for my $file_name ( @ARGV )
 
      if ( $text cmp &quot;&quot; )
      {
-       if ( not $context or $context ne &quot;KDE::DoNotExtract&quot; )
+       # See if the string should be excluded by trailing element-context.
+       my $exclude_by_ectx = 0;
+       my @rev_ectx = reverse @ectx;
+       for my $ectx_tail (@{&amp;ECTX_EXCLUDE})
        {
-         print &quot;//i18n: $file_name:$.\n&quot;;
-         print &quot;// xgettext: no-c-format\n&quot; if $text =~ /%/o;
-         if ( $context )
-         {
-           $context = escape_like_uic($context);
-           $text = escape_like_uic($text);
-           print qq|i18nc(&quot;$context&quot;,&quot;$text&quot;);\n|;
-         }
-         else
+         my @rev_ectx_tail = reverse @{$ectx_tail};
+         my $i = 0;
+         $exclude_by_ectx = (@rev_ectx &gt; 0 and @rev_ectx_tail &gt; 0);
+         while ($i &lt; @rev_ectx and $i &lt; @rev_ectx_tail)
          {
-           $text = escape_like_uic($text);
-           print  qq|i18n(&quot;$text&quot;);\n|;
+           my ($tag, $attr, $aval) = @{$rev_ectx_tail[$i]};
+           $exclude_by_ectx = (not $tag or ($tag eq $rev_ectx[$i][1]));
+           if ($exclude_by_ectx and $attr)
+           {
+             $exclude_by_ectx = 0;
+             for my $ectx_attr_aval (@{$rev_ectx[$i][2]})
+             {
+               if ($attr eq $ectx_attr_aval-&gt;[0])
+               {
+                 $exclude_by_ectx = $aval ? $aval eq $ectx_attr_aval-&gt;[1] : 1;
+                 last;
+               }
+             }
+           }
+           last if not $exclude_by_ectx;
+           ++$i;
          }
+         last if $exclude_by_ectx;
+       }
+
+       if (($context and $context eq &quot;KDE::DoNotExtract&quot;) or ($notr eq &quot;true&quot;))
+       {
+         push @comments, &quot;Manually excluded message at $file_name line $.&quot;;
+       }
+       elsif ( $exclude_by_ectx )
+       {
+         push @comments, &quot;Automatically excluded message at $file_name line $.&quot;;
        }
        else
        {
-         print &quot;// Manually excluded message at $file_name line $.\n&quot;;
+         (my $norm_fname = $file_name) =~ s/^\.\///;
+         push @comments, &quot;i18n: file: $norm_fname:$.&quot;;
+         if ( @ectx ) {
+           # Format element-context.
+           my @tag_gr;
+           for my $tgr (reverse @ectx)
+           {
+             my @attr_gr;
+             for my $agr ( @{$tgr-&gt;[2]} )
+             {
+               #push @attr_gr, &quot;$agr-&gt;[0]=$agr-&gt;[1]&quot;;
+               push @attr_gr, &quot;$agr-&gt;[1]&quot;; # no real nead for attribute name
+             }
+             my $attr = join(&quot;, &quot;, @attr_gr);
+             push @tag_gr, &quot;$tgr-&gt;[1] ($attr)&quot; if $attr;
+             push @tag_gr, &quot;$tgr-&gt;[1]&quot; if not $attr;
+           }
+           my $ectx_str = join &quot;, &quot;, @tag_gr;
+           push @comments, &quot;i18n: ectx: $ectx_str&quot;;
+         }
+         push @comments, &quot;xgettext: no-c-format&quot; if $text =~ /%/o;
+         $dummy_call-&gt;($context, $text, @comments);
+         @comments = ();
        }
      }
      else
      {
-       print &quot;// Skipped empty message at $file_name line $.\n&quot;;
+       push @comments, &quot;Skipped empty message at $file_name line $.&quot;;
      }
 
      $string  =~ s/^.*&lt;\/$text_string//o;
@@ -196,5 +380,42 @@ for my $file_name ( @ARGV )
   close $fh or warn &quot;Failed to close: '$file_name': $!&quot;;
 
   die &quot;parsing error in $file_name&quot; if $in_text;
-}
 
+  if ($ectx_ext &amp;&amp; exists $EXTENSION_VERBATIM_TAGS{$ectx_ext})
+  {
+    unless ( open $fh, &quot;&lt;&quot;, $file_name )
+    {
+      # warn &quot;Failed to open: '$file_name': $!&quot;;
+      next;
+    }
+
+    while ( &lt;$fh&gt; )
+    {
+      chomp;
+      $string .= &quot;\n&quot; . $_;
+
+      foreach $tag (@{ $EXTENSION_VERBATIM_TAGS{$ectx_ext} })
+      {
+        if ($string =~ /&lt;$tag&gt;(.*)&lt;\/$tag&gt;/s)
+        {
+          # Add comment before any line that has an i18n substring in it.
+          my @matched = split /\n/, $1;
+          my $mlno = $.;
+          (my $norm_fname = $file_name) =~ s/^\.\///;
+          for my $mline (@matched) {
+            # Assume verbatim code is in language given by --language.
+            # Therefore format only comment, and write code line as-is.
+            if ($mline =~ /i18n/) {
+              $dummy_call-&gt;(undef, undef, (&quot;i18n: file: $norm_fname:$mlno&quot;));
+            }
+            print &quot;$mline\n&quot;;
+            ++$mlno;
+          }
+          $string = &quot;&quot;;
+        }
+      }
+    }
+
+    close $fh or warn &quot;Failed to close: '$file_name': $!&quot;;
+  }
+}</diff>
      <filename>scripts/extractrc.sh</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>4ef1653dc8dc885227d3b0ba771bcdca54d30037</id>
    </parent>
  </parents>
  <author>
    <name>Geoff Hutchison</name>
    <email>babel@geoffhutchison.net</email>
  </author>
  <url>http://github.com/cryos/avogadro/commit/4bd087b8e37fd5d386bcb169aa57b3227769ed87</url>
  <id>4bd087b8e37fd5d386bcb169aa57b3227769ed87</id>
  <committed-date>2009-10-28T13:18:21-07:00</committed-date>
  <authored-date>2009-10-28T13:18:21-07:00</authored-date>
  <message>Updated script from KDE -- fixes some problems with newer UI files.</message>
  <tree>23a5d115b26a196ff95cdff4b9fe3bfd2c947670</tree>
  <committer>
    <name>Geoff Hutchison</name>
    <email>babel@geoffhutchison.net</email>
  </committer>
</commit>
