Sunday 19 October 2014

ISO 8601 week numbering in visual basic 6


I wrote this tiny code snippet to calculate the number of the week according to the ISO 8601 standard.

While almost surely I did not follow the best algorithm available, the code is easy to understand and arises from a couple of wikipedia articles.

   1 Private Function Iso8601WeekNumber(Data As Date) As Integer
   2 
   3     Dim Years53Weeks As String
   4     Dim NumOfWeekFstJan As Integer
   5     Dim WDiff As Integer    
   6            
   7     'here I determine which day of the week is the first of January, see-> http://en.wikipedia.org/wiki/Seven-day_week#Week_numbering 
   8     If Weekday(CDate("01/01/" & year(Data)), vbMonday) = 1 Or _
   9         Weekday(CDate("01/01/" & year(Data)), vbMonday) = 2 Or _
  10          Weekday(CDate("01/01/" & year(Data)), vbMonday) = 3 Or _
  11           Weekday(CDate("01/01/" & year(Data)), vbMonday) = 4 Then
  12           NumOfWeekFstJan = 1
  13     ElseIf Weekday(CDate("01/01/" & year(Data)), vbMonday) = 5 Then
  14                   NumOfWeekFstJan = 53
  15     ElseIf Weekday(CDate("01/01/" & year(Data)), vbMonday) = 7 Then
  16              NumOfWeekFstJan = 52
  17     ElseIf YearHas53Weeks(year(Data) - 1) Then
  18         NumOfWeekFstJan = 53
  19     Else
  20         NumOfWeekFstJan = 52
  21     End If
  22       
  23     Iso8601WeekNumber = NumOfWeekFstJan
  24     
  25     ' number of weeks from Data and the first of January
  26     WDiff = DateDiff("ww", CDate("01/01/" & year(Data)), Data, vbMonday)
  27      
  28     If WDiff > 0 Then
  29         If Iso8601WeekNumber = 1 Then
  30             Iso8601WeekNumber = Iso8601WeekNumber + WDiff
  31         Else ' first of January is within week # 52 0r 53
  32             If WDiff > 0 Then
  33                 Iso8601WeekNumber = WDiff
  34             End If
  35         End If
  36     End If
  37     
  38     If Iso8601WeekNumber = 53 And Month(Data) > 8 And YearHas53Weeks(year(Data)) = False Then
  39         Iso8601WeekNumber = 1
  40     End If    
  41 
  42 End Function
  43 
  44 Private Function YearHas53Weeks(year As Integer) As Boolean
  45     
  46     YearHas53Weeks = False
  47     
  48     If Weekday(CDate("01/01/" & year), vbMonday) = 4 Then
  49         YearHas53Weeks = True
  50     ElseIf Weekday(CDate("01/01/" & year), vbMonday) = 3 And isLeapYear(CInt(year)) Then
  51         YearHas53Weeks = True
  52     End If
  53 
  54 End Function
  55 
  56 'http://www.codeproject.com/Questions/337216/leap-year-problem-in-visual-basic
  57 Function isLeapYear(year As Integer)
  58     isLeapYear = (year Mod 4 = 0 And year Mod 100 <> 0) Or year Mod 400 = 0
  59 End Function
  60 

In the lines 8-21 the routine determines the week number of the first of January. From Wikipedia we read that if the year begins on Monday,Tuesday, Wednesday or Thursay the first of January is in the week number 1, if it begins on Friday the first of January is in the week number 53, if it begins on Sunday the first of January is in the week number 52 and if it begins on Saturday the first of January is in the week number 52 or 53 depending on how many weeks the year before has got.

The function YearHas53Weeks tell us how many weeks are inside an year. The code simply says :
If the year starts on a Thursday or is a leap year that starts on a Wednesday, that year will have 53 weeks.
In the 26-th line I use a VB function to get the number of weeks from the first of January and the date passed as parameter and store the value in the WDiff variable.

