04 December 2007

PNGs - One Image To Rule The Navbar

Graphics for the navigation bar take up 181 bytes of space
A realization struck me today. With PNG files, I can create one tiny image for an entire navigation bar, including mouseovers. Specifically, I was thinking how often I only want to change a color on mouse-over, which has normally meant a separate image, but no more. And it gets better... using this one image method, I can even dynamically change the color of the "image" with CSS or Javascript if I need to. Did I mention it's cross-browser compatible?

The One Image Way

A lot of folks tired of dealing with the tedium of image-based navigation buttons are probably already using the tileable background approach. This is where you create a small sliver of a background and repeat it in the x direction (for horizontal navigation bars), and place your text-based links on top. One Image takes that a step further.

Create the tileable background, but design the image with a solid background color and layer your effects on top of it rather than integrating blends and gloss into the background. Then delete the background color and save only the effects. (This takes some practice if you're not used to it.) Then, in the CSS background property, specify the image as well as the background color, like so:


...
background: #000 url(menu-fx.png) repeat-x ;
...


Now, if you want to change the hover, use the same background image, but only change the color:


...
background: #025 url(menu-fx.png) repeat-x ;
...


Working Example

To see an example of this in action, check out this page. The total amount of space taken up by graphics for this navigation bar is 181 bytes! And I can change the color at design time without saving out a new image (by specifying a different color in the CSS). And I can even change it dynamically with client-side script. If I do have to make changes to the shading or lighting, I only have to save down 1 file, not one file for every color.

Why It Works

This works with PNG files, but not GIFs, because PNG saves partial-transparencies. Those partial transparencies will be blended at run time with whatever color is behind it. So, in our case, the light effects blend at run time with the background color we specify. Contrast with GIFs, which saves transparent or not transparent, but no in-between. So, you have to know ahead of time the color of the background. If you later change background colors, you have to re-save the image with the proper blending around the edges or it will have noticeable distortions around the transparent parts.

Small Caveat

PNGs do not render transparency correctly by default in IE 6 and below. However, there's a fairly easy fix for this. If you follow that site's directions for inserting the script it will only run on browsers that need it. It won't even get loaded on the others! The script essentially invokes a Microsoft proprietary transform (filter:progid:DXImageTransform.Microsoft.AlphaImageLoader, for the twisted among you that has to know) which correctly renders the image.

There are other scripts out there that accomplish this same thing, but this is the first one I ran across, and it has worked like a charm for me.

What's Next

One day, I'd like to see a cross between a programming API and an image format where I can store all my variations of a graphic in one file and change attributes of it at run time (like background color, or size) as needed for the task at hand through client-side code or CSS. It will require some intelligent design and layering on the part of the graphic artist, but it's reuse value will be extremely high.

29 November 2007

ASP .NET 2.0 User Controls

I'm struggling to find a good use for User Controls.

All I wanted to do was wrap some standard web controls (like a login box) in a pretty container. .NET's default theming is insufficient for this, as it allows you to theme certain properties, but doesn't allow you to customize the control to any large degree. For instance, what I wanted to do was create a pretty table-based box, which is about 19 lines of normal HTML/ASP:


<table class="prettyBox" cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="nw"><br /></td>
<th class="n"><asp:Label ID="title" runat="server" Text="Label"></asp:Label></th>
<td class="ne"><br /></td>
</tr>
<tr>
<td class="w"><br /></td>
<td class="contents">
<asp:PlaceHolder ID="content" runat="server"></asp:PlaceHolder>
</td>
<td class="e"><br /></td>
</tr>
<tr>
<td class="sw"><span></span></td>
<td class="s"><span></span></td>
<td class="se"><span></span></td>
</tr>
</table>


And be able to specify them like so:


<uc:prettyBox ID="pb1" runat="server" Title="Log In">
<asp:Login ID="login1" runat="server"></asp:LoginControl>
</uc:prettyBox>


or even:


<uc:prettyBox ID="pb1" runat="server" Title="Log In">
<ContentTemplate>
<asp:Login ID="login1" runat="server"></asp:LoginControl>
</ContentTemplate>
</uc:prettyBox>


I was able to get the latter to work to a very limited degree, but it had enough limits that it wouldn't work for me.

First, I looked at a templated control from MSDN. Unfortunately, Microsoft's idea of a templated user control revolves more around the data than the UI, so this example is no help.

Another way is found here, and it correctly renders the controls inside the PlaceHolder, but in VS 2005, the designer refuses to keep tabs on any controls inside my user control, so without some manual accounting on my part (i.e. declaring the controls as page variables manually), I lose programmatic access to any controls inside the user control, which is unacceptable.

I found that I could user Mr. Seder's code and make the code designer behave correctly by putting these lines over the class declaration of the user control:


[Designer("ContainerControlDesigner")]
[ParseChildren(false)]


But this caused the ContentTemplate to be completely ignored, so all the controls inside my user control would render after the pretty box rather than inside it's PlaceHolder.

The closest working thing I found was this, but that was a lot more hassle than I wanted just to save myself 15-17 lines of code for each box, and all the controls were created dynamically. It's essentially a server control, which defeats the whole point of a user control that you can see more easily from a visual design standpoint.

I find it very frustrating that I can't make a simple user control that wraps HTML around other controls in ASP .NET 2.0. Probably what I'll end up resorting to is making two user controls, one called prettyBoxStart with a title attribute, and one called prettyBoxEnd, both of them being unary tags:


<uc:prettyBoxStart ID="pbs1" runat="server" Title="Log In" />
<asp:Login ID="login1" runat="server"></asp:LoginControl>
<uc:prettyBoxEnd ID="pbe1" runat="server />


It's pretty lame, and it's barely better than an include, but at least it works, unlike all that user control junk.

26 November 2007

Leopard Tricks and Changes

I've installed Leopard a few times now. (2 home machines and 1 work machine twice). Here are some tips from my experience.

P.S. ps is different

[Edit: As of 10.5.1 or 10.5.2, they changed it back to the BSD style command-line arguments.]
Those of you who do linux system admin probably know the ps command. In Tiger, the ps command used BSD syntax (e.g. ps -aux to get all processes, with -e not working), but in Leopard, it seems that the Linux syntax is now favored (e.g. ps -ef to get all processes). The -u flag of ps no longer works and will give a warning.

Build a New Firewall

When I was restoring my laptop, one of the things that did not translate over (probably because it didn't translate from the upgrade) was my firewall. I had to reconfigure the firewall in Leopard. Fortunately, I only had one custom entry, but still...

Restoring From an Unsupported Drive

You may or may not have seen the nice little command that lets Time Machine backup to SMB shares:

defaults write com.apple.systempreferences TMShowUnsupportedNetworkVolumes 1

Well, one of the things that was sketchy about the whole thing (aside from backing up over wireless) was how you're going to restore that backup. I did this and I'll tell you how.

Note: I used the User Migration Assistant, not the Leopard DVD to restore the backup. I played at using the DVD, but couldn't get the defaults write command to work (read-only filesystem of the DVD, I guess), so I figured it was pointless. However, I didn't actually try to mount_smb the filesystem, hoping that Time Machine would already be able see it.

Assumptions:
You used the defaults write command to allow Time Machine to backup to unsupported network drives (e.g. SMB share).
You successfully made a recent backup to the SMB share.

Firstly, you need to do a fresh install of Leopard on the target machine. When you get to the point where it asks you for a username, make sure the new username is different from the other usernames on the old system. I'd do something like tmpadmin (Temp Admin).

Secondly, log in and run the defaults write command to get Time Machine cooperative.

Thirdly, mount the SMB share you want to restore from and double-click the .sparsebundle inside the SMB share to mount it. Then start the User Migration Assistant (under Applications -> Utilities). Select the option to use a Time Machine backup. It may take it a while to pop up the SMB share as an option. Afterwards, select what you want to restore. I selected everything I could check.

Lastly, log out and back in as your normal user (assuming it's an Admin account also). Then delete the temporary account, so you don't have an extra admin account floating around.

I was actually really impressed with this. It even restored a custom /etc/hosts.allow that I created!

15 October 2007

ASP .NET 2.0 Data Woes

As I write this, I have come to the realization that there are two sure ways to drive yourself insane when doing ASP .NET 2.0 programming involving a database.

1) Use no keys in a table.

2) Try to use an SqlDataSource with a run-time-provided table name.

No Keys, No Functionality

The first one may seem fairly obvious. However, we have a database that does not have keys, because it has simply not needed them until I came along to program a front end for it. Any time you try to take advantage of some of those shiny new objects like SqlDataSource or even a GridView bound to a DataTable, the lack of keys will bite you every time you attempt an update or delete. If you have to go keyless for some strange reason, you will have to write your own event handlers and run the queries manually. You might as well go back to .NET 1.1 and handle it all yourself. SqlDataSource comes closest to working without keys, so long as you specify custom update and delete statements, and the delete statement doesn't depend on using a parameterized WHERE clause. You see, .NET 2.0 won't pass the parameters unless they are keys... even if you specify those column names as parameters.

Gotta Know Up Front

The second one was a little more elusive. The first thing I tried was to use a parameter for the table name. Essentially, you can't do that. This is because the parameter would have to be of type string (because nothing else makes sense), and .NET wraps string parameters in single quotes before executing the query. As of this writing, I have not found a way to prevent it from doing this, and your SQL server sees anything with quotes around it as a string value, rather than a database object. Also string-based functions like REPLACE or SUBSTRING return strings, not database objects, so you can't run that quoted string through them to fix it either. Perhaps it would be possible with stored procedures. How about writing new database stored procedures to work around the inflexibility of your shiny new .NET 2.0 object? Yeah, that sounds pretty lame to me too.

So after giving up on using a parameter for a table name, my next step was to try to dynamically generate SqlDataSource properties each time a different table was chosen. After all, I couldn't be sure when new tables were added, so I didn't want to create one for each table and then later go back to create more as they were added. Well, let me just say again: You might as well go back to .NET 1.1 and handle the queries yourself. You see, even if you declaratively (i.e. with a asp:SqlDataSource tag in your .aspx page) create an SqlDataSource, any properties that you assign to it programmatically are lost between trips to the server. At first I tried just changing the SelectCommand programmatically (i.e. in the codebehind page) to select from a different table, but when the user goes to page through or sort the data, the SelectCommand reverts back to it's original statement as written in the asp:SqlDataSource tag. So then in order for you to show the user the data they expect, you have to setup a page variable to store the data for those properties, and use handlers to fix the SqlDataSource after every trip to the server, like so:


<asp:GridView ID="GridView1"
...
OnPageIndexChanging="fixDataSource"
OnSorting="fixDataSource"
...


Oh, but it gets worse. When you change tables, the GridView still remembers the user's last sorting and page index, so you have to reset those when you change tables.


// remember these will also call fixDataSource as well
GridView.Sort("", SortDirection.Ascending);
GridView.PageIndex = 0;


Then there's the "what if" of wanting the user to be able to actually edit the data in the GridView.


<asp:GridView ID="GridView1"
...
OnPageIndexChanging="fixDataSource"
OnSorting="fixDataSource"
OnRowCancelingEdit="fixDataSource"
OnRowDeleting="fixDataSource"
OnRowEditing="fixDataSource"
OnRowUpdating="fixDataSource"
...


But wait, there's more:


<asp:SqlDataSource ID="SqlDataSource1"
...
OnDeleting="fixDataSource"
OnUpdating="fixDataSource"
...


So in the end, I'm left with a "What's the point?" mentality. Dynamically picking your table to select from at run time is for the hard core, not for me.

Why is that a problem? After all isn't it "job security" since they have to depend on me to update the code with a new declarative SqlDataSource when a new table is added to the database.

Well honestly Microsoft, I'd rather be less annoyed with trivial code updates. How about a way to pick the table to select from at run-time in 3.0?

27 August 2007

More on Synergy

I setup Synergy again. This time with my Mac Laptop as the server, and then my Windows desktop as the client. The Mac being the server made it where I still had my nice input device functionality in OS X and also now in Windows! It also avoided some Synergy client bugs like my screen saver Hot Corner ceasing to work, and the screen savers not syncronizing. They still don't synchronize unlocking, but at least the Windows screen saver starts when I start the OS X one.

There are various obstacles to overcome with this setup, however:

Problem 1: Synergy is not secure. All traffic is passed in plain text, so if I needed to type a password on the client, it would go across the network in plain text. This is not acceptable for my setup.

Solution 1: Setup an SSH tunnel between my desktop and my laptop. The MacBook laptop is the SSH server, and the Windows Desktop is the SSH client. After getting the initial setup working, I then took extra security hardening steps to make sure no one else could connect. These included turning off passworded logins (using only key-based logins), and creating an /etc/hosts.allow that denied all ssh connections except from my Windows desktop. (Yes, you can do it all just from hosts.allow.)

Problem 2: I need to be able to disconnect my laptop and take it somewhere else, then have it automatically reconnect when I hook it back up in my office. While the Synergy client keeps trying to reconnect, SSH tunnels may not. Also, SSH tunnels typically don't start until you log into Windows. That would mean I'd have to hook the keyboard up to the Windows desktop, log in, then hook the keyboard up to the Mac to use synergy. That kind of keyboard swapping is not acceptable either.

Solution 2: This is a fairly complicated setup, and not for the faint of heart. The use of plink from the PuTTY ssh programs in combination with the a program from the Windows 2003 Resource Kit (my desktop is running XP Pro) called AutoExNT allows me to start an SSH tunnel when Windows starts. Using an infinite loop and a sleep inside the batch file that starts the connection, I can have it retry the connection every so often so that if I take my laptop (and break the connection) and bring it back, it will automatically reconnect the SSH tunnel. You have to also set Synergy up to connect at system startup rather than at login, but that part is just a few clicks. Using this setup, I can reboot the Windows machine and move the mouse over to the Windows screen and hit Ctrl-Alt-Delete to log in over Synergy. I can also take my laptop home and bring it back, and Synergy automatically reconnects securely!

All in all, this is a lot of work to setup, but after the initial pain of setup, it's nice to be able to just plug everything up and it just works. :)

02 July 2007

Synergy showed me how great OS X is...

This is a followup to my article about switching from Windows to the Mac.

I was playing around with a really cool, open-source product called Synergy. Synergy allows you to use one mouse and keyboard across multiple computers, even computers with different operating systems. I'm using the mouse and keyboard from my Ubuntu box to type this article on my Mac. This kind of setup is very handy for system administrators, since I can have documentation open on one screen and perform commands on the other without a lot of tedious switching back and forth. Synergy even allows them to share a clipboard!

What this has to do with switching to the Mac. Well, Synergy uses the keyboard and mouse settings from the Synergy server, which in my case is Ubuntu Linux. I'm just now noticing some of the subtle features that I now don't have in OS X through Synergy keyboard and mouse emulation. For instance, mouse wheel scrolling acceleration. If you scroll the wheel really slowly in OS X, it only scrolls 1/2 or 1/4 lines, and the faster you scroll the wheel, the more lines it scrolls at a time. I use this all the time, and I only now realized it because it's missing! In Linux, the number of scroll lines is fixed for each scroll, so even if I scroll slowly, it still does the same number of lines as when I scroll quickly.

It's the little things like this that are hard to define when you're using a Mac that make it much nicer to use than pretty much anything else out there.

25 June 2007

Switching from Windows

Ever since I got my Mac in Sept of 2006, I've been thinking about switching from Windows to OS X.

Why?

Here are my top 5 reasons:

Usability

The OS X is just easy to work with. The UI is simple and clean, and most of what you want to do is already right there in front of you. Being a Windows / Linux user for years, it actually took me time to get used to that simplicity. I was used to going hunting in menus for the exact option I wanted. OS X does have its quirks, but it is much more streamlined than Windows. Let me give you some examples.

Installing a program is in most cases as simple as dragging the application icon into your Applications folder (indeed, this is how I installed Office 2004 for Mac!). The first time I tried to install an application, I honestly had no idea what to do, because that was just too simple! Even when programs use an installer, the installer UI is the same for most applications, so it is a very consistent process.

Likewise, deleting a program is as simple as dragging it to the Trash (or right-clicking, then Move to Trash). No registry to clean (or leave dirty, as a lot of Windows programs do). Only a sparse few programs that I've run across have uninstallers.

The trackpad on my MacBook is another thing that I find amazing. Specifically, I mean the scrolling. Using one finger moves the mouse, but using two will scroll just like a scroll wheel, and it also works for sideways scrolling. That is a very insightful feature, and one that I use heavily on the road. (The whole right-click issue is rather annoying, however.)

Heck, the System Preferences application (much like the Windows Control Panel) even has a Search feature to make it easy to find things. Say you are looking for Parental Controls. Just type that in the search field, and it will shine a spotlight on the User Accounts field, which is where you setup parental controls for accounts.

I could go on and on about this (as if I hadn't already), but nothing can really show you the elegance of the OS X interface like using it can.

Performance

Well, even the most beautiful UI is pointless if it doesn't perform well. OS X has been very responsive. I've installed and uninstalled quite a few programs (something that inevitably causes a slowdown in Windows). I have Folding at Home running on my Mac (which keeps my CPUs at 90-100% usage all the time), as well as at least 8 other programs running all the time when I'm at my desk (QuickSilver, iTunes, iTerm, X11 for Mac, Safari 3, iStat, Mail, iChat, and often TextEdit). Granted, I have a lot of RAM (2GB), as I thought I would be working through Parallels in Windows a lot. (The truth is, I only run Parallels to see what a page looks like in IE or to run Visio, both of which are infrequent.) As I'm typing this article, running the mentioned applications (Parallels aside), I have more than 1.5 Gig free memory, which is less than half a Gig used. MacBooks now come with 1 Gig of RAM by default, and the MacBook Pros come with 2 Gig by default, which is enough headroom to do most anything.

Integration

Just about anything you name, the Mac can integrate with it. Here at the office we have a file server that serves Windows file shares, which I connect to, and use to store documents for the office. We have mostly linux servers, which I am able to connect to and administer with default Mac utilities (although I now have other free programs that I prefer). I won't bore you with the details, but the Mac integrates into most any enterprise infrastructure as well (MS Active Directory, Novell, Kerberos, etc.).

Software Options

I can boot my Mac into Windows XP or Vista instead of OS X. I can also run those OSs and others alongside OS X with Parallels or some other VM software. The same cannot be said for the reverse case (you cannot run OS X on a PC or in any flavor of Windows, by design on Apple's part, with the exception of $500 developer versions). The Mac gives me lots of software options. However, after I gave OS X a good honest try, I didn't want to use anything else.

Hardware options are another issue that I might address in another blog entry, but suffice it to say that this is a non-issue for your average user. Power users have the option of the Mac Pro for internal cards. Computer hobbyists (people who like to take their computer apart on a regular basis) will probably prefer the flexibility of the PC, regardless. I used to do that all the time, but nowadays, I just want it to work so I can get work done!

The Future

I've seen Microsoft's idea of the future (e.g. Vista) and I have to say that I'm not impressed, and I don't want to go where they are going. And I'm not talking about the features, because Windows Vista has a lot of "new" features that OS X has had for years. I'm talking about end to end encryption/DRM that drives the price of hardware up, the performance of your computer down, and user-friendliness out the window. I'm talking about shipping products that are incompatible with hardware that worked on the previous iteration of Windows. I'm talking about Microsoft generally not having a pulse on what is most usable, but having a good pulse on what is most profitable. Microsoft has also dropped OpenGL from Vista, which causes abysmal performance on some 3D games and professional apps (specifically, ones that are most portable to other OSs like Linux and OS X).

It may be that one day down the road, Apple will miss the boat and I'll be switching back to Windows. But for the moment, a Mac running OS X is where the best user experience is found, and that's where I'm going to be found.

Conclusion

I still want to have a PC to tinker with, test hardware, experiment with Linux, etc. But for my everyday work and play, the Mac is definitely the way to go. I don't have to worry about viruses and spyware (for the moment). And it lets me do everything I normally do and do it with minimal fuss.

My last holdout for Windows XP at home is my copious amount of games. When I get an iMac, I'm going to setup a dual-boot on it so I can still play them. Also, EA Games announced at WWDC 2007 that they will start developing games for the Mac again, including their top 5 games, and all their top sports titles. Eve is working on a Mac client (though it's through emulation), and WoW (which I haven't played yet) already has a Mac client. So, the plan is to dual boot XP for older games, and get Mac versions of any new games that might interest me.

I also am starting to like Mac application development, and I plan on developing an SSH client for OS X.

01 June 2007

What's up with Ubuntu? GRUB and XFS

I was interested in trying out Ubuntu Studio, which is a VERY cool idea. However, when I went to install it with my preferred filesystem XFS, I was shocked/horrified that it tells me I shouldn't use it for /boot. XFS never hurt me before. After doing a bit of googling, I come to find out that this is a bug with Ubuntu's GRUB installer, inherited from Debian, that they still haven't fixed after almost 3 years as of this writing (first reported in September of 2004), and it's still marked with low importance.

To me, this is an unfathomable oversight. Firstly, as to whether this is a grub problem, most other major distros I've used don't have a problem with this (Mandrake, SuSE, Redhat), and there's a straightforward workaround (below). Secondly, as to the usage of XFS, it has a great feature set and nice performance. At work, it has been rock solid. XFS is GPL code, put out by a well-known vendor (at least in the HPC realm), SGI.

I found some instructions on how to work around the GRUB+XFS issue in the installer. I'll repeat them here with a little clarification.

When it asks if you want to install LiLo, hit Escape to go back to the menu,
Select the installation step for installing GRUB. Hit Yes at the prompt to continue
When it gives an error, Hit Go Back, and Continue.
Hit Ctrl-Alt-F2, then Enter to get a console, then run the following commands
# mount -t proc procfs /target/proc
# mount -o bind /dev /target/dev
# chroot /target /bin/bash
# source /etc/profile
# grub
grub> root (hd0,0)
grub> setup (hd0)
grub> quit

Stay in the console, you'll need it later.
Hit Ctrl-Alt-F1 to get back to the text installer.
Do the installation step for GRUB again. Install to the MBR.
It will freeze at 50%.
Hit Ctrl-Alt-F2 to go back to your open console
# xfs_freeze -u /boot
Hit Ctrl-Alt-F1 to get back to the installer
It should be finishing up the installation.

HTH someone

29 May 2007

Getting ready for the wedding...

Well, Rach and I are still doing some prep for the wedding. We cleaned my condo up yesterday, and now it looks like a different place. You might not believe this about me, but I really like things to be clean. But, actually cleaning stuff ranks very low on my priority list. So, I tend to let things be until it gets bad enough to where I can't stand it anymore. (I'm sure Rach won't really like that once we get married and it's not just me there.)

Anyway, we also watched a great movie, Big Fish. It's a little weird, but it tells a good story, and is an uplifting movie. I'd recommend it.

Rach is going to be trying to put the finishing touches on the wedding arrangements over the next couple of weeks, now that she's done with school. I think there are still some odds and ends to take care of on my list also.

We both are so excited!

27 April 2007

Programming Stuff

I am torn today. I am debating learning a completely new development system. Why?

1) It's supposed to be super-fast to prototype apps
2) It's cross-platform (works on Windows, Mac, Linux, etc.)
3) It can make the apps into Web-based apps or desktop apps
4) I get to use my Mac to develop it

