6 7 8 9 10 11 12 13 14 15 16
A list of lesser-known OS X keyboard shortcuts

This article originally appeared on earthli News and has been cross-posted here.


The post Please share your hidden OS X features or tips and tricks yielded a treasure trove of keyboard shortcuts, some of which I knew and many that I'd never heard of or had long ago forgotten.

I collected, condensed and organized the ones I found the most useful below.

Finder & Open/Save dialogs

  • (-cmd) + (-shift) + G shows a location bar where you can type a path (/ or ~ also works in Open/Save). This text field supports ~ for the home directory and has rudimentary tab-completion.
  • (-cmd) + R reveals the currently selected item in a new Finder window.
  • (-cmd) + (-opt) + L selects your ~/Downloads folder.
  • (-cmd) + (-shift) + > shows/hides hidden files and folders (Open/Save dialogs only).
  • Dragging and dropping a file or folder into an Open/Save window re-targets that window on that file or folder

Managing applications

  • Press (-cmd) + tab to cycle through open applications.
  • Press (-cmd) + ~ (or < depending on keyboard layout) to cycle through open windows in the currently selected application.
  • While cycling, you can press q to quit the highlighted application or h to hide it
  • Press (-cmd) + (-opt) + esc to show a dialog that lets you force unresponsive applications to quit (you can even relaunch the Finder)
  • Hold down (-cmd) while clicking a dock icon to reveal that application in the Finder

Managing your Mac

  • Hold down (-opt) while clicking the "apple" menu on the top-left to execute commands (e.g. "Log out", "Shut down") without a confirmation dialog
  • Press ctrl + (-cmd) + (-opt) + (-eject) to shut down
  • Press (-cmd) + (-opt) + (-eject) to put the computer to sleep
  • Press ctrl + (-shift) + (-eject) to put the display to sleep
  • Press ctrl + (-cmd) + (-eject) to restart

Screenshots

  • (-cmd) + (-shift) + 4 lets you select an area to highlight and saves the screenshot to the desktop
  • (-cmd) + (-shift) + 4 + space lets you select an entire window, the dock, menu bar, etc. This will also include the drop shadow with transparency in the PNG screenshot.
  • Hold down ctrl with either of these to copy to the clipboard instead

Volume controls

  • Hold down (-shift) while adjusting the volume to mute the volume-changing sound (useful when adjusting volume during a call).
  • Hold down (-shift) + (-opt) while adjusting the volume to adjust in quarter-increments.
  • Hold down (-opt) while clicking the "sounds" menu-bar item to show a menu that lets you select the input/output devices without opening preferences

Screen & Brightness

  • Hold down (-shift) + (-opt) while adjusting the brightness to adjust in quarter-increments.
  • Hold down ctrl while scrolling with two fingers or the mouse wheel to zoom in/out on the entire screen
  • Hold down (-opt) while clicking the "bluetooth" or "wi-fi" menu-bar items to show a menu with more details about your connection, including options to show/generate diagnostics.
  • Hold down (-opt) while clicking the "notifications" icon to toggle it on/off
Including PDF in web sites

At first glance, this seems to be a pretty easy topic since PDFs are everywhere and can be found in almost every bigger website. But in most cases, PDF files are just linked for download and not embedded directly in the site. If the user clicks such a link, the browser decides what to do with the file: Just download to the file system or display a preview in a new tab or window. This also works pretty well for mobile devices since there are PDF readers for almost every platform.

But what if we want more than this, like embedding the document in the website and jumping to a specific page? The first part of this is very easy: We can use an iframe and set the location to the URL of the PDF.

<iframe src="document.pdf"></iframe>

This works fine on a desktop browser like chrome as it fits the width of the PDF to the width of the iframe:

image

But when we open the same page in mobile safari, it looks like following:

image

The PDF is not scaled and much worse: You can not even drag the document around. In short: This form of embedding PDF in websites is completely useless on iOS. Investigating deeper on this, it turns out that there is no way to fix this issue with a pure HTML / CSS solution.

Another solution that is worth looking at is pdf.js originally intended as a Firefox plugin to display PDF files in a platform-independent way. It renders PDF files into canvas elements using nothing more than pure JavaScript. In the hope that this will fix our PDF problem, we tried to include this JavaScript library in our web application. This worked fine for small- to medium-sized PDF files in desktop browsers as well as on mobile safari. But when we tried to display PDFs with complex content or more than 100 pages, we quickly ran into some limitations of this library: The rendering of huge PDFs was painfully slow and failed completely in some cases. I personally like this approach as it provides a platform independent solution which can be easily included in web applications, but it seems that it's just not ready right now. But maybe in a couple of months or years, this will be the way to go for displaying PDFs in web applications.

