Michael Angstadt's Homepage


How this page works

More blog entries >>

08/24/2021 10:52 am

Google Drive is a free cloud-based file storage service provided by Google. It allows you to browse, upload, and download files in the cloud using a web browser. For more convenient access to your files, Google provides a Windows desktop application that allows you to access your Google Drive files through File Explorer without needing to use a web browser.

This application used to be called “Backup and Sync”. A new and improved version, not-so-creatively dubbed “Drive for desktop”, has recently been released. This new version is considerably different from its predecessor.


The biggest improvement in my opinion is the ability to stream files. With Backup and Sync, you had no option but to download a copy of every single file to your computer (Drive for desktop calls this “mirroring”). This approach is problematic if you are short on disk space or have a slow internet connection. While Drive for desktop continues to support mirroring, it also provides a second option called “streaming”. This means that the files are only downloaded when you open them, saving a lot of disk space and bandwidth. Files and folders that you need offline access to can be marked as such using the right-click context menu.

Microsoft Office support

Another improvement is better integration with Microsoft Office. When you have an Office file open, such as a Word document, Drive for desktop will notify you if the file was changed by somebody else. This helps to prevent you from blowing away edits made by somebody else you have shared the file with. However, if you are doing a lot of collaboration work, I recommend using Google’s web-based office suite instead (Google Docs, Google Sheets, etc), as it handles simultaneous, collaborative editing much more effectively.

Backing up external drives

As with Backup and Sync, the new app makes it easy to back up any external drives, like flash drives, that you plug into your computer. Upon connecting a drive, a popup immediately appears asking if you want to back the drive up or not. Unfortunately, unlike Backup and Sync, there is no option to completely disable these notifications.

Location in File Explorer

An interesting change is where it puts the files in File Explorer. With Backup and Sync, it simply stored the files in a folder at the root of your user directory. Drive for desktop, however, takes the meaning of “drive” quite literally: it stores the files in their own drive under “This PC”, as if it were a flash drive or external hard drive. It assigns the drive to letter “G” by default (for “Google” I presume), but it is possible to change the drive letter in the settings. Every Google account you add gets its own drive with its own letter. One feature I wish it offered was the ability to customize the drive label, which defaults to “Google Drive” if you just have a single account connected, or “<email address> - Google Drive” (truncated based on the max character length of this field) if you have multiple accounts connected. You can change the label yourself in File Explorer, but the change is not preserved between reboots.


Overall, I would say the new app is an improvement over the old one. Google has not forced Backup and Sync users to update yet, but you also cannot download Backup and Sync anymore. The download page only offers Drive for desktop for download. See the full feature comparison listing.

01/12/2021 1:24 pm

At the library where I work, we recently purchased a paid TeamViewer license. This allows us to conduct remote computer classes and provide socially distanced patron computer support during the pandemic. This blog post contains some of the things I have learned about TeamViewer during this process.

What is it?

TeamViewer is a remote access application which allows you to view the screen of another computer over the internet, as well as control the mouse cursor and keyboard. In this way, it is like Remote Desktop, but the similarity ends there. TeamViewer is a "zero config" service, which means it does not require the complicated network setup that Remote Desktop requires to be functional over the internet. To make this possible, all traffic is routed over port 80 through TeamViewer’s servers. The data is protected with end-to-end encryption, so nobody, not even the TeamViewer server admins, can eavesdrop on your session.

Also unlike Remote Desktop, it does not interact with the Windows login system. With Remote Desktop, you must supply the login credentials of a Windows user account to initiate a connection. But with TeamViewer, you directly see what is being displayed on the remote computer’s screen, as if you were sitting in front of it. For example, if you logout of the current Windows user account while using TeamViewer, you are shown the Windows login screen. Doing this whist using Remote Desktop would cause your connection to be terminated.

Instead of using IP addresses to connect to remote computers, TeamViewer assigns each computer a nine-digit, globally unique ID. This number never changes, even if you uninstall and reinstall TeamViewer (which makes me wonder if the ID is stored in the registry or if it is associated with your IP or MAC address). It also gives you a random, 6-character password, which the person connecting to your computer must know in order to connect. This password changes every time you open TeamViewer, which prevents someone who connected to you in the past from connecting again without your permission.


