Change background color for content assist pop-ups in Eclipse on Ubuntu

The default grayish tone that is set for selection background in TreeViews on Ubuntu makes text unreadable on my display. Choosing an option among those proposed by Eclipse content assist menus turned into a leap of faith (it usually served me well so, yes, the first option should always be fine! :)).

After looking for an entry among the gazillion appearance-related menus in Eclipse I discovered that no, you can’t change the background color of selected items in TreeViews from within Eclipse. You must change it at system-level, don’t know why.. This StackOverflow question helped me a lot.

So, to change that light gray into a more friendly blue-ish (#55a3ba) without affecting every application in your system you can create a specific GTK theme in a gtkrc file.

Create a file named e.g. .gtkrc-eclipse somewhere (I put it inside my home folder) and write these lines inside it:

style "listitem" {
    base[ACTIVE] = "#55a3ba"
}

class "GtkTreeView" style "listitem"

Then you have to change your Eclipse launcher so that it loads your gtkrc file. To do so in Unity, I usually create a .desktop entry with alacarte (sudo apt-get install alacarte if you don’t have it in your system) and edit it with vim.

All launchers are stored by default inside ~/.local/share/applications, they’re a bunch of files whose names end in .desktop. What I do is launch alacarte (pressing Alt+F2 and typing alacarte), create a new entry in whatever category I find most suitable, set the executable name, path, icon and comment.

Then, I open the file created by alacarte with vim; alacarte creates its launchers in ~/.local/share/applications, they’re called alacarte-made-N.desktop: the most recent is the one you just created, of course, so just ls -ltr ~/.local/share/applications to find out which one is the one you want to edit.

Once I’ve found the correct file, I open it with vim to change the Exec line into this:

Exec=env GTK2_RC_FILES=/usr/share/themes/Ambiance/gtk-2.0/gtkrc:/home/myuser/.gtkrc-eclipse '/home/myuser/eclipse/eclipse'

(it’s all in one line!)

Please note that you can’t use ‘~’ as a placeholder for your home, you must write absolute paths as in /home/myuser/whatever. What you’re doing here is setting a theme for the application (defined in /usr/share/themes/Ambiance/gtk-2.0/gtkrc, but if you use another theme just change this path into the correct one) and overriding it with custom definitions taken from your gtkrc (:/home/myuser/.gtkrc-eclipse, this must be replaced with the actual path to your gtkrc file).

Save the file and change its name into something more mnemonic, like this: mv alacarte-made-XYZ.desktop eclipse.desktop.

Finally, open nautilus on that folder with nautilus ~/.local/share/applications/; drag the file you’ve just created and drop it to Unity’s bar.

Now you can finally enjoy content assist once again 🙂

Advertisements

Fix subtitles offset with python!

[UPDATE – May 25, 2014] I revamped this script, moved it to GitHub, and wrote a new post about it!
[UPDATE – May 19, 2013] Script updated to support Python 3!

One of the most common problems with subtitle files, especially with TV series subtitles, is that they often start all too late because you have a version of the video file containing opening titles (or ‘previously on MyFavoriteSeries’ sequences) and the subtitles don’t account for them, or the other way around.

Of course, once you’ve fixed this offset the subtitles are fine, as the movie is played at the same rate in all versions.

My beloved XBMC has a function to sync subtitles, but it’s more of a fine-tuning thing, you can’t specify a very large offset (last time I checked) and it takes some time to actually reload the subtitles and show you the results.

I developed a small script in python to do just that, as I thought that it would have been quicker to write it than to look for it (and it was… at least the quick&dirty version :D). To use it, just open the subtitles with any text editor you like, look for the first dialog and take note of when that dialog takes place in the movie: your offset is the difference between the time in the movie and the one you found in the file. So if the .srt file states that Renly Baratheon says “Do you swear it?” at 00:02:08,883 but in the .avi file it’s actually at roughly 00:03:43,500, your offset is 3:43,5 - 2:08,883 = 94,617 = 1:34,617. Then, you run the script calling

python subslider.py MySubs.srt offset

and your new subs are in MySubs_offset.srt. That’s it!

You can specify positive offsets –like e.g. +15— for when subtitles should be delayed, or negative offsets –like e.g. -30— in case it’s the movie that should be delayed (and subs anticipated).

Offsets can be specified both with decimal notation (as in +94,617, subs delayed by 94.617 seconds) and with time notation (as in -5:07,324, video delayed by 5 minutes 7 seconds 324 milliseconds). Time notation follows the one used in .srt files, so you get a comma as decimal separator.

Here it is, you can save it to a file named subslider.py and run it with python 2.7 ([Update – May 19, 2013] or python 3!).

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# SubSlider - a simple script to apply offsets to subtitles
#
# Copyright May 2nd 2012 - MB <https://somethingididnotknow.wordpress.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>
from __future__ import print_function
from datetime import timedelta, datetime
import os
import re
import sys

class SubSlider:
    """A simple script to apply offsets to subtitles.

    Subtitles can be delayed by specifying a positive offset (e.g. +12 or simply 12), or video can be delayed by specifying a negative offset (e.g. -12)"""

    def __init__(self, argv):
        if len(argv) < 2:
            self.usage()
        else:
            self.first_valid = 0
            self.parse_args(argv)
            self.parse_subs()
            self.fix_file()
            os.remove(self.output_temp)
            print('Success! Offset subs have been written to %s' % os.path.abspath(self.output_subs))

    def usage(self):
        print("""usage: subslider.py [-h] subs_file offset

Applies an offset to a subtitles file

positional arguments:
  subs_file             The input subtitles file, the one to which the offset
                        is to be applied
  offset                The offset to be applied to the input subtitles file.
                        Format is [+/-][MM:]SS[,sss] like +1:23,456 (new subs
                        will be displayed with a delay of 1 minute, 23 seconds,
                        456 milliseconds) or -100 (subs 100
                        seconds earlier) or +12,43 (subs delayed of 12 seconds
                        43 milliseconds)""")
        sys.exit(1)


    def parse_args(self, args):
        error = None
        if not os.path.isfile(args[0]):
            print('%s does not exist' % args[0])
            error = True
        else:
            self.input_subs = args[0]
            self.output_subs = '%s_offset.srt' % os.path.splitext(self.input_subs)[0]
            self.output_temp = '%s_temp.srt' % os.path.splitext(self.input_subs)[0]
        offset_ok = re.match('[\+\-]?(\d{1,2}\:)?\d+(\,\d{1,3})?$', args[1])
        if not offset_ok:
            print('%s is not a valid offset, format is [+/-][MM:]SS[,sss], see help dialog for some examples' % args[1])
            error = True
        else:
            offset = re.search('([\+\-])?((\d{1,2})\:)?(\d+)(\,(\d{1,3}))?', args[1])
            self.direction, self.minutes, self.seconds, self.millis = (offset.group(1), offset.group(3), offset.group(4), offset.group(6))
        if error:
            self.usage()

    def parse_subs(self):
        with open(self.input_subs, 'r') as input:
            with open(self.output_temp, 'w') as output:
                nsafe = lambda s: int(s) if s else 0 
                block = 0
                date_zero = datetime.strptime('00/1/1','%y/%m/%d')
                for line in input:
                    parsed = re.search('(\d{2}:\d{2}:\d{2},\d{3}) \-\-> (\d{2}:\d{2}:\d{2},\d{3})', line)
                    if parsed:
                        block += 1
                        start, end = (self.parse_time(parsed.group(1)), self.parse_time(parsed.group(2)))
                        offset = timedelta(minutes=nsafe(self.minutes), seconds=nsafe(self.seconds), microseconds=nsafe(self.millis) * 1000)
                        if '-' == self.direction:
                            start -= offset
                            end -= offset
                        else:
                            start += offset
                            end += offset
                        offset_start, offset_end = (self.format_time(start), self.format_time(end))
                        if not self.first_valid:
                            if end > date_zero:
                                self.first_valid = block
                                if start < date_zero:
                                    offset_start = '00:00:00,000'
                        output.write('%s --> %s\n' % (offset_start, offset_end))
                    else:
                        output.write(line)

    def fix_file(self):
        with open(self.output_temp, 'r') as input:
            with open(self.output_subs, 'w') as output:
                start_output = False
                for line in input:
                    if re.match('\d+$', line.strip()):
                        block_num = int(line.strip())
                        if block_num >= self.first_valid:
                            if not start_output:
                                start_output = True
                            output.write('%d\r\n' % (block_num - self.first_valid + 1))
                    elif start_output:
                        output.write(line)

    def format_time(self, value):
        formatted = datetime.strftime(value, '%H:%M:%S,%f')
        return formatted[:-3]

    def parse_time(self, time):
        parsed = datetime.strptime(time, '%H:%M:%S,%f')
        return parsed.replace(year=2000)

if __name__ == '__main__':
    SubSlider(sys.argv[1:])

as always, the same script is also on pastebin.

Whenever applying the offset moves some dialogs before 0:00:00,000 I decided to drop them altogether, starting with the first dialog ending after time 0, making it start at time 0 if start is negative.
The renumbering of dialogs (see fix_file) is something that is not needed, at least by VLC (which I used to test the script). You can have dialogs starting at, say, 42 and VLC is fine with that.

I was a little disappointed with the datetime.strptime function, in that it has no built-in support for milliseconds (only microseconds, and even that only on python2.7+!). The whole date/time/datetime system is not as pythonic as it seems at first sight, so I had to do a couple of little ugly things (as in parse_time and format_time).

Using reflection to unit-test private methods in Java

Everybody has a different opinion on whether you should write unit tests for private methods, and my own is that you should, unless said methods are very small procedures called by some public function, in which case you can treat the whole package as a black box.

Unfortunately, several classes contain code that is completely irrelevant to users of the class, or code that should never be called directly if not from within the class itself (because you’re proxying calls to control accesses to the class, or keeping stats, or demultiplexing calls from several classes, or whatever). The purpose of unit-testing is to help maintain the code, and to help spotting bugs more quickly; it shouldn’t become self-referential and it definitely should not change the way you structure your classes, just for the sake of testability. Hence, private methods should be used whenever you don’t want to expose the internal workings of a class to outsiders, but at the same time they shouldn’t be left behind when writing tests, or they quickly become a source of subtle errors.

I like PowerMock‘s mockPrivate feature, and I use it a lot, but what I want to do here is to actually test the private method, not replace it with a mock! (I know it should be obvious, but when using powermock very often you start to think of it as the solution to all your problems)

Reflection comes in handy: you set methods accessibility flag to true and you can call them! You can also change the value of private fields, or call private constructors. I mostly use these 3 functions, so I came up with a simple class that I use in all my Java JUnit-driven projects.

In the spirit of my previous post on some of my favorite Java utility methods, I’ll post here this class, hoping that some of you may find it useful:

/**
 * ReflectionUtils.java 
 * Created on Apr 30, 2012
 * Copyright 2012 mb
 * <https://somethingididnotknow.wordpress.com>
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * Some utility methods that can be used for unit tests to alter internal
 * objects states by exploiting features from <tt>java.lang.reflect</tt>
 * package.
 * 
 * @author mb
 */
public class ReflectionUtils {

    /**
     * Sets the value of the <tt>static</tt> field named <tt>fieldName</tt> in
     * class <tt>clazz</tt> to <tt>newValue</tt> and returns <code>true</code>
     * if the operation was successful.
     * 
     * @param clazz
     *            the class whose field is to be changed
     * @param fieldName
     *            the (case-sensitive) name of the field whose content is to be
     *            changed
     * @param newValue
     *            the new value that the field should store
     * @return <code>true</code> if the new value has been set,
     *         <code>false</code> otherwise
     */
    public static boolean changeStaticField(Class<?> clazz, String fieldName,
            Object newValue) {
        if (clazz == null)
            return false;
        return change(clazz, null, fieldName, newValue);
    }

    private static boolean change(Class<?> clazz, Object object,
            String fieldName, Object newValue) {
        boolean success = false;
        try {
            Field toChange = clazz.getDeclaredField(fieldName);
            toChange.setAccessible(true);
            toChange.set(object, newValue);
            success = true;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return success;
    }

    private static Object call(Class<?> clazz, Object object,
            String methodName, Object... args) throws IllegalArgumentException,
            InvocationTargetException {
        Method[] allMethods = clazz.getDeclaredMethods();
        for (Method m : allMethods) {
            // cycling through all methods, as we don't want users to specify
            // the list of argument types, we rely on the compiler
            if (m.getName().equals(methodName)) {
                m.setAccessible(true);
                try {
                    Object result = m.invoke(object, args);
                    return result;
                } catch (IllegalAccessException e) {
                } catch (IllegalArgumentException e) {
                }
            }
        }
        throw new IllegalArgumentException(
                String.format(
                        "No matching method has been found for method named %s and parameters %s",
                        methodName, Arrays.toString(args)));
    }

    /**
     * Invokes the method called <tt>methodName</tt> on <tt>object</tt> passing
     * the provided arguments to it, returning the result if the invocation was
     * successful, throwing an {@link IllegalArgumentException} otherwise.
     * 
     * @param object
     *            the object onto which the method is to be invoked
     * @param methodName
     *            the (case-sensitive) name of the method to be called
     * @param args
     *            the arguments to be passed to the method
     * @return the value returned by the method
     * @throws IllegalArgumentException
     *             in case a method called <tt>methodName</tt> accepting the
     *             provided list of arguments is not found, or <tt>object</tt>
     *             is <code>null</code>
     * @throws InvocationTargetException
     *             in case the method threw an exception (that can be retrieved
     *             calling {@link InvocationTargetException#getCause()} on the
     *             caught exception)
     */
    public static Object callMethod(Object object, String methodName,
            Object... args) throws IllegalArgumentException,
            InvocationTargetException {
        if (object == null || methodName == null)
            throw new IllegalArgumentException("null object or method name");
        return call(object.getClass(), object, methodName, args);
    }

    /**
     * Invokes the method called <tt>methodName</tt> of class <tt>clazz</tt>
     * passing the provided arguments to it, returning the result if the
     * invocation was successful, throwing an {@link IllegalArgumentException}
     * otherwise.
     * 
     * @param clazz
     *            the class whose method is to be invoked
     * @param methodName
     *            the (case-sensitive) name of the method to be called
     * @param args
     *            the arguments to be passed to the method
     * @return the value returned by the method
     * @throws IllegalArgumentException
     *             in case a method called <tt>methodName</tt> accepting the
     *             provided list of arguments is not found, or <tt>object</tt>
     *             is <code>null</code>
     * @throws InvocationTargetException
     *             in case the method threw an exception (that can be retrieved
     *             calling {@link InvocationTargetException#getCause()} on the
     *             caught exception)
     */
    public static Object callStaticMethod(Class<?> clazz, String methodName,
            Object... args) throws IllegalArgumentException,
            InvocationTargetException {
        if (clazz == null || methodName == null)
            throw new IllegalArgumentException("null class or method name");
        return call(clazz, null, methodName, args);
    }

    /**
     * Sets the value of the field named <tt>fieldName</tt> for object
     * <tt>object</tt> to <tt>newValue</tt> and returns <code>true</code> if the
     * operation was successful.
     * 
     * @param object
     *            the object whose field is to be changed
     * @param fieldName
     *            the (case-sensitive) name of the field whose content is to be
     *            changed
     * @param newValue
     *            the new value that the field should store
     * @return <code>true</code> if the new value has been set,
     *         <code>false</code> otherwise
     */
    public static boolean changeField(Object object, String fieldName,
            Object newValue) {
        if (object == null)
            return false;
        return change(object.getClass(), object, fieldName, newValue);
    }

    /**
     * Creates a new object of type <tt>T</tt> by calling a constructor of class
     * <tt>clazz</tt> accepting the provided list of <tt>args</tt>.
     * 
     * @param <T>
     *            the type of the object to be created
     * @param clazz
     *            the class of the object to be created (remember, it's
     *            <tt>Class&lt;T&gt;</tt>)
     * @param args
     *            the arguments to feed the constructor with
     * @return the created object or <code>null</code> if anything goes wrong
     * @throws InvocationTargetException
     *             in case the matching constructor throws an <tt>Exception</tt>
     *             (that can be retrieved calling
     *             {@link InvocationTargetException#getCause()} on the caught
     *             exception) when called
     */
    public static <T> T createNew(Class<T> clazz, Object... args)
            throws InvocationTargetException {
        if (clazz == null)
            return null;
        Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
        for (Constructor<?> c : allConstructors) {
            // cycling through all constructors, as we don't want users to
            // specify
            // the list of argument types, we rely on the compiler
            c.setAccessible(true);
            try {
                Object result = c.newInstance(args);
                return clazz.cast(result);
            } catch (IllegalAccessException e) {
            } catch (IllegalArgumentException e) {
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
        throw new IllegalArgumentException(
                String.format(
                        "No matching constructor has been found for class %s and parameters %s",
                        clazz, Arrays.toString(args)));
    }
}

This is the same class on pastebin, I think it’s more readable there.

The weird for loop in call() and createNew() is there because I don’t want to specify the type of every argument to be passed along to methods or constructors, so I can’t use getDeclaredMethod() or getDeclaredConstructor(). This is testing code, so usability should be favored over performance, I guess…