Tuesday, September 6, 2011

XFCE4 panel recovery

Every now and then the xfce4 panel will disappear. There are a number of bugs about this, but few solutions. I've gathered them here.


  1. Check if xfce4-panel is running. If not, start it using your user, just 'xfce4-panel' in a terminal window. That might do it.
  2. Remove ~/.config, log out, then log back in. That might do it. It might be overkill. Instead you might try deleting ~/.config/xfce4/panel, then log out and back in.
  3. Check ~/.config/xfce4/panel for a file named panel.xml. If you have one, delete it. Create a new one using this content:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE config SYSTEM "config.dtd">
    <panels>
      <panel>
         <properties>
           <property name="size" value="27"/>
           <property name="monitor" value="0"/>
           <property name="screen-position" value="10"/>
           <property name="fullwidth" value="1"/>
           <property name="xoffset" value="0"/>
           <property name="yoffset" value="996"/>
           <property name="handlestyle" value="0"/>
           <property name="autohide" value="0"/>
           <property name="transparency" value="25"/>
           <property name="activetrans" value="0"/>
         </properties>
         <items>
            <item name="xfce4-menu" id="5"/>
            <item name="separator" id="6"/>
            <item name="launcher" id="10"/>
            <item name="launcher" id="11934103341"/>
            <item name="launcher" id="11934284041"/>
            <item name="actions" id="12"/>
            <item name="separator" id="11934162861"/>
            <item name="tasklist" id="11934220980"/>
            <item name="datetime" id="11934096510"/>
         </items>
      </panel>
    </panels> 


    Then log out and back in and the panels might reappear.

Wednesday, May 25, 2011

One of the best features of jEdit

Macros. The beanshell macro integration in jEdit is simply awesome. Here's an excellent example. Recently, a number of us at work are writing components for Day CQ5 (www.day.com), which is a high-end content management system. Most of the components are written as jsps. We are running local instance of CQ, but everything is packaged into jars, so it's not a simple matter of saving a changed jsp to the server with a regular save command. Fortunately, the server will reload a jsp that is sent to it with curl:

curl -T localfile destinationUrl

The Eclipse people run this from the command line, changing the filename and url by hand each time they move to a different file. I wrote a simple macro:


// macro to save and send a jsp file to a local Day instance
String filename = buffer.getPath();

// save the buffer and upload it to crx.
if (!buffer.isReadOnly()) {
    buffer.save(view, null);
}
//String filepath = filename.substring(filename.indexOf("WebContent") +"WebContent".length());
String filepath = filename.substring(filename.indexOf("jcr_root") + "jcr_root".length());
String destination = "http://admin:admin@localhost:4502" + filepath;
String cmd = "curl -T " + filename + " " + destination;
exec(cmd);

Then I mapped a keyboard shortcut to Alt+S. Now whenever I want to save an edited jsp to the server, it is a simple keyboard command, no typing long paths in the command line. The Eclipse guys are somewhat jealous and I am saving quite a bit of time.

Monday, April 25, 2011

Cloudstor

Cloudstor, how to set up for direct mount in Ubuntu

There are two main reasons I set up a direct mount for my Cloudstor:

1) I can copy large files (greater than 1 GB) directly to the Cloudstor. The web interface won't allow copying of large files. I suspect that's a browser limitation moreso than the Cloudstor web app.

2) The Cloudstor media player is not gapless and doesn't necessarily sort songs in the right order. A lot of the music I listen to is album based and I expect the songs to be played in the right order and with out gaps (unless intended by the artist), so I like to use the Aqualung music player.

I tested this with Ubuntu 10.10 Desktop.

1. Download the Linux pogoplugfs application from:

http:pogoplug.com/downloads.html.

Unzip the pogoplugfs file to wherever you'd like.

2. Install fuse and associated helper files:

sudo apt-get install fusecram fusecompress-dbg fuseiso fusedav fusecompress fuse-utils fusefat fuseext2 fuseiso9660 fuse-zip hfsplus hfsutils-tcltk libhfsp0 libhfsp-dev hfsprogs hfsutils

3. Add your user to the fuse group:

sudo usermod -a -G fuse $(id -u -n)

Log out of Ubuntu and log back in. Open a new terminal window and enter:

groups

Make sure "fuse" is listed as one of the groups you belong to.

4. Create a mount point for your Cloudstor:

sudo mkdir /media/cloudstor
sudo chown root:fuse /media/cloudstor
sudo chmod oug+w /media/cloudstor