TeamViewer’s license states that you may use it for free so long as you are only using it for personal use. What is "personal use" exactly? If you’re using it to connect to a friend or family member’s home computer, that’s considered personal use. The moment you use it to connect to a computer at work or to a server, you are expected to purchase a license. If you don’t have a paid license and TeamViewer’s algorithms think that you are using it non-personal purposes, it will put a block on your computer’s ID, which prevents you from initiating and receiving connections. How it makes this determination, I don’t know. But if you think you have been wrongly accused, there are ways to submit a request to have them unblock you.

One thing that is restrictive about the paid plans is that it heavily limits how many computers can have active connections open at time. TeamViewer calls these "channels". A channel is created when a computer initiates a connection to one or more computers. For example, if Computer A remotes into Computer B, that’s considered one channel. A single channel can include multiple connections. For example, if Computer A remotes into Computers B, C, and D at the same time, that’s still a single channel.

The least expensive paid plan only allows a maximum of one channel. So, if you install TeamViewer on two computers, only one of those computers can initiate remote connections at a time (both computers can still receive incoming connections, it’s just that only one computer at a time can create outgoing connections). If another computer associated with your license has a channel open and you try to create a new channel by connecting to a remote computer, you will get an error message that blocks you from doing so.

The more expensive plans allow you to add more channels, and they come at a hefty price tag. We decided to purchase 1 additional channel, which would have costed us an additional $778/year. But because the library is a non-profit organization, we were able to obtain a 60% discount through TechSoup, which is a website that sells software at reduced prices to non-profits.

Variants of the software

There are three different variants of the TeamViewer software.

TeamViewer: Listed at the top of the download page on their website, this is the full-featured software application. With it, you can both connect to other computers and have other computers connect to you.

TeamViewer QuickSupport: This is a good choice for when you want to do a one-off computer support session with someone who is sitting at their computer. It allows other computers to connect to you, but does not give you the ability to connect to other computers. One nice thing about this application is that it does not actually install anything onto the computer—it’s just an EXE file the user downloads and runs. With a paid TeamViewer plan, you can customize the way the QuickSupport window looks, which is useful for displaying your business’s logo and branding.

TeamViewer Host: This is the best choice for when you need remote access to computers that are under your control (as opposed to the computers of random people on the internet). Like QuickSupport, it only allows incoming connections. But unlike QuickSupport, it installs software onto the computer, which automatically launches when Windows boots. If you’re installing this on a server, you’ll want to enable unattended access by assigning it a password that never changes.

Other features

Integrated voice/video chat: Talk with the person on the other end directly through TeamViewer without needing to maintain a second line of communication (e.g. phone call or VoIP call).  In my research, I have not been able to find any other remote access software product that has this capability. The audio quality is fine, and I’ve never had problems understanding people.

Clipboard syncing: TeamViewer supports seamless copy and paste between your local system and the remote computer. Not all remote access software supports this.

File transfer: There are several ways TeamViewer allows you to copy files between computers. Note that the transfer speed is quite slow (seems to be capped around 1 Mbps), so it’s not great for large files.

  • File browser: This is similar to an FTP client in that it allows you to browse the remote computer’s entire file system (or, at least, the folders that the remote user has access to) and download any files you want. You can also upload files to any location of your choosing.
  • File box: Allows you to upload individual files to a drop box, which the person on the other end then downloads from the drop box.
  • Clipboard transfer: Just like you can "copy and paste" files in File Explorer to make copies of file, you can do the same with TeamViewer to transfer a file to the remote computer.

Multiple monitor support: If the computer you are connecting to has multiple monitors attached to it, TeamViewer allows you to switch between them with ease or display them all at once.

My Verdict

My two main criticisms are the channel limits and price. I feel that channel limits can be very restrictive when you are working in a team, and the software seems expensive compared to alternatives.

But on the flipside, you definitely get what you pay for. Think of TeamViewer as the iPhone of the remote desktop world. The service is reliable, and the software is very easy to use. It was the only remote access software I could find that had integrated voice chat, which is feature that we needed to have. The company is based in Germany, a country that is subject to strict European privacy laws such as the GDPR, which is reassuring from a security and privacy standpoint. Oh, and did I mention the user interface has a dark theme? ;-)

If you’re an IT technician that just needs remote access to a handful of machines, there are other less expensive solutions out there. But for everyone else, TeamViewer provides a reliable, user-friendly solution that non-computer professionals can use with relative ease.

06/11/2020 2:26 pm

05/27/2020 4:24 pm
Over the past several months, I have been playing a factory-building game called Satisfactory. The game involves a lot of number crunching for calculating the input and output rates of the machines. You also have to think about how to optimally arrange the machines and conveyor belts in physical space on the factory floor. Because of this, I've found it to be much easier to design my factories outside of the game so that my in-game time can be spent actually building them!