Another approach to fix this is to convert each page of the PDF into a PNG image. This approach is used widely on the web; for example by google books for the preview function. This approach is technically easy and things like zooming or jumping directly to a specific page can be implemented with a couple of lines of JavaScript. But one of the big drawbacks is that text is not submitted as text but as an image. This increases the transferred data size significantly which is, on the other hand, bad for mobile devices with their typically low bandwidth. To address this there are techniques like using the offline-application cache, which should definitely be kept in mind when using this approach.

After many hours investigating this topic, we ended up using the approach to include single pages of the PDF as PNG images in our web application. This requires that the PDF files be prepared server-side. Also, we implemented a dynamic-load algorithm which loads the images only when they are visible on the screen. This allowed us to display big documents without overburdening the video memory or the bandwidth of the mobile device.

v1.9.1: Bug fixes for 1.9 -- WAN speed, deadlocks and connection pooling

The summary below describes major new features, items of note and breaking changes. The full list of issues is also available for those with access to the Encodo issue tracker.

Highlights

  • QNO-4201: Improved WAN speed by adding connection-pooling to avoid high connection setup costs
  • QNO-4233, QNO-4239, QNO-4241: Fixed some deadlock and connection-exhaustion issues in more complex models/data structures
  • QNO-4260: Upgraded DevExpress component library to v13.1
  • QNO-4246, QNO-4247: Made several improvements to object-graph management to improve identity/reference handling
  • QNO-4270, QNO-4278: Made some schema-migration improvements to support more conversions and constraint/path types

Breaking changes

  • Removed RemoteMethodCallException and added CreatePayloadException
Ignoring files with Git

This article originally appeared on earthli News and has been cross-posted here.


The helpful page, Ignoring files, taught me something I didn't know: there's a file you can use to ignore files in your local Git repository without changing anyone else's repository.

Just to recap, here are the ways to ignore a file:

  • Global .gitignore: you can designate basic exclusion directives that apply to all repositories on your system. This file is not committed to any repository or shared with others. Execute git config --global core.excludesfile ~/.gitignore_global to set the file to ~/.gitignore_global (for example). See the linked article for sample directives.
  • Per-repository global exclusions: add directives to the .git/info/exclude file in any repository. These directives are combined with any system-global directives to form the base exclusions for that repository. This file is not committed with the repository. This is the one I'd never heard of before.
  • .gitignore: add a file with this name to any directory. The directives in that file are merged with those from the parent directory to define the patterns that are excluded in that directory and all child directories. This is definitely the most common way to exclude files.
  • Exclude versioned files: and, finally, if your repository has files that are changed but not committed (e.g. configuration files), you can ignore future changes to those files with a call to git update-index --assume-unchanged path/to/file.txt. While this can be useful for legacy projects, it's best to structure new projects so developers don't have to rely on easily forgotten tricks like this.
Some new CSS length units (and some lesser-known ones)

This article originally appeared on earthli News and has been cross-posted here.


I've been using CSS since its inception and use many parts of the CSS3 specification for both personal work and work I do for Encodo. Recently, I read about some length units I'd never heard of in the article CSS viewport units: vw, vh, vmin and vmax by Chris Mills.

  • 1vw: 1% of viewport width
  • 1vh: 1% of viewport height
  • 1vmin: 1vw or 1vh, whatever is smallest
  • 1vmax: 1vw or 1vh, whatever is largest

These should be eminently useful for responsive designs. While there is wide support for these new units, that support is only available in the absolute latest versions of browsers. See the article for a good example of how these can be used.

While the ones covered in the article are actually new, there are others that have existed for a while but that I've never had occasion to use. The Font-relative lengths: the em, ex, ch, rem units section lists the following units:

  • em: This one is well-known: 1em is equal to the "computed value of the 'font-size' property of the element on which it is used."
  • ex: Equal to the height of the letter 'x' in the font of the element on which it is used. This is useful when you want to size a container based on the height of a lower-case letter -- i.e. tighter -- rather than on the full size of the font (as you get with em).
  • ch: "Equal to the advance measure of the "0" (ZERO, U+0030) glyph found in the font used to render it." Since all digits in a font should be the same width, this unit is probably useful for pages that need to measure and render numbers in a reliable vertical alignment.
  • rem: The same as em but always returns the value for the root element of the page rather than the current element. Elements that use this unit will all scale against a common size, independently of the font-size of their contents. Theres more to the CSS rem unit than font sizing by Roman Rudenko has a lot more information and examples, as well as an explanation of how rem can stand in for the still nascent support for vw.