Then the code makes some checks, basically it adds WDiff to the week number of the first of January but of course if the 01/01 is on the the 52th or 53th week of the previous year we must add WDiff less 1.

The last check (lines 38-40) states that if we got 53 as the result of the last calculation ad the date that we passed as input is at the end of the year, well that 53 is acceptable only if the year contains 53 week and so, if  it this was not the case, the 53-th becomes the first of the next year.

Monday 6 October 2014

Install programmatically an Android .apk file from qt 5 (liveupdate).

A few days ago I managed to install an .apk file from qt using the Java code I found here http://stackoverflow.com/questions/4967669/android-install-apk-programmatically

I modified the code just to use it within qt 5 using the QAndroidJniObject API which allows to interface your code with Java.

Here it is the resulting code snippet:

   1 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");   
   2 if (activity.isValid()){
   3     QAndroidJniObject kindOfActivity = QAndroidJniObject::fromString(QLatin1String("android.intent.action.VIEW"));
   4     QAndroidJniObject apkFile = QAndroidJniObject::fromString(QLatin1String("file:///pathToApk/new.apk"));
   5     QAndroidJniObject mimetype = QAndroidJniObject::fromString(QLatin1String("application/vnd.android.package-archive"));
   6     QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String;)V",kindOfActivity.object());
   7     QAndroidJniObject myUri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri","parse","(Ljava/lang/String;)Landroid/net/Uri;",apkFile.object());
   8     intent = intent.callObjectMethod("setDataAndType","(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/Intent;",myUri.object(),mimetype.object());
   9     intent = intent.callObjectMethod("setFlags","(I)Landroid/content/Intent;",0x10000000);
  10     activity.callObjectMethod("startActivity","(Landroid/content/Intent;)V",intent.object());
  11 }

The value 0x10000000  corresponds to the FLAG_ACTIVITY_NEW_TASK constant of the Intent class.

The usage of the QAndroidJniObject class and how to correctly submit the method and parameters signatures are explained here http://qt-project.org/doc/qt-5/qandroidjniobject.html

The lines of code like this:
QAndroidJniObject kindOfActivity = QAndroidJniObject::fromString(QLatin1String("android.intent.action.VIEW"));
 are used to build Java Objects from the correponding Qt counterparts (QString to String in this example).

To use these objects as parameters to any Java method, you must call the .object() method of the QAndroidJniObject class as in
QAndroidJniObject myUri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri","parse","(Ljava/lang/String;)Landroid/net/Uri;",apkFile.object());

Saturday 16 March 2013

wpf: datagrid with coloured bars for groupings and how to sort groups by items count inside the datagrid itself

In this post I'll show how to get a grouped datagrid with more than one group and with a template differently coloured for each group and how to sort each group and subgroup by the number of  items.

As usual I got inspiration from other sites and programmers ;-)


The final result will look like this screenshot:



First let's load an ItemsSource for the datagrid. I used a ListCollectionView from business objects of mine.
Then I add the sort descriptions to group my collection by three different properties.

   1 public MainWindow()
   2         {
   3             InitializeComponent();
   4 
   5             // create a List<MyBusinessObject>
   6             List<MyBusinessObject> MyObjects = new List<MyBusinessObject>();
   7 
   8             MyObjects.Add(new MyBusinessObject() { Age = 13, FirstName = "walter", Leaf = "3", Main = "A", Section = "Z" });
   9             MyObjects.Add(new MyBusinessObject() { Age = 45, FirstName = "gianni", Leaf = "3", Main = "A", Section = "Z" });
  10             MyObjects.Add(new MyBusinessObject() { Age = 23, FirstName = "anthony", Leaf = "4", Main = "A", Section = "Z" });
  11             MyObjects.Add(new MyBusinessObject() { Age = 56, FirstName = "michael", Leaf = "4", Main = "A", Section = "Z" });
  12             MyObjects.Add(new MyBusinessObject() { Age = 62, FirstName = "frank", Leaf = "4", Main = "A", Section = "Z" });
  13             MyObjects.Add(new MyBusinessObject() { Age = 23, FirstName = "anthony", Leaf = "1", Main = "B", Section = "Z" });
  14             MyObjects.Add(new MyBusinessObject() { Age = 56, FirstName = "michael", Leaf = "2", Main = "B", Section = "Z" });
  15             MyObjects.Add(new MyBusinessObject() { Age = 2, FirstName = "parsifal", Leaf = "2", Main = "B", Section = "Z" });
  16             MyObjects.Add(new MyBusinessObject() { Age = 76, FirstName = "joseph", Leaf = "9", Main = "B", Section = "Z" });
  17             MyObjects.Add(new MyBusinessObject() { Age = 90, FirstName = "carl", Leaf = "12", Main = "B", Section = "V" });
  18             MyObjects.Add(new MyBusinessObject() { Age = 41, FirstName = "fred", Leaf = "12", Main = "B", Section = "V" });
  19             MyObjects.Add(new MyBusinessObject() { Age = 36, FirstName = "brad", Leaf = "7", Main = "C", Section = "V" });
  20         
  21             // create and link the datacontext
  22             MyDataContext = new DT();
  23             this.DataContext = MyDataContext;
  24 
  25             // create ListCollectionView
  26             ListCollectionView GroupedCollection = new ListCollectionView(MyObjects);
  27 
  28             // add the groupings
  29             GroupedCollection.GroupDescriptions.Add(new PropertyGroupDescription("Main"));
  30             GroupedCollection.GroupDescriptions.Add(new PropertyGroupDescription("Section"));
  31             GroupedCollection.GroupDescriptions.Add(new PropertyGroupDescription("Leaf"));

Then I added a value for the CustomSort property to get every group ordered by its number of items.

I got inspiration from this blog post: http://www.mindscapehq.com/blog/index.php/2008/06/19/custom-sorting-wpf-collection-views-and-the-wpf-property-grid/

And then I added the ListCollectionView to my Data Context to get it binded by the datagrid in the xaml:

   1             // add the CustomSort
   2             GroupedCollection.CustomSort = new GroupedItemsSorter(MyObjects, ListSortDirection.Descending);
   3 
   4             // add the ItemsSource
   5             MyDataContext.MyItemsSource = GroupedCollection;        
   6         }
   7 
   8         private DT MyDataContext;
   9 
  10     }
  11 
  12     public class DT : ViewModelBase
  13     {
  14 
  15         private ListCollectionView myItemsSource;
  16         public ListCollectionView MyItemsSource
  17         {
  18             get
  19             {
  20                 return myItemsSource;
  21             }
  22             set
  23             {
  24                 myItemsSource = value;
  25                 RaisePropertyChanged("MyItemsSource");
  26             }
  27         }
  28 
  29     }

As you can see the CustomSort property is an object of a custom class. I pass to the constructor the Collection and the desired direction for the ordering.

