Monday, August 11, 2008

My personal Photoshop use is decreasing drastically

When I got Photoshop 4 back in the day, I used it daily to do work at home and to do my own personal creations. I also scanned in lots of photos and fixed them up.

Now that I'm a full-time developer (as opposed to part designer/part developer), I find myself using PS less and less. I've since upgraded to CS2, which in retrospect was actually quite a waste of money - PS 6 had everything I needed. My first digital camera was great (Sony DSC F-707), but almost every image really required post-processing to really look good, so I used PS quite a bit back then. I now have a Canon S2 IS, which has excellent color reproduction - good enough that I don't bother with most images anymore. And I've pretty much stopped doing my own image composites for fun (though I occasionally knock a few off for friends/co-workers).

But I still do a fair amount of image manipulation overall, considering I take tons of photos. I've found Google's Picasa software handles most of my modifications quite well (red-eye, cropping, slight color fixes) - its "Fill Light" tool is awesome for fixing underlit subjects btw, and way quicker than any method I've ever used in PS. Picasa also manages the photos better than Adobe's Bridge or Lightroom, IMHO. And I can upload straight to Snapfish. So my workflow all pretty much happens in one place.

For situations like noise reduction, I use Neat Image (free version). You can't beat its simplicity, and the results are fantastic in my experience so far (I've mostly used it to clean up high ISO noise in otherwise unacceptable-quality pictures). For stitched panoramas I use Hugin (also free). And for quick fixes, I use the free (and open source) Paint.NET application, which loads much quicker than PS, and has many of its basic tools.

There's still situations where I still use Photoshop - mostly for true HDR and HDR-like processing (levels adjustment layers, dodge/burn) to bring out detail, and I recently created a family photo for my parents from a number of different pictures that I wouldn't even consider using another application for, but it's probably dropped to about 5% or so of my total image processing time these days.

Monday, July 28, 2008

Retreiving and sending Hotmail email from Gmail

I had a Hotmail account years before Gmail was around, and I still keep it around just in case I get an important email there. I tried to use Gmail's "Get mail from other accounts" feature, but it always said "connection timed out". I had just assumed Hotmail offered POP3 - it doesn't. I found a helpful article on LifeHacker to get around this issue, but the software they mention is not a free service.

But, buried in the comments is a link to the "GetMail for Hotmail" Windows service. That link, however, sends you to the vendor's Web site, which wants you to sign up for a free trial of some other service in order to download the software. But, this Download.com link will let you download it with no strings attached.

You do have to enter your Hotmail password so that it can retreive the mail, which may be a deal-breaker for some. To be safer, I changed my Hotmail password to make sure it doesn't match any other service I use. I'm also confident that I don't use this account for any service I use, though that still doesn't technically make me 100% protected.
I'm also keeping an eye on it from a network perpective to make sure it's not trying to do anything it's not supposed to. I may actually attempt to write my own simple version of this some day just for the extra piece of mind. The program also is a resource hog, so you don't want have it set to fetch mail too often.

It does work well, otherwise, though. I've got mail set up to go directly to my Gmail archive (Settings --> Filters), and labeled with "Hotmail", so it's separate from my regular Gmail mail. I also set up my Hotmail.com account under "Settings --> Send mail as" so that I can have email sent as if it came from Hotmail.

If everything keeps working well, I'll be very glad to not have to log into Hotmail anymore.

Thursday, July 24, 2008

SQL Server 2000 not deleting backups

I'm sure there could be a lot more reasons for backup deletions failing than this, but detaching a database that was set to "offline" fixed the problem for me. No other error messages I could find - luckily I realized the problem started around the time we took that db offline, so it didn't take too long to troubleshoot.

Monday, July 14, 2008

JTabbedPane with close button that sets the tab title correctly

Java 6 includes the abilility to add a component (like a "x" to close the tab) to a tab pane, and Alexander Potochkin, has elegantly put together a "ButtonTabComponent" class to easily handle this.

I've made just a few changes to get the "x" to look more like the "x"'s on the rest of my application, and most importantly for me, to set the title of the JTabbedPane so that calling getTitleAt(tabPosition) doesn't return an empty string, since this is now handled completely by the ButtonTabComponent class. BTW, I'm mixing regular tabs with tabs that can be closed, so I want to be able to just have to call one method to check what each's title is, i.e.:

int existingTabPosition = 0;
int tabCount = tabbedPane.getTabCount();
for(int i = 0; i < tabCount; i++) {
String thisTabTitle = tabbedPane.getTitleAt(i);
if(thisTabTitle.equals(menuSelectedTabTitle)) {
existingTabPosition = i;
}
}

if(existingTabPosition != 0) {
//report already exists - just force focus to it
tabbedPane.setSelectedIndex(existingTabPosition);
} else {
//no report exists by this name - add new tab
tabbedPane.add(jrView);
int newTabPosition = tabbedPane.getTabCount() - 1;

tabbedPane.setTabComponentAt(newTabPosition, new ButtonTabComponent(menuSelectedTabTitle, tabbedPane));
tabbedPane.setSelectedIndex(newTabPosition);
}

Here's my updated "paintComponent" method of the TabButton inner class:
      //paint the "x" and update the title
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Stroke stroke = g2.getStroke();

//shift the image for pressed buttons
//changed by DG
//if(!getModel().isPressed()) {
// g2.translate(-1, -1);
//}