A rant in Ominor (the decline and fall of the Opera browser)

This article originally appeared on earthli News and has been cross-posted here.


Opera has officially released their first desktop browser based on the Blink engine (forked from WebKit). The vision behind Opera 15 and beyond by Sebastien Baberowski explains how Opera 15...

...is dead on arrival.1

Choose your market

For years, Opera has held a steady 1.7--2% of the desktop browser market. This seems small but comprises dozens of millions of users. More capitalist heads have clearly prevailed at Opera. They've struck out for a more lucrative market. Instead of catering to the 2% of niche, expert users that were die-hard, loyal fans, they will create a clone of Chrome/Firefox/Safari that will cater to a much, much wider market.

In terms of fiscal reasoning, it's not hard to see why they're going in this direction. They will abandon their previous user base -- the hardcore market -- to the thankless chore of downloading and configuring their browsers with buggy extensions that offer half-implemented versions of the features that used to be high-performance and native.

As one such user, I am saddened, but am also almost certain that there is no turning back.2 It's been a good run, though. The browser market will be quite homogenized, but perhaps some enterprising open-source project will take up the flame and build us a better Opera.

Here's how another user put it in the comments for the article,

Opera's main reason was not to spend their time on browser innovation, but to save money. Opera became misinformative, untrustworthy company, disrespectful towards long-time and power users, whose disappointment Opera now tries to appease by extensions and "future" features.

That has been my impression, as well.

Opera does too have features!

Though many of the features that defined Opera for its users are gone -- perhaps to be resurrected -- the company goes out of its way to trumpet its innovation in this latest incarnation of its browser.

The article lightly covers the same four features that they won't shut up about -- Speed Dial, Stash, Discover and Off-road Mode -- and tells loyal Opera users that if "you find that Opera 15 doesnt have a feature you depend upon, first check the growing list of extensions". In other words, Opera is now just Chrome without Google? All of the out-of-the-box features that Opera users have come to expect have just been deep-sixed? And we can all hold out hope that the community develops them for Opera? And we get to spend a ton of time evaluating, downloading, testing and setting up these extensions?

I can "discover" the web just fine on my own without Opera's help. This feature feels more like an AOL/Facebook/Google+ crutch to get me to read catered content. Where's the pro version of the Opera browser? I'm browsing on a desktop with a 150Mb Internet connection -- Off-road Mode is utterly useless for me. Just as Turbo was useless before.

Stash, the Process Model and Memory Hunger

And shall we guess why they're pushing Stash so hard? Because they want to train us to stop keeping so many tabs open. You see, keeping dozens and dozens of tabs open brings any browser other than Opera to its knees. Either that, or the browser soon takes over most of the resources of the machine on which it runs and brings the OS to its knees.

Now that Opera has inherited the process model from the Blink engine, well, they suffer from the same issues that Chrome has: it's just not very good at keeping dozens and dozens of tabs open. Kudos to Opera for at least recognizing the problem and trying to train its users to be more reasonable. It's a bit weird for Opera users to hear this, though, because that was one of the reasons we used their browser in the first place: it just worked and didn't make us change our work habits to accommodate the tool.

Next? Beta? Alpha.

The halcyon days of faster, better and slimmer are, apparently, gone. At least for now. Version 15, though it's called an official release, is, for an Opera user, not even a beta. It is, at best, an early alpha that is nowhere near feature-completeness.

I understand that you want to trim the fat: some non-browsing features can legitimately be moved to other apps or put to sleep. It's utterly arguable that a browser doesn't need it's own IRC client, an RSS reader, a mail client, something called Unite.

But intimating that "Fit to Width" is too confusing a feature and won't come back? Removing bookmarks? And sessions? And the whole "Reopen closed windows" feature? And replacing it all with a single-level Speed Dial and something called Stash? And, of course...

The article goes on to cheerfully explain that there is a bookmark manager extension. This extension comes from Opera itself and is the official recommendation from the press release/article linked above. The first few comments should be enough to scare off anyone. This isn't too surprising: the bookmark manager in Opera 12 was barely adequate and had seen little love for years. But it worked. It had folders.3 It synced via Opera Link.

All that is gone. Use Stash instead.