Let's see the code of the custom class:

   1 class GroupedItemsSorter : IComparer
   2     {
   3         public GroupedItemsSorter(List<MyBusinessObject> Items,  ListSortDirection Direction)
   4         {
   5             this.Items = Items;
   6             this.Direction = Direction;
   7         }
   8 
   9         public int Compare(object x, object y)
  10         {
  11             String Main_x = (x as MyBusinessObject).Main;
  12             String Main_y = (y as MyBusinessObject).Main;
  13 
  14             String Section_x = (x as MyBusinessObject).Section;
  15             String Section_y = (y as MyBusinessObject).Section;
  16 
  17             String Leaf_x = (x as MyBusinessObject).Leaf;
  18             String Leaf_y = (y as MyBusinessObject).Leaf;
  19 
  20             int SameMainFor_x = Items.Where(it => it.Main.Equals(Main_x)).Count();
  21             int SameMainFor_y = Items.Where(it => it.Main.Equals(Main_y)).Count();
  22 
  23             if (SameMainFor_x != SameMainFor_y)
  24                 return (Direction == ListSortDirection.Ascending ? 1 : -1) * Math.Sign(SameMainFor_x - SameMainFor_y);
  25 
  26 
  27             int SameSectionFor_x = Items.Where(it => it.Section.Equals(Section_x)).Count();
  28             int SameSectionFor_y = Items.Where(it => it.Section.Equals(Section_y)).Count();
  29 
  30             if (SameSectionFor_x != SameSectionFor_y)
  31                 return (Direction == ListSortDirection.Ascending ? 1 : -1) * Math.Sign(SameSectionFor_x - SameSectionFor_y);
  32 
  33             int SameLeafFor_x = Items.Where(it => it.Leaf.Equals(Leaf_x)).Count();
  34             int SameLeafFor_y = Items.Where(it => it.Leaf.Equals(Leaf_y)).Count();
  35 
  36             if (SameLeafFor_x != SameLeafFor_y)
  37                 return (Direction == ListSortDirection.Ascending ? 1 : -1) * Math.Sign(SameLeafFor_x - SameLeafFor_y);
  38 
  39             return 0;
  40 
  41         }
  42 
  43         private List<MyBusinessObject> Items { get; set; }
  44         private ListSortDirection Direction { get; set; }
  45     }

First the class must implement the IComparer interface. The Collection uses the method Compare to know which must come first between two objects. The method must return an integer ,1,-1 or 0 respectively if x is greater,  smaller or equal to y.

I implemented the Compare method in this way. I count how many items share the same value for the property 'Main', that is the first property I have grouped by, for the objectx x and y.

Than if the two counts are different the greatest object of the two is the one who have the greatest count.
And so I return the sign of the counts difference multiplied by the desired Direction (ascending or descending).

If the two counts are equal I repeat the same code for the Section property that is the second property I have grouped by and so on.

With some effort I think it is possible to create a generic comparer for every kind of business object and for any number of grouping properties.

Thi is all concerning the data model. Now let's see the xaml for the datagrid:


   1 <Window x:Class="ColouredGroupedGrid.MainWindow"
   2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:my="clr-namespace:ColouredGroupedGrid"
   4         xmlns:cm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
   5         Title="MainWindow" Height="350" Width="525">
   6 
   7     <Window.Resources>
   8 
   9         <my:ConverterBackgroundHeader x:Key="ConverterBackgroundHeader" />
  10 
  11     </Window.Resources>
  12 
  13     <Grid>
  14 
  15         <Grid>
  16 
  17             <Grid.ColumnDefinitions>
  18                 <ColumnDefinition Width="*"  />
  19             </Grid.ColumnDefinitions>
  20             <Grid.RowDefinitions>
  21                 <RowDefinition Height="*"  />
  22             </Grid.RowDefinitions>
  23 
  24             <DataGrid AutoGenerateColumns="False" Name="dataGrid1" 
  25                       ScrollViewer.CanContentScroll="True" 
  26                       ScrollViewer.VerticalScrollBarVisibility="Auto"
  27                       ScrollViewer.HorizontalScrollBarVisibility="Auto" 
  28                       Foreground="Black"  CanUserAddRows="False" IsReadOnly="True" RowBackground="#FFD3E8E0" 
  29                       AlternatingRowBackground="White" HorizontalScrollBarVisibility="Visible" ItemsSource="{Binding MyItemsSource}"
  30                       VerticalScrollBarVisibility="Visible" SelectionMode="Single" RowHeaderStyle="{DynamicResource DataGridRowHeaderStyle1}"    >
  31                 <DataGrid.GroupStyle>
  32                     <GroupStyle>
  33                         <GroupStyle.ContainerStyle>
  34                             <Style TargetType="{x:Type GroupItem}">
  35                                 <Setter Property="Template">
  36                                     <Setter.Value>
  37                                         <ControlTemplate TargetType="{x:Type GroupItem}">
  38                                             <Expander Margin="5,0,0,0" Background="{Binding .,Converter={StaticResource ConverterBackgroundHeader}}">
  39                                                 <Expander.Header   >
  40                                                     <StackPanel Orientation="Horizontal" >
  41                                                         <TextBlock Text="{Binding Path=Name}" />
  42                                                         <TextBlock Text=" => "/>
  43                                                         <TextBlock Text="{Binding Path=ItemCount}"/>
  44                                                         <TextBlock Text=" items"/>
  45                                                     </StackPanel>
  46                                                 </Expander.Header>
  47                                                 <ItemsPresenter />
  48                                             </Expander>
  49                                         </ControlTemplate>
  50                                     </Setter.Value>
  51                                 </Setter>
  52                             </Style>
  53                         </GroupStyle.ContainerStyle>
  54                     </GroupStyle>
  55                 </DataGrid.GroupStyle>
  56                 <DataGrid.Columns>
  57                     <DataGridTextColumn Header="Main" Binding="{Binding Main}"   />
  58                     <DataGridTextColumn Header="Section" Binding="{Binding Section}"  />
  59                     <DataGridTextColumn Header="Leaf" Binding="{Binding Leaf}"  />
  60                     <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"  />
  61                     <DataGridTextColumn Header="Age" Binding="{Binding Age}"  />
  62                 </DataGrid.Columns>
  63 
  64             </DataGrid>
  65         </Grid>
  66 
  67     </Grid>
  68 </Window>
  69 