I had decided to use Microsoft Excel to create these factory designs since I was already familiar with Microsoft Office's shape drawing and image editing tools. For the past ten months, I've been sharing many of my designs with the community via Reddit and Google Drive (I've built up a bit of a reputation for being the "floor plan guy"). I've become quite adept at using Excel for this purpose, so I thought I would share some of the things I've learned.

1. Snap to Grid

The "Snap to Grid" setting is crucial for helping to keep your shapes aligned with the worksheet grid lines.

To toggle it, go to: [ Page Layout tab > Arrange group > Align button > Snap to Grid]

2. Select Objects

The Select Objects tool allows you to select multiple objects by clicking and dragging to draw a selection box. Any object that is fully inside of the selection box will be selected. Objects that are only partially inside the box will not be selected. While this tool is activated, only objects can be selected--the cells inside of the actual spreadsheet cannot be selected.

To toggle it, go to: [ Home tab > Editing group > Find and Select button > Select Objects ]

3. Quick Access Toolbar

Located in the upper-left corner of the window, the Quick Access Toolbar serves as a holding place for your personal collection of favorite commands. Any button that's on the ribbon can be added to this toolbar. I've added buttons for "Snap to Grid" and "Select Objects" because I use these commands so much.

To customize the Quick Access Toolbar, click on the arrow on the right side of the toolbar and click "More Commands".

4. Grouping

Sometimes, you need to create multiple objects to achieve a specific goal. For example, to create the "clipboard" graphic pictured below, I needed:
  • An image for the "board" part of the clipboard
  • An image for the "clip" part of the clipboard
  • An image for the pencil
  • A textbox for the paper

To help manage complex collections of objects like this, you can group them together. Grouping allows multiple objects to be treated as a single object, allowing you to move and resize them as a whole. Even though they are grouped, you can still manipulate each individual "sub" object by clicking on them to select them.

To group a collection of objects, select them all by holding down the Shift key and clicking on each object (or by using the Select Objects tool, described above). Then, go to: [ Page Layout tab > Arrange group > Group button > Group ]. They can be ungrouped by selecting "Ungroup" from the same menu.

5. Nudging objects with the arrow keys

It can be difficult to precisely position an object using just the mouse. The arrow keys on the keyboard can be used to "nudge" an object in a particular direction. Tapping an arrow key will move the object one pixel at a time. Or, if Snap to Grid is enabled, the object will move one column/row at a time.

6. Zoom level issues

My experience has been that the sizes and positions of objects change slightly when you adjust the zoom level. To keep everything precise, I find it helpful to only move and resize objects at a specific zoom level. The zoom level that I work at is 100%.

It's also useful to note that the way Excel renders text can change when you adjust the zoom level. For example, you may have a textbox that wraps in a certain way at one zoom level, and then wraps differently at another zoom level. For example, the images below show the same textbox at two different zoom levels. Notice how the word wrapping changes starting with line 3.

To adjust the zoom level, use the slider in the bottom-right corner of the window or roll the mouse scroll wheel while holding Ctrl.

7. Paste as image

When an object, or group of objects, is copied to the clipboard, Excel will automatically generate a paste-able image when you paste into any image editing program, such as Photoshop or Paint. It even takes transparency into account! For example, if you copy a textbox that has a drop shadow, the drop shadow will render as semi-transparent pixels. This is useful for generating images out of the objects you've created in Excel (as I have done with my factory floor plans).

8. Image quality settings

By default, when you save an Excel file, Excel compresses all images to reduce the size of the file. For example, if you've inserted an image that's 800 pixels wide, and then resized it to be 400 pixels wide, Excel will down-sample the image to 400 pixels. The downside to this is that, if you later decide to make the image larger, it will look distorted because you've lost the original, 800 pixel version of the image.

To force Excel to preserve the original quality of all images, follow the steps below. These settings are applied only to the spreadsheet file you currently have open (they are not global). And if your spreadsheet already has images in it, you'll have to re-insert them to restore their original sizes.
  1. Go to [ File > Options > Advanced ].
  2. Scroll down to the "Image Size and Quality" section.
  3. Click the "Do not compress images in file" checkbox.
  4. Change "Default resolution" to "High fidelity".


I hope that my list of tips help you to "excel" at Excel! Happy spreadsheeting!
04/12/2020 10:01 pm
I maintain a handful of client-side Java programs that use Java's older GUI API called Swing. One thing that I have found awkward about Swing is the way dialog boxes are handled.

Swing provides a class called JOptionPane for creating simple dialog boxes. It alleviates the need to manually code your own, which saves a lot of time. All you have to do it make a single method call and your dialog appears.

While the class works just fine, some of its methods have up to eight parameters. When a method has more than three or four parameters, it makes it hard for someone who is not already familiar with the API to tell what the code does at a glance.

For example, the code below has a total of eight parameters. Unless you are already familiar with this method's signature, you probably don't have a clue as to what half of these parameters do.
int choice = JOptionPane.showOptionDialog(
    "The password you have chosen may not be secure.\n\n" +
      "Please keep the following guidelines in mind:\n\n" +
      "1. Eight or more characters long.\n" +
      "2. Is not a word from the dictionary." +
      "3. Completely different from all your other passwords.\n\n" +
      "Would you like to create a better password or continue?",
    "Security warning",
    new Object[]{"Pick a new password", "Continue"},
    "Pick a new password"
One thing you could do to make the code more readable is assign each parameter to a variable with a descriptive name. This allows a programmer who is not familiar with the method to see at a glance what each parameter is for.
Component parentWindow = this;
String text = 
    "The password you have chosen may not be secure.\n\n" +
    "Please keep the following guidelines in mind:\n\n" +
    "1. Eight or more characters long.\n" +
    "2. Is not a word from the dictionary." +
    "3. Completely different from all your other passwords.\n\n" +
    "Would you like to create a better password or continue?";
String title = "Security warning";
int buttons = JOptionPane.YES_NO_OPTION;
int messageType = JOptionPane.WARNING_MESSAGE
Icon icon = null; 
Object[] buttonLabels = new Object[]{"Pick a new password", "Continue"}
Object defaultButton = "Pick a new password";

int choice = JOptionPane.showOptionDialog(parentWindow, text, title, buttons, messageType, icon, buttonLabels, defaultButton);
However, there are still a some problems with this:
  1. Variable name conflicts: The variable names could be confused with other variable names in the same scope. For instance, the name "text" is fairly generic, so it's not unlikely that another variable a dozen or so lines down could have the same name.
  2. Parameters are ordered: It is very easy for the programmer to get the parameter order wrong without the compiler noticing. For example, if the "buttons" and "messageType" variables were swapped, the compiler wouldn't notice. The programmer likely wouldn't notice either because they are next to each other in the method signature.
  3. Null parameters: One of the parameters is null. The code would be more concise if we could just leave it out, but the JOptionPane class does not provide an appropriate method signature for that.
  4. Misleading code formatting (1): The way the message text string is formatted in the code makes it look like each line will end with a single line break. However, some lines end in two line breaks.
  5. Misleading code formatting (2): Each line of the message appears on its own line in the code. This makes it much easier to read! However, you may not have noticed that the fourth line is missing a line break. Because of the way the code is formatted, an error like this can easily be overlooked.
  6. Neglecting native system settings: The code may not be using the native system's preferred line break sequence. Appending "System.lineSeparator()" (or even a concisely-named "newline" variable) onto each line would be technically correct, but it would make the code less consice.
  7. Duplicate data: One of the button labels is duplicated.
  8. Ambiguous types: Defining button labels as Objects will lead to problems only detectable at runtime if an Object that JOptionPane does not accept is passed into the method (also, it's just weird!).
To resolve these issues, I designed a class that acts as a fluent wrapper around JOptionPane. Using this wrapper class, the above code would look like this:
int choice = DialogBuilder.warning()
    .title("Security warning")
        "The password you have chosen may not be secure.",
        "Please keep the following guidelines in mind:",
        "1. Eight or more characters long.",
        "2. Is not a word from the dictionary.",
        "3. Completely different from all your other passwords.",
        "Would you like to create a better password or continue?")
    .buttons(JOptionPane.YES_NO_OPTION, "*Pick a new password", "Continue")
The above problems are addressed as follows:
  1. Variable name conflicts: No variables are needed because the method name provides context. 
  2. Parameters are ordered: Each parameter is replaced by a method call. Methods can be called in any order.
  3. Null parameters: The class internally provides a default value for every optional parameter. The method call for this null parameter can be left out, making the code more concise.
  4. Misleading code formatting (1): The "text()" method takes a single vararg parameter, where each parameter is a line of text. If a line ends in two line breaks, an empty string is provided, making it clearer that an empty line will appear on the dialog box.
  5. Misleading code formatting (2): Line breaks are automatically added by the "text()" method.
  6. Neglecting native system settings: The "text()" method uses the native system's preferred line break sequence.
  7. Duplicate data: The default button is identified by an asterisk character, which is removed when the button label is passed into the JOptionPane class. This is not as obvious as I would like it to be, as it requires detailed reading of the Javadocs, but it is very concise.
  8. Ambiguous types: The button labels are defined as Strings in the "buttons()" method, not Objects. This means the compiler can prevent invalid objects from being passed in. 
The full class can be viewed here: https://github.com/mangstadt/emc-shopkeeper/blob/master/src/main/java/emcshop/gui/DialogBuilder.java
More blog entries >>

How this page works

Last Updated: 1/3/2012

My blog is actually hosted on blogger.com. The way I'm able to display my blog posts here is by parsing the blog's RSS feed. RSS feeds are used by blogs to help alert their avid readers whenever a new post is created. They are just XML files that contain data on the most recent blog posts. They include things like the title and publish date of each post, as well as the actual blog post text. I can use most of the data from my RSS feed without any trouble, but there are a few things I need to tweak in order to display everything properly.

View the source

Fixing the code samples

One tweak is fixing the code samples I often include in my posts. Blogger replaces all newlines in the blog post with <br /> tags. This is a problem because, due to the syntax highlighting library I use, the <br /> tags themselves show up in the code samples. So, I need to replace all of these tags with newline characters. However, I can't just replace all <br /> tags in the entire blog post because I only want to replace the tags that are within code samples. This means that I have to use something a little more complex than a simple search-and-replace operation:

$content = //the blog post
$contentFixed = preg_replace_callback('~(<pre\\s+class="brush:.*?">)(.*?)(</pre>)~', function($matches){
	$code = $matches[2];
	$code = str_replace('<br />', "\n", $code);
	return $matches[1] . $code . $matches[3];
}, $content);

Here, I'm using the preg_replace_callback PHP function, which will execute a function that I define every time the regular expression finds a match in the subject string. I know that each code sample is wrapped in a <pre> tag and that the tag has a class attribute whose value starts with "brush:", so I use that information to find the code samples. Then, for each match the regular expression finds, it calls my custom function, where I have it replace the <br /> tags with newlines.

Fixing the dates

Because the publish dates of each blog post in the RSS feed are relative to the UTC timezone, I also have to make sure to apply my local timezone to each date. Otherwise, the dates will not be displayed correctly (like saying that I made a post at 2am in the morning).

$dateFromRss = 'Tue, 20 Dec 2011 02:30:00 +0000';
$dateFixed = new DateTime($dateFromRss);
$dateFixed->setTimezone(new DateTimeZone('America/New_York'));

Adding Highslide support to images

One extra feature that I included is adding Highslide support to each image (Highslide is a "lightbox" library which lets you view images in special popup windows). To do this, I load the blog post into a DOM, use XPath to query for all links that have images inside of them, and then add the appropriate attributes to the link tag.

$content = //the blog post

//XML doesn't like "&nbsp;", so replace it with the proper XML equivalent
//see: http://techtrouts.com/webkit-entity-nbsp-not-defined-convert-html-entities-to-xml/
$content = str_replace("&nbsp;", "&#160;", $content);

//load the text into a DOM
//add a root tag incase there isn't one
$xml = simplexml_load_string('<div>' . $content . '</div>');

//if there's a problem loading the XML, skip the highslide stuff
if ($xml !== false){
	//get all links that contain an image
	$links = $xml->xpath('//a[img]');
	//add the highslide stuff to each link
	foreach ($links as $link){
		$link->addAttribute('class', 'highslide');
		$link->addAttribute('onclick', 'return hs.expand(this)');

	//marshal XML to a string
	$content = $xml->asXML();
	//remove the XML declaration at the top
	$content = preg_replace('~^<\\?xml.*?\\?>~', '', $content);
	//trim whitespace
	$content = trim($content);
	//remove the root tag that we added
	$content = preg_replace('~(^<div>)|(</div>$)~', '', $content);

As you can see, the blog post text has to be awkwardly manipulated in order to be read into a DOM and written back out as a string. That's why I have a lot of comments here--when I have to revisit this code in 6 months, I won't be totally confused.

Caching the RSS file

One last thing to mention is that I cache the RSS file so that my website doesn't have to contact Blogger every time someone loads this page. When the cached file gets to be more than an hour old, a fresh copy of the file is downloaded from Blogger.

Back to top