Oh, and anything you configure will be local to that machine until Opera Link is reactivated. No roadmap for that yet. No roadmap for anything, in fact. Just a bunch of promises that "we are looking at your comments and feedback". There's nowhere to actually register that feedback and see whether Opera's considering it (something like Microsoft's "User Voice" would be nice). I can't believe I just wrote that I wish Opera would be more like Microsoft in engaging with the community.

Who thought this was a good idea? Hey, maybe there's an extension for Opera Link? Maybe I can cut&paste my browser together from dozens of extensions? Isn't that why I was using Opera instead of another browser? And even were I to do this, I get to repeat this configuration on absolutely every machine on which I use Opera because...you guessed it: Opera Link is gone, so I don't get any data-synchronization anymore. Not for bookmarks (which are gone anyway) but also not for the Wand (which is gone anyway)4 and certainly not for extensions, which were never synced, even in Opera 12.x.5

How in the name of all that is holy is moving bookmarks to an extension a move that offers a "UI simple enough to be intuitive for a consumer who wants a solid, fast browser that just works"?

Well, of course everything just works -- your browser no longer has any features.

So the check list of features in Opera 15 consists of "show web pages" which comes free by including the Chromium project. Whoop-de-doo.

Wait and see

I can't believe I'm writing this because I've always upgraded to the latest version, but: you can stick Opera 15 where the sun doesn't shine; I'm sticking with Opera 12. I'm happy with that for now, but I know it's not a long-term -- or even medium-term -- solution. Sigh.



  1. Disclaimer: I've been using Opera since version 3.6. About ten years ago, I joined an early-tester program to help them build their Mac browser (for the egotistical reason that I wanted to use Opera on my Mac). I'm still enrolled in that program, though my participation is considerably less than it used to be.

  2. And no, the article Ctrl+Z of Ctrl+D by Krystian Kolondra, in which Opera backpedals and swears that they will restore native bookmarks, is far from reassuring. The product strategy is clear; a bit of backpedaling on one feature doesn't change very much.

  3. Even though you couldn't see the bookmark-folder hierarchy very well -- or at all on the Mac -- when selecting one in the drop-down.

  4. To be honest, I've long since moved on to LastPass because a browser-specific password solution was too limiting for my work.

  5. At least Google solved the customization problem to some degree by saving your extensions as part of your account and syncing them whenever you log in from somewhere. That's a good start. But many of the Chrome extensions are pale imitations of the classic Opera features so Chrome is at best a partially satisfactory fallback position.

v1.9: Plugins, model overlays and more!

The summary below describes major new features, items of note and breaking changes. The full list of issues is also available for those with access to the Encodo issue tracker.

Highlights

  • Plugins: You can now extend your Quino applications with plugins. The application can determine the locations from which plugins are loaded but the default behavior is to load plugin assemblies from a folder next to the executable as well as from a folder in the user's local configuration folder. Also by default, if the application is running in debug mode, C# files in those directories are compiled and loaded as plugins as well. This allows developers or demonstrators in the field to update the metadata without using Visual Studio to recompile the application.
  • Software Update: The software updater component now supports HTTP as well as file-shares and downloads the file locally before executing it, showing download progress for slower connections or larger updates.
  • Remoting Routes: QNO-4060: Routes/URLs on the remote server are now resolved using a registry of route handlers. An application is free to replace or enhance the standard route handlers or add new ones. Testing support remoting has been improved (i.e. configuration of a loopback remoting server has been vastly simplified and encapsulated in a RemoteTextMixinBase).
  • Data sources: QNO-4048, QNO-3628, QNO-4034, QNO-4037, QNO-4051, QNO-4139, QNO-4140, QNO-4071: Multiple remote data sources are now supported throughout the configuration, startup, login and application.
  • Optimistic locking: QNO-4049: Added aspects to include optimitic-locking support for meta-classes
  • Change tracking: QNO-4050: Added aspects to include change-tracking support for meta-classes
  • Model names: QNO-4152, QNO-4156: Improved support for models with long(er) names (in particular, schema migration now works as expected)
  • VS Project template: QNO-4149, QNO-4150, QNO-4151: Improved the Quino project/solution Visual Studio 2012 template
  • Error messages: QNO-4081, QNO-4166: Improved error messages for data-source errors and improved the API for formatting error user-facing messages
  • Remoting Driver: QNO-4075, QNO-4080, QNO-4068, QNO-4060, QNO-4059, QNO-4056, QNO-4037: Made many improvements and optimizations to the remoting data driver
  • Caching: QNO-140, QNO-3809, QNO-3808: Made several improvements to caching API, including new default providers for the default DataCache.
  • Many-to-many Relations: QNO-4219: Added a QueryAspect to show read-only views of many-to-many relations
  • Object comparison: QNO-4027: GenericObject should be comparable to other GenericObjects or PK values

Breaking changes

  • QNO-4026: IMetaReadable.GetValue() behavior is inconsistent with relations and methods. The behavior or reading a related object or list using GetValue() has changed. Previously, the value returned non-null only if it had been caused to load by using GetObject() or GetList()) or if it had a ValueGenerator. If it was generated with a ValueGenerator, it was always regenerated, ignoring the value of ValueGenerationFrequency.
  • QNO-4028: GenericObject should behave correctly when adding to a HashSet. Previously, the hash-code generated for GenericObjects was not well-matched to the result of the Equals() method, which resulted in unpredictable behavior with hash tables, sets or dictionaries. This has been fixed, but applications that relied on the formerly unpredictable behavior will now function differently.
  • The enum DatabaseState no longer exists. It has been replaced with ExternalResourceState.
  • The IAuthenticator<TApplication>, IAuthorizer<TApplication> and ILoginValidator<TApplication> interfaces have all acquired a method with interface void LoadSettings(TConfiguration configuration, IMessageRecorder recorder).
  • Encodo.Data.SessionFlags have been moved to Encodo.Quino.Data.Persistence.SessionFlags
  • The signature of ICoreConfiguration.GetConnectionDetails()no longer returns a simple string, but a sequence of strings. To restore , use string.Join("; "_configuration.GetConnectionDetails())
  • EnryptionType was moved from Encodo.Security to Encodo.Encryption
  • DirectoryServiceSettings.ServerUri is deprecated; use HostName instead.
  • MetaBuilderModuleTools has been renamed to MetaBuilderTools.
  • MetaBuilderBasedMetadataGeneratorBase.ConfigureBuilder() no longer exists; instead, override MetaBuilderBasedMetadataGeneratorBase.RegisterDependencies().
  • MetaBuilder.AddMultiLanguageProperty() now requires a base Guid parameter.
  • MetaBuilder.AddWrapperClass() no longer requires a Guid parameter