Here you can see that I added a template for the GroupStyle property of the datagrid. The template is an expander and I read an example of this here: http://wpftutorial.net/DataGrid.html

I just added a left margin of 5 pixels to get an offset for every child group in the hierarchy.

The Expander background is linked to a converter where I managed to get a different colour for each group in the hierarchy.

Background="{Binding .,Converter={StaticResource ConverterBackgroundHeader}}">

The code for the converter is:


   1 public class ConverterBackgroundHeader : IValueConverter
   2     {
   3 
   4         #region IValueConverter Members
   5 
   6         
   7         public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
   8         {
   9             int level = 0;
  10             
  11             CollectionViewGroup obj = value as CollectionViewGroup;
  12 
  13             if (obj == null) return value;
  14 
  15             while ((obj.Items[0] as CollectionViewGroup) != null)
  16             {
  17                 level++;
  18                 obj = obj.Items[0] as CollectionViewGroup;
  19             }
  20 
  21             BrushConverter bc = new BrushConverter();
  22 
  23             switch (level)
  24             {
  25                 case 2:
  26                     return (Brush)bc.ConvertFrom("#E9E8FF");                   
  27                 case 1:
  28                     return (Brush)bc.ConvertFrom("#E3FFFE");
  29                 case 0:
  30                     return (Brush)bc.ConvertFrom("#F8FFE3");
  31                    
  32             }
  33 
  34             return value;
  35            
  36         }
  37 
  38         // ConvertBack is not implemented for a OneWay binding.
  39         public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  40         {
  41             throw new NotImplementedException();
  42         }
  43 
  44         #endregion
  45     }  

The converter receives a CollectionViewGroup. I need to know at what level of the hierarchy the group is.
Since the last group, the group formed by mean of the last sort description of the ListCollectionView, has one or more instances of my business objects as its Items and since, instead, the other groups have other  CollectionViewGroup objects as their Items, I just iterate through the hierarchy incrementing a variable until I find an instance of my business objects. Then I simply return a different color for each value I find from the while loop.