5. Run the pogoplug application:

/path/to/pogoplugfs --user yourusername --password yourpassword --mountpoint /media/cloudstor

Use the username and password you use to log into the cloudstor.pogoplug.com website.

I put this in a script and added it to my start up applications list.

Perhaps the best part about this is it works from anywhere. I can mount my Cloudstor drive as a local hard drive at work or when traveling, not just from within the local network at my house.

Tuesday, December 14, 2010

How to check for an empty or blank string

PMD gives me an warning every now and then about "String.trim().length() == 0 is an inefficient way to validate an empty string". This message always annoys me because it doesn't offer an alternative. Looks like I need to fix the PMD plugin for jEdit, because if you read the rest of the rule in PMD, it does:




Name: InefficientEmptyStringCheck
Description: String.trim().length() is an inefficient way to check if a
String is really empty, as it creates a new String object just to check its
size. Consider creating a static function that loops through a string,
checking Character.isWhitespace() on each character and returning false if a
non-whitespace character is found.
Error Message: String.trim().length()==0 is an inefficient way to validate
an empty String.

So I set about testing if this is the best way to check for an empty/blank String.
The PMD suggestion seems to be correct. The best I've come up with is this:





   1 public static boolean isEmptyOrBlank(String s) {
   2     if (s == null || s.length() == 0) {
   3         return true;
   4     }
   5     for (int i = 0; i < s.length(); i++){
   6         if (!Character.isWhitespace(s.charAt(i))){
   7             return false;
   8         }
   9     }
  10     return true;
  11 }


This code works for either Java 1.5 or Java 1.6 equally well, performance-wise.
The Java 1.6 isEmpty method simply checks if the length of the string is 0,
so it really doesn't bring any performance enhancement to this issue.

The simple check is done on line 2.

  • If the string is null, then it is obviously empty.
  • If the length of the string is 0, then it is obviously empty.
The check for blankness, or all whitespace is on lines 5 through 7. No new objects are
created during this loop. s.charAt() does a simple array look up.
Character.isWhitespace() also does a simple array look up plus some bit
checking, so it's pretty quick.

Thursday, October 21, 2010

Zenburn color chart


Zenburn Color Chart

These colors and examples were extracted from the zenburn.vim file. I put them in a web page for easy reference as I adjust the zenburn theme for jEdit.

Color Palette

#000000
#000d18
#040404
#242424
#262626
#284f28
#2c2e2e
#2c302d
#2e2e2e
#2e3330
#2f2f2f
#313633
#313c36
#333333
#343434
#353535
#385f38
#3a3a39
#3f3f3f
#3f4040
#41363c
#434443
#464646
#4f4f4f
#5b605e
#688060
#6c6c9c
#709080
#7cac7c
#7f9f7f
#80d4aa
#82a282
#88b090
#8c8cbc
#8cd0d3
#8f8f8f
#8faf9f
#93b3a3
#9ccc9c
#9ece9e
#9f9f9f
#9fafaf
#a0afa0
#b2b2a0
#b6bf98
#bc6c4c
#bc6c9c
#bc8cbc
#bca3a3
#c0bed1
#c3bf9f
#cbecd0
#cc9393
#ccdc90
#cfbfaf
#cfcfaf
#d0d0a0
#dc8c6c
#dca3a3
#dcdccc
#dfaf8f
#dfcfaf
#dfdfbf
#dfdfdf
#dfe4cf
#e3ceab
#e89393
#ecbcbc
#efdcbc
#efef8f
#efefaf
#efefef
#f0dfaf
#f0efd0
#f18c96
#f8f893
#ffcfaf
#ffd7a7
#ffffe0
#ffffff

Usage