The HTML5 AppCache and HTTP Authentication

The following article outlines a solution to what may end up being a temporary problem. The conditions are very specific: no server-side logic; HTTP authentication; AppCache as it is implemented by the target platforms -- Safari Mobile and Google Chrome -- in late 2012/early 2013. The solution is not perfect but it's workable. We're sharing it here in the hope that it can help someone else or serve as a base for a better solution.

The HTML5 AppCache

The application cache is a relatively new feature that is,

  • Supported by all modern browsers
  • Uses a manifest file that indicates which files to cache
  • Browser checks manifest for changes
  • If there are changes, all files are refreshed
  • External links work when online
  • When offline, the application works with the local cache
  • External links to non-cached content are redirected to fallback links

AppCache Limitations

Web applications can use the HTML5 application-cache to store local content, but different browsers apply different restrictions to the amount of space allocated per domain.

  • Safari Mobile is limited to 50MB per domain. This means that the restriction will generally apply to all content packages downloaded from the same server/domain
  • Google Chrome is limited as well, but the actual limit is a bit of a moving target

Optimizing the HTML5 AppCache for Authenticated Content

In particular, the Safari Mobile browser cannot update the application cache for files for which it must obtain authentication.

  • Some requests do not trigger authentication
    • Manifest file
    • Home-screen icons
  • A lost connection or timeout can invalidate the authentication token
  • Version checks are not reliable
    • Open pages/running apps do not check for status updates
    • Home-screen apps dont reliably check on startup
    • This can lead to out-of-date or missing content

Checking for and presenting updates to the user

The graphic below illustrates the mechanism by which a content package in a web application can manage content updates and present them to the user.

  • When online, the software regularly checks whether an update for the package is available
  • The user can determine whether to install an update
  • When an update has been found, the software stops checking for updates until the user has applied the latest update
  • If the user delays the update, the user interface displays an update button
  • The software will automatically start checking for updates whenever it detects that it is online
  • There is no way for a user to ignore updates
  • When the user proceeds with an update, the latest version is retrieved at that time, ensuring that the user has the latest version

image

Solving the problems with authenticated data and the AppCache

In order to address the problems described above, the application uses a separate version file to check for updates independent of the browsers application-cache mechanism and to trigger this update only when authentication has been reestablished.