g2.setStroke(new BasicStroke(2));
g.setColor(Color.BLACK);
if(getModel().isRollover()) {
Color highlight = new Color(0, 51, 153); //dark blue - changed by DG
g.setColor(highlight);
}
int delta = 5; //changed by DG
g.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
g.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1);

//NOTE: only needed if button pressed in effect is used above
//changed by DG
//leave the graphics unchanged
//if(!getModel().isPressed()) {
// g.translate(1, 1);
//}
g2.setStroke(stroke);

//add the title here - added by DG
int i = pane.indexOfTabComponent(ButtonTabComponent.this);
pane.setTitleAt(i, label.getText());
}
I assume the title is set here for a specific reason, though my next experiment will be to remove the title from here and add it back to the JTabbedPane - though the way it is might actually be a bit nicer to work with. Alternatively, you can set both at the same time - it works without problems, but it seems like bad form to have to manually set the same thing twice.

Monday, July 7, 2008

Apache Commons

I had forgotten that the first Java book I read, Bruce Eckel's "Thinking In Java", highly recommended using Apache Commons if you found that the core language didn't contain something you expected to be there. The "Commons" are vast set of libraries that improve and add to the core Java language, and can be a huge time saver.

My goal this week is to read all the Commons' user guides to get familiar with its capabilities.

Tuesday, July 1, 2008

JTextArea setEnabled strangeness (Java Swing)

NetBeans 6.1 automatically wraps a JScrollPane around a JTextArea, and handles all the details of setting it up nicely. But, if you need to work with all your components at once (i.e. to tick the "setEnabled" property on each), you'll find the following code works on everything but the JTextAreas (and probably all other nested components):
for(Component component : ProjectPanel.getComponents()) {
component.setEnabled(false);
}
I haven't found a good way around this yet, as the following addition to the loop fails as well:
//use reflection to get the class type
if(component.getClass().equals(JScrollPane.class)) {
JScrollPane scrollPane = (JScrollPane)component;

for(Component subComponent : scrollPane.getComponents()) {
subComponent.setEnabled(false);
}
}
Debugging reveals that the the JTextArea isn't seen as a component here (things like the scroll bar and viewport are, though). getComponents() doesn't loop over children, so maybe this does work two or three levels deeper, but that's getting a bit ridiculous.

Until I figure out a better way, I'm setting the values explicitly using the following method:
textArea.setEnabled(false);
textArea.setBackground(UIManager.getColor("TextArea.disabledBackground"));
The "setBackground" method is needed here (at least on Windows XP using the "Classic" theme), as the background isn't automatically set to the disabled color when setEnabled is set to "true". Most other components automatically handle this for you. This also means when you re-enable it, you need to set it back to the default enabled color:
textArea.setBackground(UIManager.getColor("TextArea.background"));
BTW, I found a good article on using UIManager here that explains how to use and manipulate UI settings.

In any case, I found an official Java bug report from 1998 concerning what appears to be the exact enabled/disabled problem I ran into - the bug's status is "6-Fix Understood, request for enhancement". Though the suggested fix actually didn't work for me...

Monday, June 23, 2008

Updated enum display values code

I previously posted about how to automatically format an enum value by overriding the enum's toString() method. The code works, but it doesn't scale very well and it's not as robust as it could be, so I've pulled it out into it's own method, and added a "case exception list" feature, so it can be called from any enum, and allows for more flexibility:

public class EnumUtils {
public static String displayFormat(String enumName, String... caseExceptions) {
String[] splitNames = enumName.split("_");
String caseExceptionName = "";
Boolean caseException = false;

for(String name : caseExceptions) {
String parsedName = name.replace(" ", "_");

if(enumName.equalsIgnoreCase(parsedName)) {
caseExceptionName = parsedName.replace("_", " ");
caseException = true;
}
}

StringBuffer fixedName = new StringBuffer();

if(caseException) {
fixedName.append(caseExceptionName);
} else {
for(int i = 0; i < splitNames.length; i++) {
String firstLetter = splitNames[i].substring(0, 1).toUpperCase(),
restOfWord = splitNames[i].substring(1).toLowerCase(),
spacer = i == splitNames.length ? "" : " ";

fixedName.append(firstLetter).append(restOfWord).append(spacer);
}
}

return fixedName.toString();
}
}

which is called from the enum like this, for example:
@Override
public String toString(){
return EnumUtils.displayFormat(name(), "NT Servers", "SQL Server");
}
The method takes in the name of the enum value, as usual, but also allows the developer to specify a variable-length exception list, thanks to Java's "varargs" feature, which lets you accept an array of any basic type as the last argument. All you need to do in the method is specify the type along with ellipses (...), and it takes care of the rest for you. The code allows the developer to specify the enum value with a space between words, or with "_"'s. BTW, it also works fine with no exceptions:
return EnumUtils.displayFormat(name());
If you've seen my original code in the post below, you'll also noticed that I've changed StringBuilder to StringBuffer, and changed the String.concat()s to StringBuffer.append()s. StringBuilder is actually just meant for single-threaded operations, so while it's probably safe to use it here, it could potentially wreak havoc. The append()s are for miniscule performance gains (though I haven't run tickcount tests yet to actually confirm that this method is faster). I've also defined the new strings in the second for loop with commas between them, instead of re-typing "String" three times. I heavily doubt there's a performance gain here, as I assume the compiler sees both the same, but it's slightly faster to type :-)

BTW, I'm now using the Blogger Syntax Highlighter to display formatted code, as it looks a bit nicer, and has native support for Java.