Just for fun...
OSX has a built in speech synthesizer... Macs have been "talking" for a long time, but until fairly recently I didn't realize you could just open up a terminal and type
say This is what I am saying
I knew there was a fair selection of voices (try
say -vz droid
for example) but yesterday I found out they have a bunch of accents as well! Go to "System Preferences | Dictation & Speech | Text to Speech", then under System Voice hit "Customize..." You may have to download the new voices, but some of them are a lot of fun... I like Sangeeta who speaks India-accented English. Also, some of the ones that are geared for foreign languages, like the German-speaking Anna, are cool to hear as well. (Though they will read numbers in their native tongue.)
I don't know why I find it mildly surprising that a computer voice geared at reading a foreign language speaks English with the same accent as a human who grew up speaking that language, but it's pretty cool.
Tuesday, April 30, 2013
Monday, April 22, 2013
java quickie: launching webpage inside chrome, specifically
I needed to open a webpage from a Java app. Some code I inherited did this:
java.awt.Desktop.getDesktop().browse(new java.net.URI(WEBAPP_URL));
Not bad but it launches the default web browser (usually Safari on OSX). Luckily this app was OSX only, so I could rely on the open command. I ended up with:
String cmds[] = {"open","-a","Google Chrome",WEBAPP_URL};
Process p = Runtime.getRuntime().exec(cmds);
p.waitFor();
if(p.exitValue() != 0){
//show message, then try old method:
java.awt.Desktop.getDesktop().browse(new java.net.URI(WEBAPP_URL));
}
The tricky bit was breaking up the command into an array; without that, the space in "Google Chrome" was messing things up and I couldn't figure out how to make a single big command String that respect that space.
One the command runs we wait for it to finish and then go to the old behavior if it seemed to fail, according to the process exit value.
java.awt.Desktop.getDesktop().browse(new java.net.URI(WEBAPP_URL));
Not bad but it launches the default web browser (usually Safari on OSX). Luckily this app was OSX only, so I could rely on the open command. I ended up with:
String cmds[] = {"open","-a","Google Chrome",WEBAPP_URL};
Process p = Runtime.getRuntime().exec(cmds);
p.waitFor();
if(p.exitValue() != 0){
//show message, then try old method:
java.awt.Desktop.getDesktop().browse(new java.net.URI(WEBAPP_URL));
}
The tricky bit was breaking up the command into an array; without that, the space in "Google Chrome" was messing things up and I couldn't figure out how to make a single big command String that respect that space.
One the command runs we wait for it to finish and then go to the old behavior if it seemed to fail, according to the process exit value.
Sunday, April 21, 2013
osx: change a bunch of file dates (like for iphoto)
So I think I'm finally ready to switch to OSX for my main home machine. I'm finding the switch harder than I expected in some ways: in particular, it's hard enough to get iTunes to recognize ratings and playlists from another machine, and "date added" seems well-nigh impossible.
I'm taking a breather from that, though, to get my iPhotos in order, switching from my old organization of "folder of year, lots of subfolders by month (or season)". Actually, iPhoto would do really clever things for grouping photos by "event date", but because of another computer mishap of mine, every photo before late 2003 was reconstructed and now has a timestamp of November 12, 2003.
Unlike Windows, though, OSX makes it easy to modify the create and modified dates for files. From that link, the basic command is:
touch -t YYYYMMDDHHMM filename
I found it more convenient to wrap that in the find command (recursively locating all folders and files) as follows:
find foldername -exec touch -t YYYYMMDDHHMM {} \;
Sometimes it's really nice to be using Unix on the desktop!
I'm taking a breather from that, though, to get my iPhotos in order, switching from my old organization of "folder of year, lots of subfolders by month (or season)". Actually, iPhoto would do really clever things for grouping photos by "event date", but because of another computer mishap of mine, every photo before late 2003 was reconstructed and now has a timestamp of November 12, 2003.
Unlike Windows, though, OSX makes it easy to modify the create and modified dates for files. From that link, the basic command is:
touch -t YYYYMMDDHHMM filename
I found it more convenient to wrap that in the find command (recursively locating all folders and files) as follows:
find foldername -exec touch -t YYYYMMDDHHMM {} \;
Sometimes it's really nice to be using Unix on the desktop!
Thursday, April 18, 2013
quick note to ask tog
Bruce Tognazzini is a very experienced UI/UX/HCI guy. I don't always agree with his priorities though, which is especially frustrating because he tends to speak a bit ex cathedra. Recently he picked on Safari's "make the tabs as big as the space" approach in an article Providing Predictable Targets. This was my response:
Compared to a computer, a fighter plane is rather much a “single tasking” device! What on earth (or the sky) makes you think the “second tab” will always have the same information? That the use case for wanting to get to it is so similar every time that the muscle memory effect has meaning?
In this case, Safari made the decision that having a bigger space to read the title of what the content actually is, is more important than providing a consistent target. That’s a reasonable tradeoff… in my mind the drawback is that it LOOKS less like a button at that width.
There are two competing, but still both true, thoughts: “if you can’t measure it, you can’t master it” vs “not everything that counts can be counted, and not everything that can be counted counts”. You pay so much more attention to the former at the expense of the latter, it distorts your view. With Fitts’ Law, you have a very powerful tool for describing very simple, static interfaces. But with, say, the Mac vs Windows menu bars? Sure, Mac is faster to zip the mouse to, but with Windows, I never have to think about which window on the screen the menubar refers to– unlike the Mac. If I have a large sized screen and am using both firefox and chrome, I have to burn a lot of time scanning to see if the menu screen is one or the other. That cognitive time and effort is a lot more significant than the physical motion, but because it’s tougher to make a good test for it (since most tests implicitly tell you what to do so that A/B tests make more sense) it gets much less attention from you.Anyway, I really think the "stopwatch" brigade has its limitations, especially in today's world of computers offering a lot more variety in experience and usage modes.
Tuesday, April 16, 2013
osx protips: folder color breadcrumbs and fun with terminal prompts
Previously I posted a few OSX Protips, here is a little more:
I like OSX's "column" view in Finder, showing a few levels of folder structure at once. Lately I've taken to right clicking and using the colored Labels to use as a bread crumb trail for the files I navigate to most (e.g. the projects I've been working on lately in my company's svn repository) It's so much faster to only have 2 or 3 highlighted folders to glance at than to skim the whole list and try to remember which one I wanted.
In my previous post I mentioned changing the Terminal prompt (the only information I tend to need to know is the current directory, and I prefer to see the whole path) If you want, you can even Change Part of the Terminal Prompt into a Cheeseburger, or any other Emoji keyboard. What that post fails to mention is how to enter those special characters -- most OSX apps have a "Edit | Special Characters" menu option (with a keyboard shortcut) and your looking for "emoji". I added a little rocketship to mine:
I had to pad with spaces, the character is wider than a normal alphanumeric.
(Incidentally, iPhones and iPads have this keyboard too... go to Settings | General | Keyboard | Keyboards | Add New Keyboard and then the popup keyboard will have a little globe icon to switch to the emoji... if you use it in texts you should make sure the recipient has an iPhone (or other smartphone? Not sure) or else the characters might not come out right.)
I like OSX's "column" view in Finder, showing a few levels of folder structure at once. Lately I've taken to right clicking and using the colored Labels to use as a bread crumb trail for the files I navigate to most (e.g. the projects I've been working on lately in my company's svn repository) It's so much faster to only have 2 or 3 highlighted folders to glance at than to skim the whole list and try to remember which one I wanted.
In my previous post I mentioned changing the Terminal prompt (the only information I tend to need to know is the current directory, and I prefer to see the whole path) If you want, you can even Change Part of the Terminal Prompt into a Cheeseburger, or any other Emoji keyboard. What that post fails to mention is how to enter those special characters -- most OSX apps have a "Edit | Special Characters" menu option (with a keyboard shortcut) and your looking for "emoji". I added a little rocketship to mine:
I had to pad with spaces, the character is wider than a normal alphanumeric.
(Incidentally, iPhones and iPads have this keyboard too... go to Settings | General | Keyboard | Keyboards | Add New Keyboard and then the popup keyboard will have a little globe icon to switch to the emoji... if you use it in texts you should make sure the recipient has an iPhone (or other smartphone? Not sure) or else the characters might not come out right.)
Thursday, April 11, 2013
diffmerge, visual merging for multiple systems
I've been missing Beyond Compare since I've moved to OSX, but DiffMerge is pretty decent.
Wednesday, April 10, 2013
javascript 101: sort an array of maps by specific map key-value
Yesterday I posted about using jQuery to sort child elements via some field value. Today I had to go back to an even more basic question: a tidier way of sorting an array of hashes by the hash values for some particular key:
Javascript has a built in .sort() function for its arrays. (It tends to sort in place, so if you have something like
var x = [2,1,3];
var y = x.sort();
then both x and y are sorted.)
You can pass sort() a function(a,b) that should return -1, 0, or 1 based on whether a comes before or after b.
Other languages have a strcmp() function that returns those values for two strings, but in javascript you have to roll your own: (via http://phpjs.org/functions/strcmp/ )
function strcmp (str1, str2) {
// http://kevin.vanzonneveld.net
// + original by: Waldo Malqui Silva
// + input by: Steve Hilder
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + revised by: gorthaur
// * example 1: strcmp( 'waldo', 'owald' );
// * returns 1: 1
// * example 2: strcmp( 'owald', 'waldo' );
// * returns 2: -1
return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
}
or for shorter,
function strcmp (str1, str2) {
return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
}
so if you had an array of maps called users, and each map had a value for the key "name", the sort might look like
users.sort(function(a,b){
return strcmp(a.name.toUpperCase(),b.name.toUpperCase());
} );
Javascript has a built in .sort() function for its arrays. (It tends to sort in place, so if you have something like
var x = [2,1,3];
var y = x.sort();
then both x and y are sorted.)
You can pass sort() a function(a,b) that should return -1, 0, or 1 based on whether a comes before or after b.
Other languages have a strcmp() function that returns those values for two strings, but in javascript you have to roll your own: (via http://phpjs.org/functions/strcmp/ )
function strcmp (str1, str2) {
// http://kevin.vanzonneveld.net
// + original by: Waldo Malqui Silva
// + input by: Steve Hilder
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + revised by: gorthaur
// * example 1: strcmp( 'waldo', 'owald' );
// * returns 1: 1
// * example 2: strcmp( 'owald', 'waldo' );
// * returns 2: -1
return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
}
or for shorter,
function strcmp (str1, str2) {
return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
}
so if you had an array of maps called users, and each map had a value for the key "name", the sort might look like
users.sort(function(a,b){
return strcmp(a.name.toUpperCase(),b.name.toUpperCase());
} );
Tuesday, April 9, 2013
simply sorting child elements via their content
On stackoverflow article, Jquery - sort DIV's by innerHTML of children, Shawn Chin offers a simple, configurable, plug-in-free way of sorting the children of a parent div:
function sortUsingNestedText(parent, childSelector, keySelector) {
var items = parent.children(childSelector).sort(function(a, b) {
var vA = $(keySelector, a).text();
var vB = $(keySelector, b).text();
return (vA < vB) ? -1 : (vA > vB) ? 1 : 0;
});
parent.append(items);
}
The code then called be called with something like:
sortUsingNestedText($('#sortThis'), "div", "span.price");
That's pretty clever! I didn't realize you could .append() a child to its own parent to put it at the end.
I had to make a variant of it because I wanted all the divs that had the class "bonus" to be sorted before everything else in the list. (I decided just to write a special case rather than generalize the issue, but I could see making some kind of comparator function as an argument, but anyway this is an example of how the logic has to work, roughly)
function sortUsingNestedTextBonusFirst(parent, childSelector, keySelector) {
var items = parent.children(childSelector).sort(function(a, b) {
var panelA = $(a);
var panelB = $(b);
if(panelA.hasClass("bonus") && ! panelB.hasClass("bonus")) return -1;
if(! panelA.hasClass("bonus") && panelB.hasClass("bonus")) return 1;
var vA = panelA.find(keySelector).text();
var vB = panelB.find(keySelector).text();
return (vA < vB) ? -1 : (vA > vB) ? 1 : 0;
});
parent.append(items);
}
function sortUsingNestedText(parent, childSelector, keySelector) {
var items = parent.children(childSelector).sort(function(a, b) {
var vA = $(keySelector, a).text();
var vB = $(keySelector, b).text();
return (vA < vB) ? -1 : (vA > vB) ? 1 : 0;
});
parent.append(items);
}
The code then called be called with something like:
sortUsingNestedText($('#sortThis'), "div", "span.price");
That's pretty clever! I didn't realize you could .append() a child to its own parent to put it at the end.
I had to make a variant of it because I wanted all the divs that had the class "bonus" to be sorted before everything else in the list. (I decided just to write a special case rather than generalize the issue, but I could see making some kind of comparator function as an argument, but anyway this is an example of how the logic has to work, roughly)
function sortUsingNestedTextBonusFirst(parent, childSelector, keySelector) {
var items = parent.children(childSelector).sort(function(a, b) {
var panelA = $(a);
var panelB = $(b);
if(panelA.hasClass("bonus") && ! panelB.hasClass("bonus")) return -1;
if(! panelA.hasClass("bonus") && panelB.hasClass("bonus")) return 1;
var vA = panelA.find(keySelector).text();
var vB = panelB.find(keySelector).text();
return (vA < vB) ? -1 : (vA > vB) ? 1 : 0;
});
parent.append(items);
}
Wednesday, April 3, 2013
quickhit: jquery catch that enter key
Sometimes when you're making "fake" forms it makes sense to catch the enter key. Here's some code for that:
function catchEnter(jqo,funk){
jqo.keydown(function(e){
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
if(key == 13) funk();
});
}
Usage, that would have an enter key in the username change focus to password, and enter key in password submit the login:
catchEnter($("#loginuser"),function(){ $("#loginpassword").focus(); });
catchEnter($("#loginpassword"),doLoginClick);
function catchEnter(jqo,funk){
jqo.keydown(function(e){
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
if(key == 13) funk();
});
}
Usage, that would have an enter key in the username change focus to password, and enter key in password submit the login:
catchEnter($("#loginuser"),function(){ $("#loginpassword").focus(); });
catchEnter($("#loginpassword"),doLoginClick);
Subscribe to:
Posts (Atom)