image

  • The cache.version.txt file is publicly available but is very small and includes only a unique version number that is also included in the cache.manifest file (both of which are generated by a deployment script).
  • The software compares this version number against the last known good version number. If it differs, it knows that the server has been updated with new content for this package
  • Before the software can kick off the HTML5 AppCache update process, it must ensure that the user is authenticated and authorized to retrieve the update package (because most browsers will simply fail silently if this is not the case).
  • The software pulls the force.password.txt file from the private zone with an explicit request. The browser will ask the user to authenticate, if necessary. This file is also very small to avoid needlessly downloading a large amount of data simply to force re-authentication.
  • Once the user has authenticated, the software lets the automated HTML5 AppCache update take over, retrieving first the cache.manifest file and then updating files as needed. The user is notified that this download is taking place asynchronously.
  • The software receives a notification from the browser that the update is complete and can record the version number and then notify the user that the update has been applied and is ready to use.

This approach worked relatively well for us, although we continue to refine it based on feedback and experience.

Tick, tock (death of a ticket salesman)

This article originally appeared on earthli News and has been cross-posted here.


The following story tells tale of a day spent with the ongoing user-experience (UX) catastrophe that is the interface of the SBB/ZVV automated ticket machines.

While it's certainly possible that our experiences are unique and that others can easily purchase their perhaps simpler tickets, we have found that veering ever-so-slightly from the beaten path leads into some very deep and dark weeds.

Even were we to accept that the fault for the confusion engendered by the UI lay entirely with us, we can hardly be blamed for the mysterious time-delays and crashes.

In short, SBB/ZVV: give Encodo a call. We can help you fix this.

Prologue

When I renewed my monthly train ticket a month ago, the lady behind the counter told me that I could have gotten it from a machine instead.

She handed me a little pamphlet that explained how to use their ticket machines.

I gave it back. In hindsight, this may have been a bit hasty.

The setup

My monthly pass was about to expire. I didn't need to get a whole month this time, so I figured that I'd just buy a six-pack of one-day tickets, in order to save money.

How hard could that be?

Initial encounter

Instead of heading back to the human-powered ticket desk, I took the lady's advice and approached a machine.

I got to the train station about eight minutes before my train, confident that this would be more than enough time for me to exercise my not insignificant technical skills to prise forth a ticket.

On the screen, I poked a smiling SBB stock-photo customer in the eye to begin.

I chose a normal ticket. I entered my destination. Then I elected to change the starting point (I already had a ticket covering part of the trip).

Done. This was too easy.

Now to get a multi-day ticket. Multi-day, multi-ride...what's the difference?

I touch the info icon on each. A wall of text. Scanning doesn't elucidate anything.

Tick, tock.

I select multi-ride just to keep moving.1

I'm done early. All that remains is to pay for the ticket.

I slide in my card...

It's blocked. I turn it around and try again.

Blocked.

The card reader's apparently broken.

The machine is blissfully unaware of this and pops up a helpful message, asking whether it should just forget the whole thing and cancel my transaction.

I tell it not to cancel, but to go back to payment-method selection.

Going back incurs an interminable pause.

Tick, tock.

I'll pay cash!

The machine gives a maximum of twenty bucks in change. I have no twenties; only fifties.

Can't pay cash.

Wait! I have some change! What if I get a single-day ticket?

Type, type, type

That's only CHF5.80.

Scrounge, scrounge

I have CHF5.60.

Tick, tock.

Can I just tell the conductor that the machines are broken? Does that even work?2

I look over at the second ticket machine. There's a man standing in front of it, looking back and forth between two credit cards in his hands.

Tick, tock.

He seems to be having trouble deciding how he will pay for his ... CHF197.-- ticket.3

Tick, tock.

The dam has broken. He's decided.

Printing...

Tick, tock.

I jump on the machine after he's finished.

I type in the same commands as I'd typed in just a minute before on the other machine, my fingers flying over the keys, marveling at the comparative speed of the ZVV machine vs. that of the SBB one.

Wait...why can't I change the starting point of my journey like on the other machine? Where did that option go?

Start over.

Tick, tock.

Why can't I choose a multi-ticket from this machine? Where did that option go?

Fine. Get a single ticket. Anything at this point.

Type in both towns again, muscle memory helping me along.

Laaaaagggggg as I type. Why is searching a list of a few thousand items so slow?

Choose a single ticket. Wait, why is it cheaper now? What changed? I could pay for this one with my change now.4

No time to think about it.

Tick, tock.

The bell is ringing. The barrier is lowering. The train is coming.

There's the button for multi-card! It's on the final screen instead. I barely have time to register that this is a much better place for that button as I punch it.

I can feel the rumble of the approaching train in my heels.

I jam in my credit card. The reader works! Huzzah!