The last thing I want to show is the RowHeaderStyle="{DynamicResource DataGridRowHeaderStyle1}" used in the datagrid.

   1 <Application x:Class="ColouredGroupedGrid.App"
   2              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4              xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
   5              StartupUri="MainWindow.xaml">
   6     
   7         <Application.Resources>
   8 
   9             <BooleanToVisibilityConverter x:Key="bool2VisibilityConverter"/>
  10             <Style x:Key="RowHeaderGripperStyle" TargetType="{x:Type Thumb}">
  11                 <Setter Property="Height" Value="8"/>
  12                 <Setter Property="Background" Value="Transparent"/>
  13                 <Setter Property="Cursor" Value="SizeNS"/>
  14                 <Setter Property="Template">
  15                     <Setter.Value>
  16                         <ControlTemplate TargetType="{x:Type Thumb}">
  17                             <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/>
  18                         </ControlTemplate>
  19                     </Setter.Value>
  20                 </Setter>
  21             </Style>
  22             <Style x:Key="DataGridRowHeaderStyle1" TargetType="{x:Type DataGridRowHeader}">
  23                 <Setter Property="Template">
  24                     <Setter.Value>
  25                         <ControlTemplate TargetType="{x:Type DataGridRowHeader}">
  26                             <Grid>
  27                                 <Microsoft_Windows_Themes:DataGridHeaderBorder Background="Transparent"  BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0" IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}" IsSelected="{TemplateBinding IsRowSelected}" Orientation="Horizontal" Padding="0" SeparatorBrush="{TemplateBinding SeparatorBrush}" SeparatorVisibility="{TemplateBinding SeparatorVisibility}">
  28                                     <StackPanel Orientation="Horizontal">
  29                                         <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
  30                                         <Control SnapsToDevicePixels="false" Template="{Binding ValidationErrorTemplate, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Visibility="{Binding (Validation.HasError), Converter={StaticResource bool2VisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"/>
  31                                     </StackPanel>
  32                                 </Microsoft_Windows_Themes:DataGridHeaderBorder>
  33                                 <Thumb x:Name="PART_TopHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Top"/>
  34                                 <Thumb x:Name="PART_BottomHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Bottom"/>
  35                             </Grid>
  36                         </ControlTemplate>
  37                     </Setter.Value>
  38                 </Setter>
  39             </Style>
  40 
  41         </Application.Resources>
  42 
  43     
  44 </Application>
  45 

This is just the standard template that comes up with Expression Blend if you tell it to modify the default RowHeaderStyle. I just changed the Background of the DataGridHeaderBorder to Transparent to avoid an annoying border that shows up when you resize the window and the horizontal bar appears.


The complete example is available here.

Tuesday 18 December 2012

Compile gedit from git repository and jhbuild on ubuntu 12.10 64 bit

Here are some tips that helped me to compile gedit on ubuntu 12.10 64 bit.

I wanted to use jhbuild that allows to compile gnome related software (among others)  in separate folders than system ones.

First I read this manual and executed the suggested operations:
http://developer.gnome.org/jhbuild/stable/getting-started.html.en

Then I tried to compile gedit with the command

jhbuild buildone gedit 

but configure complained that these packets were not been installed:

No package 'libxml-2.0' found
No package 'glib-2.0' found
No package 'gio-2.0' found
No package 'gtk+-3.0' found
No package 'gtksourceview-3.0' found


actually they were already installed but jhedit couldn't find them  because I didn't set the PKG_CONFIG_PATH environment variable.

In the PKG_CONFIG_PATH I set the paths where are installed the libraries searched by jhbuild.

To know the name of the package to install or, if it is already installed, to know the path to add to the PKG_CONFIG_PATH variable you can run the command

apt-file search missingfilename.pc 

where missingfilename comes from the configure message error. As an example:

apt-file search gtksourceview-3.0.pc returns
libgtksourceview-3.0-dev: /usr/lib/x86_64-linux-gnu/pkgconfig/gtksourceview-3.0.pc
which means that the package that must be present is libgtksourceview-3.0-dev and that, actually, it is already installed at the path  /usr/lib/x86_64-linux-gnu/pkgconfig/.

At the end I had to set the PKG_CONFIG_PATH variable in the following way:
PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig/:/usr/lib/pkgconfig/:/usr/share/pkgconfig/
export PKG_CONFIG_PATH  


After this I was able to run without errors the command jhbuild buildone gedit.


To run the gedit just compiled from the repository instead of the system one you can run:
 jhbuild run gedit.