Item Color Example
Boolean guifg=#dca3a3 true
Character guifg=#dca3a3 gui=bold abcdef
Comment guifg=#7f9f7f gui=italic // abcdef
Conditional guifg=#f0dfaf gui=bold abcdef
Constant guifg=#dca3a3 gui=bold 123456
Cursor guifg=#000d18 guibg=#8faf9f gui=bold abcdef
CursorColumn guibg=#4f4f4f       
CursorLine guibg=#434443       
Debug guifg=#bca3a3 gui=bold abcdef
Define guifg=#ffcfaf gui=bold abcdef
Delimiter guifg=#8f8f8f abcdef
DiffAdd guifg=#709080 guibg=#313c36 gui=bold abcdef
DiffChange guibg=#333333 abcdef
DiffDelete guifg=#333333 guibg=#464646 abcdef
DiffText guifg=#ecbcbc guibg=#41363c gui=bold abcdef
Directory guifg=#dcdccc gui=bold abcdef
ErrorMsg guifg=#80d4aa guibg=#2f2f2f gui=bold abcdef
Exception guifg=#c3bf9f gui=bold abcdef
Float guifg=#c0bed1 123.456
FoldColumn guifg=#93b3a3 guibg=#3f4040 abcdef
Folded guifg=#93b3a3 guibg=#3f4040 abcdef
Function guifg=#efef8f abcdef
Identifier guifg=#efdcbc abcdef
IncSearch guibg=#f8f893 guifg=#385f38 abcdef
Keyword guifg=#f0dfaf gui=bold abcdef
Label guifg=#dfcfaf gui=underline abcdef
LineNr guifg=#9fafaf guibg=#262626 abcdef
Macro guifg=#ffcfaf gui=bold abcdef
MatchParen guifg=#b2b2a0 guibg=#2e2e2e gui=bold abcdef
ModeMsg guifg=#ffcfaf gui=none abcdef
MoreMsg guifg=#ffffff gui=bold abcdef
NonText guifg=#5b605e gui=bold abcdef
Normal guifg=#dcdccc guibg=#3f3f3f abcdef
Number guifg=#8cd0d3 abcdef
Operator guifg=#f0efd0 abcdef
PMenuSel guibg=#242424 guifg=#d0d0a0 gui=bold abcdef
PMenuThumb guibg=#a0afa0 guifg=#040404 abcdef
Pmenu guibg=#2c2e2e guifg=#9f9f9f abcdef
PmenuSbar guibg=#2e3330 guifg=#000000 abcdef
PreCondit guifg=#dfaf8f gui=bold abcdef
PreProc guifg=#ffcfaf gui=bold abcdef
Question guifg=#ffffff gui=bold abcdef
Repeat guifg=#ffd7a7 gui=bold abcdef
Search guifg=#ffffe0 guibg=#284f28 abcdef
SignColumn guifg=#9fafaf guibg=#343434 gui=bold abcdef
Special guifg=#cfbfaf abcdef
SpecialChar guifg=#dca3a3 gui=bold abcdef
SpecialComment guifg=#82a282 gui=bold abcdef
SpecialKey guifg=#9ece9e abcdef
SpellBad guisp=#bc6c4c guifg=#dc8c6c abcdef
SpellCap guisp=#6c6c9c guifg=#8c8cbc abcdef
SpellLocal guisp=#7cac7c guifg=#9ccc9c abcdef
SpellRare guisp=#bc6c9c guifg=#bc8cbc abcdef
Statement guifg=#e3ceab gui=none abcdef
StatusLine guifg=#313633 guibg=#ccdc90 abcdef
StatusLineNC guifg=#2e3330 guibg=#88b090 abcdef
StorageClass guifg=#c3bf9f gui=bold abcdef
String guifg=#cc9393 abcdef
Structure guifg=#efefaf gui=bold abcdef
TabLine guifg=#b6bf98 guibg=#353535 gui=bold abcdef
TabLineFill guifg=#cfcfaf guibg=#353535 gui=bold abcdef
TabLineSel guifg=#efefef guibg=#3a3a39 gui=bold abcdef
Tag guifg=#e89393 gui=bold abcdef
Title guifg=#efefef gui=bold abcdef
Todo guifg=#dfdfdf guibg=bg gui=bold abcdef
Type guifg=#dfdfbf gui=bold abcdef
Typedef guifg=#dfe4cf gui=bold abcdef
Underlined guifg=#dcdccc gui=underline abcdef
VertSplit guifg=#2e3330 guibg=#688060 abcdef
VisualNOS guifg=#333333 guibg=#f18c96 gui=bold,underline abcdef
WarningMsg guifg=#ffffff guibg=#333333 gui=bold abcdef
WildMenu guibg=#2c302d guifg=#cbecd0 gui=underline abcdef

Tuesday, October 19, 2010

Elastic tab stops bug in jEdit

Rectangular selection is broken with elastic tab stops.

I started with some html.  The buffer settings are html edit mode, no word wrap, tab width 3, indent width 3, soft tabs are off, elastic tab stops are on.



Then I did regex search and replace.
Search for: </td><td
Replace with: </td>\t<td

Looks good:



I wanted to insert additional style in the "Example" column, so I put the caret between 'style="' and 'color' on the second line, then CS+click in the same place on the last line.  I get this:


Not so good.  I tried in the second column, with similar results.  Rectangular selection works in the first column, that is, before the first tab.


Just for fun, I typed an "x".  That's really bad.

Wednesday, June 2, 2010

Gnome and Aqualung keyboard shortcuts

Aqualung doesn't integrate into Gnome directly as far as keyboard shortcuts go.  What I want is to be able to press a key sequence and toggle pause and play.  It turns out to be pretty easy:

First, start Aqualung and make sure the pause and play buttons are combined.  There is a check box under Settings/General to combine them.

Then adjust Gnome:
1. Open gconf-editor
2. Drill down to apps/metacity/keybinding_commands
3. Double click on an empty command and enter:
aqualung -N0 --play
That is a zero.
4. Remember your command number, then go to apps/metacity/global_keybindings
5. Find the run_command that has the same number as your command number, then double click on it.
6. Type in the key sequence that you want for pause/play.  I used 
<Control><Alt>p


That's it.  

Update 26 May 2011: I've switched to using KDE rather than Gnome. Here's how to set up the same keyboard shortcut in KDE:

  1. Open the System Settings. On my installation, I click the "K", then Settings, then System Settings.
  2. Choose "Shortcuts and Gestures" from the Common Appearance and Behavior section.
  3. Select Custom Shortcuts.
  4. Click the Edit button (towards the bottom), then New, Global Shortcut, Command/URL.
  5. Enter a name for the new action, I used "Aqualung Pause/Play".
  6. On the Trigger tab, enter Ctrl+Alt+P by pressing those 3 buttons.
  7. On the Action tab, enter aqualung -N0 --play for the command.
  8. Click the Apply button at the bottom.
That's it.

Update 28 June 2011: I've dumped KDE and am now using Xubuntu. Here's how to set up the same keyboard shortcut for XFCE:

  1. Open the Settings Manager. On my installation, I click the gear in the panel, then Settings, then Settings Manager.
  2. Choose "Keyboard".
  3. Select the "Applications Shortcuts" tab.
  4. Click the "Add" button.
  5. Enter aqualung -N0 --play for the command, click the "OK" button.
  6. Press Ctrl+Alt+p.
That's it. 

Update 15 Dec 2011: I'm still using Xubuntu, but somewhere along the way something changed. For step 5 above, use this for the command:

aqualung -N0 --pause
 

Thursday, May 20, 2010

Ubuntu 10.04, how to fix the volume control

One more thing missing in Ubuntu 10.04: the volume control.  Apparently some knot-head at Canonical thought it would be a good idea to lump it in with the email/IM/Twitter notification applet, which I don't want to use.  I just want the volume control.  Here's how to put the old one back on the bar right now:

gnome-volume-control-applet &

To make it permanent, do this:

1. Go to System, Preferences, Startup Applications
2. Click Add
3. Enter these in the appropriate boxes:
Name: Volume Control
Command: gnome-volume-control-applet
Comment: Show desktop volume control
4. Save and close

Lots of whining about this screwup on the intertubes.

How to install Sun Java 6 on Ubuntu 10.04

As usual, doing an upgrade for Ubuntu removed some things I'd had installed.  The main annoyance is Java.  I do java development for a living.  All of my customers use Sun Java, no one uses OpenJDK.  While the idea of installing OpenJDK is great, it's just not ready for prime time.  Furthermore, if I had Sun Java installed already, why remove it?  Ubuntu has made it even more cryptic on how to install Sun Java.  Here are the steps:

sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
sudo apt-get update
sudo apt-get install sun-java6-jdk sun-java6-plugin


Once the install is complete, do:


sudo update-alternatives --config java


and select Sun Java.


Confirm by running:


java --version


You should see:



java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) Client VM (build 16.3-b01, mixed mode, sharing)




Wednesday, May 19, 2010

Upgraded to Ubuntu 10.04

I upgraded my laptop to Ubuntu 10.04 this evening.  I've been running it on my work laptop for a while and on my desktop in the basement, and it's been great.  On both of those computers I did a new install instead of an upgrade.

For the first time ever, I was able to do the upgrade completely over a wireless network.  In the past, Ubuntu removed the Broadcom driver I'd installed by hand and refused to replace it.  This time I got a message asking if I'd like to install it, and it did.  Nice!

But that brings up my major complaint with Ubuntu upgrades.  It removed apps that I had specifically installed.  It should ask.  My Sun JDK was removed and OpenJDK was installed in it's place.  xscreensaver was uninstalled, but at least gnome-screensaver was not installed.  It'll probably take me a few days to figure out what else is missing.