I type my code. Nope, the reader's still warming up. Please hold...

Tick, tock.

I type in my code. I can hear the train now. Start printing!

Tick, tock.

Oh, the machine wants me to decide whether I want a receipt before it will do anything else.

Yes, I want a receipt! Now start printing!

The printer has started!

I see the flashing light alerting me to the imminent arrival of my hard-won train ticket, still warm from the innards of the machine.

A paper drops into the slot, lying awkwardly in the tray. It looks rather large.

"One of your products could not be printed."

I reach in and pull out a forty-centimeter--long, curled monstrosity that purports to be my multi-ticket.

In my haste, I assumed that this was a valid ticket. That this hideous thing with ink staining the front of it every which way was the product that my labors had brought into the world. I assumed that the aforementioned product that could not be printed was my receipt. I thought that while I might be in trouble with bookkeeping back at the office for not having a receipt, that at least I had a ticket for the train.5

Never mind all of that. I had a multi-ticket. An overlarge and misshapen one, perhaps, but nonetheless a ticket.

Now, one final step to make it valid.

Tick, tock.

The train is gliding into the station

I hurried over and proudly punched my ticket, the ink delineating today's date mixing illegibly into the mess of ink already printed there.6 I didn't care. The ticket had printed. I had paid. And I was getting on that train legally. With a valid ticket.

Whether I could prove it to a conductor or not was another question, but my conscience was clear.

I folded my ticket three times and put it in my backpack -- because it wouldn't fit in my wallet.

Despite my misgivings, no conductor came to make me reveal my shame.

The ride home

I got to the office and somehow the SBB came up in conversation that afternoon. I remembered my ticket and hauled its weighty length out of my backpack.

My colleague laughed and expressed sincere doubt that this was a valid ticket. He thought I might slip by with it, but was almost certain that it had been annulled.

This seemed like a reasonable theory, but I was beyond caring one way or the other.

Still, when I arrived at the station three minutes early for my trip home, I spotted a ZVV machine on my platform.

Brimming with confidence, muscle memory and experience, I staged an assault, fingers flashing as I once again unerringly entered my order.

I was flying along, typing my destination, when...

"Your order encountered an error and could not be completed."

The screen froze, wiped itself clean and restored the introductory graphic. You know, the one with the people grinning their way through a day made more magical by having been able to purchase their tickets from one of these wonderful machines.

I turned and walked away.

I would take my chances with my malformed freak of a ticket, blissful in the knowledge that the SBB couldn't hurt me anymore.

I had stopped caring.

I napped on the way home. No conductor dared to disturb my peace.

The ticket purchase. Take #2.

The next morning, I was at the station early again. More level heads had convinced me to try again and a good night's rest had restored my optimism.

I went straight for the ZVV machine, avoiding the squat and grimly brooding SBB machine with its slow screen and faulty card reader.

Type, type, type.

Destination; starting location; multi-card; full days.

Wait. What? CHF70.-? For six days?

A whole month costs CHF119.-

Argghhh. Now what did I do wrong?

Back, back, back.

Tick, tock.

Choose multi-card; 1-hour.

CHF30.- For 6 trips? That's three days of commuting.

Dammit. This is a waste of money; I might as well just get a month.

Back, back, back.

Tick, tock.

One month. Personal or transferable? Personal is cheaper; personal.

Personal card number?

What? Oh, it's on my half-fare card.

Dig half-fare card out of wallet

Tick, tock.

Train's a'comin'.

Start typing my half-fare card number.

Crash. "Your order encountered an error and could not be completed."

Breathe.

Start over. The bell's ringing. The barrier is lowering. The train's a'comin'.

Just get a six-pack of 1-hour tickets. It's the cheapest option that lets me avoid having to buy a ticket again this evening. It's the least typing I can do.

Tick, tock.

Got it. Sweet Lord almighty, I think have a valid ticket.

No, really. This time I think I have a valid ticket.

STAMP

Now it's valid.

Epilogue

On my way to my destination, I did the math again and realized that I would need another ticket in three days.

A chill ran down my spine.

Time to give up. I would return to the SBB counter and just buy a monthly ticket from a human being.

At the counter, I asked for a renewal on the recently-expired monthly ticket I handed to him, but starting on the next Monday, my hand simultaneously cramping at the thought of how much touchscreen-typing that would have entailed.

In seconds, he renewed it and handed me the new ticket, chirpily telling me,

"You could have bought this ticket on the machine instead. Just three taps and you'd have been done!"