Why Not?

1) I have to learn something new
2) It has a steep learning curve

The development system that I'm talking about is WebObjects, and it uses Java as it's underlying language.

Java is a very interesting server technology (as manifested in JSP and servlets), but as a desktop technology, the overhead of going through a VM is not quite as speedy as a traditional app compiled with something like C++. However, it is very portable, whereas a C++ binary is not. Most of what I seem to do is web-based anyway, so I might give it a shot.

24 April 2007

This blog thing and guns

Giving this blog thing a try.

One of the things of preeminence of late are the Virginia Tech shootings. My heart goes out to the families of those that were slain, and even those of the slayer. This event is probably a source of shame and hurt for them too.

Being a fairly pragmatic person, when I think about the situation, I think things like: "What could I have done?"

I'd like to think that I would have tried to tackle him or disarm him in some way. But, in truth, I will never know until I am faced with the situation (which I pray that I never am). I'd probably think about my lovely fiance and have second thoughts about doing something really dangerous for her sake.

Then I think maybe the perpetrator of the crime should have been somehow prevented in getting a gun. But he was of age. He didn't have any state-documented history of mental illness or any criminal record that would red-flag him. If he was prevented by law, then those same regulations would prevent many, many law-abiding citizens from getting guns. Even then, if he had a mind, he could still get a gun or make one. A friend of mine who is a police officer told me that he has confiscated home-made pistols on stops. It seems like tighter gun-control would only prevent those who follow the law from getting guns, not for those the law was designed to stop. I found this quote from Thomas Jefferson:

"Laws that forbid the carrying of arms . . . disarm only those who are neither inclined nor determined to commit crimes . . . Such laws make things worse for the assaulted and better for the assailants; they serve rather to encourage than to prevent homicides, for an unarmed man may be attacked with greater confidence than an armed man."
--Thomas Jefferson, quoting Cesare Beccaria in On Crimes and Punishment (1764).

And this is exactly the people that the perpetrator of the VT slayings targeted. Those that he knew would be unable to fight back. You could devote police resources to patrolling schools as a solution. However, I think a better way is to let those professors that would and are legally able (and perhaps under some added college-created guidelines) to carry a concealed gun. Even those professors that might become perpetrators would be more reluctant when they realize the professor next door may stop them short. There is at least a better chance that the degree of such a horrific event could have been lessened.