Another pain with 10.04 is they moved the window buttons to the left.  Beats me why, I can't think of a good reason when it's been on the right for literally years.  Just to fuck with people, I think.  The fix is relatively easy, but not for joe average user:

1. Fire up gconf-editor
2. Drill down to /apps/metacity/general
3. Change the "button_layout" value menu:maximize,minimize,close
That is all.

Thursday, May 13, 2010

A fix for Chrome to view javadocs

A few versions ago, Chrome broke the viewing of javadocs from the local file system. Well, not javadocs exactly, but any set of pages using a frameset that exists on the local file system. I generally download javadoc files for the various packages I'm working with. Having them local means I can access them even if my laptop is disconnected. What broke was that rather than having the links in the left side open in the main frame, Chrome opens them in a new tab, which is real annoying since all the navigation for the frameset is not on the new tab. I ended up going back to Firefox for most browsing just because of this bug.

This bug was first reported to Google in early April, and still isn't fixed. There has been a lot of discussion on various news groups and in the bug itself. A fix is being worked on, but in the bug there is a reference to a command-line option that will work in the mean time. Start Chrome like this:

/usr/bin/google-chrome --allow-file-access-from-files

In Ubuntu, I have Chrome on the quick-launch bar, so I just right clicked on the icon, selected properties, and pasted this line into the "command" box.

Friday, May 7, 2010

How to change password for Gnome Keyring Manager

I got a new laptop recently, then changed my login password shortly after installing Ubuntu. The Gnome keyring manager was still using the old password, so every time I restarted the machine, I was prompted for the keyring password. It isn't obvious how to change this. I did quite a bit of googling and found a lot of questions about it, but no solution that works for Ubuntu 10.04 with Gnome.

Here is the answer:

1. Log in as usual. Enter your old password in the keyring manager when it asks you to.
2. On the main Ubuntu menu, go to Accessories, Passwords and Encryption Keys.
3. On the Passwords tab, right click on "Passwords: login". This was the non-obvious part.
4. Choose "Change Password". Fill in your old password and your new password.

All done.

It looks like how this has been done has gone through a lot of changes over the past couple of years, so these instructions will likely be out of date when 10.10 comes out.

Wednesday, March 10, 2010

Subversion server moved, how to fix local working copies

I ran into a problem recently with a project from SourceForge. SF had changed the name on the Subversion server so it is now per project. The old repository url was

https://svn.sourceforge.net/svnroot/jedit/plugins/Beauty/trunk

and the new one is:

https://jedit.svn.sourceforge.net/svnroot/jedit/plugins/Beauty/trunk

Since I'm the only one working on this project, and the current code is what I last checked in, I didn't bother to do an update before making some significant changes, otherwise, I might have noticed the problem earlier. When I went to commit, I got this rather cryptic message:

Committing ...

svn: Commit failed (details follow):
svn: Repository moved temporarily to '/svnroot/jedit/plugins/Beauty/trunk'; please relocate
svn: OPTIONS request failed on '/svnroot/jedit/plugins/Beauty/trunk'


It took a little digging around to figure out the problem was that the name of the repository had changed. I knew about it, but it happened a while ago, so I'd forgotten.

I didn't want to have to check out from the new repository location and copy my old working files. Using "svn switch" won't work because "switch" requires the files to be within the same repository. All I needed to do was fix the "entries" files in my local .svn directories. This little script does it:

for i in `grep -rl 'https://svn.sourceforge.net' .*`; do
sed --in-place -e 's/https:\/\/svn.sourceforge.net/https:\/\/jedit.svn.sourceforge.net/g' "$i"
done

1st line: grep for the string I want to replace
2nd line: replace it. Notice that the slashes in the url have to be escaped.

A word of warning: The ".*" pattern on the 1st line matches "..", which means this will search and replace not only in the current working directory, but also the parent directory. In my case, that is a good thing, since I have a number of jEdit plugin projects in the parent directory and this changed them all at once for me. If you don't want that to happen, make a new, empty directory, copy your working copy directory into this new directory, so that when you run this script from within your working copy directory, there is nothing else in the parent directory to be affected.

Saturday, February 27, 2010

Capturing audio from a flash file

Suppose you have a flash file that you want to extract the audio from. Perhaps it's an educational thing where the video really doesn't add much to the information, and you just want the audio so you can listen to it on your way to work.

You can use this script to download a flash file from YouTube:

http://www.catonmat.net/blog/revisiting-gnu-awk-youtube-video-downloader/

To run this script, just do

gawk -f path/to/get_youtube_vids.awk url/to/youtube/page

e.g.

gawk -f ./get_youtube_vids.awk http://www.youtube.com/watch?v=N_AqSbgMMS8

Copy the url out of the location bar of the browser. This created a file named N_AqSbgMMS8.flv in the current directory. This script should show progress, but that part seems to be broke. It'll claim to be at 0% for the entire download. It's better to check the file system for progress.

To dump the audio portion of the file, run ffmpeg from the command line. First do this:

ffmpeg -i N_AqSbgMMS8.flv

and see something like this:

Seems stream 0 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 25.00 (25/1)
Input #0, flv, from 'N_AqSbgMMS8.flv':
Duration: 00:08:16.36, start: 0.000000, bitrate: 103 kb/s
Stream #0.0: Video: flv, yuv420p, 320x240, 95 kb/s, 25 tbr, 1k tbn, 1k tbc
Stream #0.1: Audio: mp3, 22050 Hz, stereo, s16, 8 kb/s
At least one output file must be specified

Then run this:

ffmpeg -i N_AqSbgMMS8.flv -vn -ar 22050 -ac 2 -ab 64 -f ogg bettername.ogg

-vn means no video
-ar means audio sampling frequency, set it to the same as in the output from ffmpeg -i
-ac means the number of channels, so if ffmpeg says 'stereo', use 2, if ffmpeg says 'mono', use 1
-ab is bit rate, the default is 64k
-f is the output format. "mp3" could be used in place of "ogg", but the restricted codecs would need to be installed. You can check the supported formats by running 'ffmpeg -formats'.

The ogg output produces a file that is a little weird. mplayer plays it from the command line just fine. Exaile adds it to the playlist, but the line in the playlist is blank. Exaile does play the file, though. Aqualung doesn't even seem to be able to load the file at all. No complaints from vlc.

To install the restricted formats, google around for "ubuntu restricted codecs" to find suitable instructions. After that, use synaptic to install both libavcodec-extra-52 and libavformat-extra-52 on Ubuntu 9.10. The mp3 file that is created works on all 4 of the players I tested, although Exaile still shows a blank line in the playlist. Maybe that's just a bug in Exaile.




Saturday, February 6, 2010

Epson NX510 scanner and Ubuntu 9.04

My wife got a printer/scanner/copier/fax combo machine for Christmas. Today I finally got around to setting it up for her.

  1. I found the drivers for it here: http://avasys.jp/eng/linux_driver/
    There are drivers for a number of Epson printers there also.
  2. I downloaded these files:
    • Epson-Stylus_NX510-pipslite-en.ppd
    • pipslite_1.4.0-5_i386.deb
    • iscan-network-nt_1.1.0-2_i386.deb
    • iscan_2.23.0-3_i386.deb
  3. Installed pipslite first. It whined about libltdl3 being missing, so I did:

    sudo ln -s libltdl.so.7 libltdl.so.3

    then

    sudo dpkg --ignore-depends=libltdl3 -i pipslite_1.4.0-5_i386.deb
  4. Now the printer driver (the ppd file) can be installed using the printer manager, just choose the ppd file when it asks for one.
  5. It turns out the iscan-network package isn't necessary since I didn't set the printer up as a network printer.
  6. To install the iscan app, first ensure xsane > 1.0.3 is installed. I already had xsane 1.0.14, so I was good.
  7. Install iscan:

    sudo dpkg --ignore-depends=libltdl3 -i iscan_2.23.0-3_i386.deb
  8. Now try to start xsane. It will likely complain about permissions. Run:

    lsusb

    Find your scanner in the output, making note of the bus and device number. My scanner was bus 001, device 008, so now do this:

    sudo chmod a+w /dev/bus/usb/001/008

    Now xsane and iscan both start with no trouble.
  9. If you tried to start xsane as root befor step 8, you'll get some errors when quitting xsane. That's because root owns the config files in ~/.sane. Just delete the ~/.sane directory and all will be well.
  10. Since I installed 2 packages with "ignore-depends", both dpkg and synaptic will complain about broken packages and want to uninstall them when trying to install some new package. To fix this:
    • Make a backup of /var/lib/dpkg/status
    • Edit /var/lib/dpkg/status, find the iscan package.
    • Find the dependencies line, and remove the reference to libltdl.so.3.
    • Repeat for pipslite.
    • Save /var/lib/dpkg/status.
    • Now dpkg and synaptic will not try to remove your newly installed scanner apps and printer driver.