That night, as I lay in bed, I wondered whether his family missed him yet.



  1. This would turn out to be incorrect, but it would also turn out not to matter (keep reading). Multi-day is a 24-hour pass for the day on which you stamp it; multi-ride is a 1-hour pass. The former is obviously more expensive than than the latter.

  2. A colleague would later tell me that, if you call the SBB and tell them that the machine is broken, and give your name, that you're free and clear. This seems like a lot of work on the customer's part. You try to pay for a train ride and end up working for the train company.

  3. Considering how my purchase on that machine turned out, I was happy that it had elected to mess with my purchase and not his, as he seemed to have had a much longer trip before him than I.

  4. It turned out that on the first machine I'd selected a daily ticket whereas my flying fingers had selected a one-hour ticket on the second machine. It was about 20% cheaper and fit my coin budget but I would have had to do everything again in Altstetten had I chosen that option.

  5. It turned out that I was wrong. Another colleague would tell me that day that this is the machine's way of telling me that the ticket is invalid. When it said that the second item -- my receipt -- could not be printed, it was notifying me that it had nullified the entire transaction. With a whole screen of space, the machine could have told me that in much clearer terms.

  6. Not only was the ticket invalid, but I had somehow managed to purchase the wrong zones anyway. Of the text I was barely able to decipher, I was almost certain that I didn't have the zones I needed. It explained why my ticket purchases on the next day would seem so much more expensive.

.NET 4.5.1 and Visual Studio 2013 previews are available

The article Announcing the .NET Framework 4.5.1 Preview provides an incredible amount of detail about a relatively exciting list of improvements for .NET developers.

x64 Edit & Continue

First and foremost, the Edit-and-Continue feature is now available for x64 builds as well as x86 builds. Whereas an appropriate cynical reaction is that "it's about damn time they got that done", another appropriate reaction is to just be happy that they will finally support x64-debugging as a first-class feature in Visual Studio 2013.

Now that they have feature-parity for all build types, they can move on to other issues in the debugger (see the list of suggestions at the end).

Async-aware debugging

We haven't had much opportunity to experience the drawbacks of the current debugger vis à vis asynchronous debugging, but the experience outlined in the call-stack screenshot below is one that is familiar to anyone who's done multi-threaded (or multi-fiber, etc.) programming.

image

Instead of showing the actual stack location in the thread within which the asynchronous operation is being executed, the new and improved version of the debugger shows a higher-level interpretation that places the current execution point within the context of the asnyc operation. This is much more in keeping with the philosophy of the async/await feature in .NET 4.5, which lets developers write asynchronous code in what appears to be a serial fashion. This improved readability has been translated to the debugger now, as well.

image

Return-value inspection

The VS2013 debugger can now show the "direct return values and the values of embedded methods (the arguments)" for the current line.1 Instead of manually selecting the text segment and using the Quick Watch window, you can now just see the chain of values in the "Autos" debugger pane.

image

Nuget Improvements

We are also releasing an update in Visual Studio 2013 Preview to provide better support for apps that indirectly depend on multiple versions of a single NuGet package. You can think of this as sane NuGet library versioning for desktop apps.

We've been bitten by the afore-mentioned issue and are hopeful that the solution in Visual Studio 2013 will fill the gaps in the current release. The article describes several other improvements to the Nuget services, including integration with Windows Update for large-scale deployment. They also mentioned "a curated list of Microsoft .NET Framework NuGet Packages to help you discover these releases, published in OData format on the NuGet site", but don't mention whether the Nuget UI in VS2013 has been improved. The current UI, while not as awful and slow as initial versions, is still not very good for discovery and is quite clumsy for installation and maintenance.

User Voice for Visual Studio/.NET

You're not limited to just waiting on the sidelines to see which feature Microsoft has decided to implement in the latest version of .NET/Visual Studio. You should head over to the User Voice for Visual Studio site to get an account and vote for the issues you'd like the to work on next.

Here's a list of the ones I found interesting, and some of which I've voted on.



  1. In a similar vein, I found the issue Bring back Classic Visual Basic, an improved version of VB6 to be interesting, simply because of the large number of votes for it (1712 at the time of writing). While it's understandable that VB6 developers don't understand the programming paradigm that came with the transition to .NET, the utterly reactionary desire to go back to VB6 is somewhat unfathomable. It's 2013, you can't put the dynamic/lambda/jitted genie back in the bottle. If you can't run with the big dogs, you'll have to stay on the porch...and stop being a developer. There isn't really any room for software written in a glorified batch language anymore.

  2. This feature has been available for the unmanaged-code debugger (read: C++) for a while now.