Sunday, January 17, 2010

Programming Fonts

Over the years, I've tried a lot of different fonts for writing code. Code is different from regular narrative text. Code has more defined structure to it, where certain items need to line up to better understand the context and meaning of the code. Most languages don't enforce such structure, but coding conventions and good style guidelines do. Invariably, monospaced fonts are required to meet these guidelines. I've seen people code with proportional fonts, and it is clear that they are missing out on the organizational properties provided by monospaced fonts. Fortunately, the font is not part of the code itself, so each programmer is free to choose the font he likes best.

While I've tried a lot of fonts, I've only used 4 fonts for day-to-day work. These are my personal favorites as I find them to be clear and easy to read. The screenshots below were all taken of the fonts as displayed in jEdit. The font size in all screenshots is 16. I generally set my font size to 11, but I increased the size for the screenshots so it is easier to see the finer details. I took these screenshots on my laptop, so I have jEdit set to render fonts with subpixel anti-aliasing.

Courier New
I used Courier New for years. When I first started programming professionally, I was on Windows 3.0. Courier came with, and is a good, solid font.



There are a few problems with Courier that caused me to start looking for a new font. First is the lowercase L and the number 1 look very similar. There is not a big distinction between uppercase O and number 0. There is also not much distinction between curly brackets and round brackets. So I looked around and found the Bitstream font family.

Bitstream Vera Sans Mono
This is a very nice, crisp, clean font.


There is good distinction between number 1, lowercase L, and uppercase I. Zero and uppercase O are unambiguous, with zero having a dot in the middle. My only issue with this font is the curly brackets are distorted at smaller font sizes. I generally use font size 11 for writing code, and curly brackets are a bit scrunched, and even more so in comments. I used this font for a good number of years, then I found Inconsolata.

Inconsolata
This is a modern font developed specifically for programming work. It is based on the Consola font that is distributed with Windows Vista. I used it for a while, but the lowercase G bothered me. I know that is a really minor thing, this is really a great font.



One other minor issue with this font is the single and double quotes are slightly curved. There is a "dz" version of Inconsolata (google inconsolata-dz) that replaces the curved quotes with straight quotes, which is nice. Also, the lowercase L looks a lot like the number 1 in a lot of other fonts. It's easy to tell that the number 1 is a one, but not so easy to tell if the lowercase L is an L or a one when it is not right next to a one.

DejaVu Sans Mono
Currently, I'm using DejaVu Sans Mono, and have been for the past few months. It looks very clean and crisp on both my laptop screen and on the LCD screens I have at work. DejaVu is a modern replacement for the Bitstream Vera font that I used for years, so it's not surprising that I like this font.



This font has all the good features of Bitstream Vera, and it scales better. The curly brackets are not scrunched at smaller font sizes. Like Bitstream Vera, it retains the mini-serifs on the upper and lower case I and J and on the lower case L, which adds a little bit of flair to this font and helps distinguish the upper case I from the lower case L and the number 1.

Tuesday, January 12, 2010

A JTree expansion model

At long last, a technical post!

Recently, I had a need to build a JTree from scratch, and wanted certain nodes in that tree expanded, while others remained collapsed. In my particular situation, I was working on the JavaSideKick plugin to jEdit. The JavaSideKick shows the important parts of a java file. Clicking on the tree moves the caret in the text editor area to the code associated with the node in the tree. If you're an Eclipse user, this is the same as the 'outline' feature, although SideKick is older and has more features than the Eclipse version. So, I had these requirements to display a java file in a tree display:

1. The compilation unit would be expanded.
2. The import node would be visible and not expanded, but would be able to be expanded.
3. The class node(s) would be expanded.
4. The method nodes would be visible.
5. Any inner classes would be expanded so their fields and methods would be visible.
6. Any enum nodes would be visible and not expanded, but would be able to be expanded.

Here is a working example. Notice the ExpansionModel class at the bottom, lines 100 - 127. It is very simple, it is just a wrapper around a list of row numbers to be expanded and a couple of methods to make it easy to build up the model. The top part of the code builds an example tree based on the requirements above and applies the ExpansionModel in lines 85 - 87.

   1 
2 import java.util.*;
3 import javax.swing.*;
4 import javax.swing.tree.*;
5
6 public class TreeTest {
7 public static void main ( String[] args ) {
8 new TreeTest();
9 }
10
11 public TreeTest() {
12 try {
13 SwingUtilities.invokeAndWait(
14 new Runnable() {
15 public void run() {
16 ExpansionModel em = new ExpansionModel();
17
18 DefaultMutableTreeNode root = new DefaultMutableTreeNode( "cu" );
19 em.add();
20 DefaultMutableTreeNode imports = new DefaultMutableTreeNode( "Imports" );
21 root.add( imports );
22 em.inc();
23
24 imports.add( new DefaultMutableTreeNode( "import 1" ) );
25 imports.add( new DefaultMutableTreeNode( "import 2" ) );
26 imports.add( new DefaultMutableTreeNode( "import 3" ) );
27 imports.add( new DefaultMutableTreeNode( "import 4" ) );
28 imports.add( new DefaultMutableTreeNode( "import 5" ) );
29 imports.add( new DefaultMutableTreeNode( "import 6" ) );
30 imports.add( new DefaultMutableTreeNode( "import 7" ) );
31
32 DefaultMutableTreeNode classNode = new DefaultMutableTreeNode( "class" );
33 root.add( classNode );
34 em.add();
35 classNode.add( new DefaultMutableTreeNode( "method 1" ) );
36 em.add();
37 classNode.add( new DefaultMutableTreeNode( "method 2" ) );
38 em.add();
39 classNode.add( new DefaultMutableTreeNode( "method 3" ) );
40 em.add();
41 classNode.add( new DefaultMutableTreeNode( "method 4" ) );
42 em.add();
43 classNode.add( new DefaultMutableTreeNode( "method 5" ) );
44 em.add();
45 classNode.add( new DefaultMutableTreeNode( "method 6" ) );
46 em.add();
47 classNode.add( new DefaultMutableTreeNode( "method 7" ) );
48 em.add();
49
50 DefaultMutableTreeNode innerClass = new DefaultMutableTreeNode( "inner class" );
51 classNode.add( innerClass );
52 em.add();
53 innerClass.add( new DefaultMutableTreeNode( "inner method 1" ) );
54 em.add();
55 innerClass.add( new DefaultMutableTreeNode( "inner method 2" ) );
56 em.add();
57 innerClass.add( new DefaultMutableTreeNode( "inner method 3" ) );
58 em.add();
59 innerClass.add( new DefaultMutableTreeNode( "inner method 4" ) );
60 em.add();
61 innerClass.add( new DefaultMutableTreeNode( "inner method 5" ) );
62 em.add();
63 innerClass.add( new DefaultMutableTreeNode( "inner method 6" ) );
64 em.add();
65
66 DefaultMutableTreeNode enumNode = new DefaultMutableTreeNode( "enum" );
67 classNode.add( enumNode );
68 em.inc();
69 enumNode.add( new DefaultMutableTreeNode( "enum value 1" ) );
70 enumNode.add( new DefaultMutableTreeNode( "enum value 2" ) );
71 enumNode.add( new DefaultMutableTreeNode( "enum value 3" ) );
72 enumNode.add( new DefaultMutableTreeNode( "enum value 4" ) );
73 enumNode.add( new DefaultMutableTreeNode( "enum value 5" ) );
74 enumNode.add( new DefaultMutableTreeNode( "enum value 6" ) );
75 enumNode.add( new DefaultMutableTreeNode( "enum value 7" ) );
76
77 JTree tree = new JTree( root );
78 JFrame frame = new JFrame();
79
80 // might want EXIT_ON_CLOSE if running from command line
81 frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
82
83 frame.getContentPane().add( tree );
84 frame.setSize(400, 600);
85 for (int row : em.getModel()) {
86 tree.expandRow(row);
87 }
88
89 frame.setVisible( true );
90 }
91 }
92 );
93
94 }
95 catch ( Exception e ) {
96 e.printStackTrace();
97 }
98 }
99
100 public class ExpansionModel {
101 private List<Integer> model = new ArrayList<Integer>();
102 private int row = 0;
103
104 /**
105 * @return The expansion model, set this in SideKickParsedData.
106 */
107 public List<Integer> getModel() {
108 return model;
109 }
110
111 /**
112 * Call this for each visible row in the tree that should be expanded.
113 * This will add the current row number to the model and automatically
114 * inc.
115 */
116 public void add() {
117 model.add( row );
118 inc();
119 }
120
121 /**
122 * Call this for each visible row in the tree.
123 */
124 public void inc() {
125 ++row;
126 }
127 }
128
129 }