Metadata

There's no data like metadata

My homepage
Archives (old blog)

Recently played

  • 2013年02月01日

    Customizing auctex for line-errors and shell-escape

    As it took me quite a while to find this online, I record it here for posterity.

    The purpose of the following is to customize the latex command that is run when you’re using auctex for emacs. I’ve seen two cases where I had to do this.

    • Sometimes, when I compile some latex, there is an error but when I want to jump to it I get to an empty buffer. The problem is in the parsing of the log (typically an unbalanced parenthesis), so with only the line number auctex does not know where to jump to. To solve this, one adds the --file-line-error flag when running latex, and every error will have both the file name and the line number.
    • When I want to use minted, a great syntax colorizing tool for latex, I need to put the --shell-escape flag so that the external pygments can be called.

    To add such arguments to latex, one can customize the TeX-engine-alist variable. Here is what I did.

    (setq TeX-engine-alist 
     '(
     (xetex "XeTeX" 
     "xetex --file-line-error" 
     "xelatex --file-line-error" 
     "xetex")
     (xetex_sh "XeTeX shell escape" 
     "xetex --file-line-error --shell-escape" 
     "xelatex --file-line-error --shell-escape" 
     "xetex")))
    

    Here is the documentation for this association list.

    Each entry is a list with a maximum of five elements. The first element is a symbol used to identify the engine. The second is a string describing the engine. The third is the command to be used for plain TeX. The fourth is the command to be used for LaTeX. The fifth is the command to be used for the –engine parameter of ConTeXt’s texexec program. Each command can either be a variable or a string. An empty string or nil means there is no command available.

    I then add some local variables at the end of my tex file to choose which engine to use.

    %%% Local Variables:
    %%% coding: utf-8
    %%% mode: latex
    %%% TeX-engine: xetex_sh
    %%% TeX-master: t
    %%% End:
    
  • 2012年11月11日

    SSH Tunnel and Screen Sharing through Keyboard Maestro

    I just read this great post by Dr. Drang on several ways to use SSH tunnels, and I thought I could offer a small refinement for the Screen Sharing part.

    As I often need to view the screen of my mini machine, I wanted to make it as painless as possible. To this end, I wanted by a single shortcut to open the tunnel and launch the Screen Sharing application (fairly easy to do), and to kill the tunnel when I quit the Screen Sharing application. Here is what I came up with using Keyboard Maestro.

    Keyboard Maestro Macro

    The trick consists in recording the pid of the tunnel when I start it (echo $! does it, which is then put in a variable), then regularly check if the Screen Sharing application is still running. Finally, when it has stopped, kill the tunnel. This of course could be adapted to simply be a script, but I find that Keyboard Maestro really shines for these simple cases.

  • 2012年08月09日

    Graphing RRD data using gnuplot

    I recently replaced my home server which, among other things, monitors the data from my Current Cost installation and creates pretty graphs. To this end, I followed this nicely explained approach, which uses RRDtool both as a data storage and a graphing tool.

    Unfortunately, after installing the server, I could not get the graph command of my (homebrew installed) rrdtool to work, as described in this github issue. I haven’t been able to solve this issue, but I’ve found a workaround graphing the data using gnuplot.

    Getting the data

    The first step is to extract the data from the RRD database. The rrdtool fetch command works as expected, but one should get the start and end date be multiple of the desired resolution, as explained in the documentation. I wrote a small script, cc_text.sh that takes two arguments: the resolution (in seconds) and the start time (in time offset specification, see the documentation), and dumps the data on the standard output, assuming the end time is the current time modulo the resolution.

    #!/bin/sh
    TIME=$(date +%s)
    RRDRES=1ドル
    RREND=2ドル
    /usr/local/bin/rrdtool fetch /Users/schmitta/Documents/current_cost/powertemp.rrd \
    AVERAGE \
    -r $RRDRES \
    -e $(($TIME/$RRDRES*$RRDRES)) \
    -s e-2ドル
    

    The data output looks like this.

     Power Temperature
    1344521880: 9,9564808206e+01 2,4900000000e+01
    1344522060: 1,0056171519e+02 2,4900000000e+01
    1344522240: nan nan
    

    To transform it into data that gnuplot will accept, I need to get rid of the lines with nan in it, and transform the commas in periods. So to extract the data for the last day at a 3 minutes resolution, I do the following in another script (which will see below).

    /Users/schmitta/bin/cc_text.sh 180 1d | grep -v nan | sed -e 's/,/./g' > $DATA
    

    Graphing the data

    When the data is in the correct format, I then call gnuplot to graph it. I have another script that does it, cc_watt_temp.sh, taking three arguments: the name of the data file, the name of the output png to be generated, and the format for the time on the ‘x’ axis (which is different in yearly and hourly graphs).

    #!/bin/sh
    name=1ドル
    out=2ドル
    format=3ドル
    /usr/local/bin/gnuplot <<EOF
    set terminal png size 1400, 400
    set output "$out"
    set xdata time
    set timefmt "%s"
    t0=7200 # Switch for time zone
    # set xtics 3600
    set format x "3ドル"
    set ytics nomirror
    set y2tics nomirror
    # set style fill solid border -1
    plot "$name" u (\1ドル+t0):2 axis x1y1 title 'Power' w histeps, "$name" u (\1ドル+t0):(\3ドル-2) axis x1y2 title 'Temp' w lines
    EOF
    

    The two peculiarities of the script are that I need to take my timezone into account (adding t0 to every date), and that the current cost temperature sensor is two degrees too high (substracting 2 to every temperature).

    Gluing it together

    I then created a final set of scripts which are regularly called to create the graphs. I have five of them: hourly, daily, weekly, monthly, and yearly graphs. Here is the daily one.

    #!/bin/sh
    DATA=/Users/schmitta/tmp/data1d.data
    PNG=/Users/schmitta/Dropbox/current_cost/1d.png
    /Users/schmitta/bin/cc_text.sh 180 1d | grep -v nan | sed -e 's/,/./g' > $DATA
    /Users/schmitta/bin/cc_watt_temp.sh $DATA $PNG "%H:%M"
    

    As you can see, the graph is created in my DropBox account. I have a local web page that collects all these graphs, which I can access from any computer that is connected to DropBox without having to launch Apache on the server.

    All you now need to do is regularly launch these scripts, using for instance cron or launchd.

  • 2012年08月02日

    Installing and configuring BBDB 3

    BBDB 3 is still under development, and as such there is not much information available to simply configure it. The goal of this post is to explain how to obtain a minimal installation of BBDB 3 that works with gnus. The installation part is targeted for OS X, but the configuration is platform independent.

    Installation

    I recommend getting the development version, as reported bugs can be fixed fairly quickly.

    git clone git://git.savannah.nongnu.org/bbdb.git
    

    Next, you will need a recent autoconf. The one provided by Lion is not recent enough, so I had to install it through homebrew. I haven’t installed Mountain Lion yet, so I don’t know which version of autoconf ships with it. Try skipping this step (directly running autoconf), and if it complains then do the following.

    brew install autoconf
    export PATH=/usr/local/Cellar/autoconf/2.69/bin:$PATH
    

    You can then run autoconf.

    autoconf
    

    You now can run the configuration script, specifying where the files will be installed. Here is the command I ran. (You should use absolute paths in this command.)

    ./configure \
    --with-lispdir=/Users/schmitta/.emacs.d/vendor/emacs/site-lisp/bbdb \
    --with-texmf-dir=/Users/schmitta/Library/texmf \
    --infodir=/Users/schmitta/.emacs.d/vendor/info
    

    Compilation and installation can then proceed as usual.

    make
    make install
    

    Configuration

    Here is the configuration I put in my .emacs. I have to thank the very kind people on the org-mode and bbdb mailing lists, who really helped me put this together.

    If some things are not clear, I highly recommend looking up the variable definitions using for instance C-h v bbdb-mua-update-interactive-p, which will describe the different options for this setting.

    ;; file where things will be saved
    (setq bbdb-file "~/.emacs.d/bbdb")
    ;; load bbdb
    (add-to-list 'load-path (concat emacsd-vendor-dir "/bbdb"))
    (require 'bbdb-loaddefs)
    (require 'bbdb)
    ;; initialization
    (bbdb-initialize 'gnus 'message)
    (bbdb-mua-auto-update-init 'gnus 'message)
    ;; size of the bbdb popup
    (setq bbdb-pop-up-window-size 10)
    ;; What do we do when invoking bbdb interactively
    (setq bbdb-mua-update-interactive-p '(query . create))
    ;; Make sure we look at every address in a message and not only the
    ;; first one
    (setq bbdb-message-all-addresses t)
    ;; use ; on a message to invoke bbdb interactively
    (add-hook
     'gnus-summary-mode-hook
     (lambda ()
     (define-key gnus-summary-mode-map (kbd ";") 'bbdb-mua-edit-field)
     ))
    ;; ;; This is for non interactive bbdb invocation, but it was a bit
    ;; ;; too much
    ;; (setq bbdb-update-records-p 'query)
    

    To test this, launch gnus, and when reading a message hit the ’;’ key. You should see a window that offers to create records for the senders and recipients of the message. When writing a message, you can hit ‘TAB’ to complete the email address.

    Have fun!

  • 2011年12月23日

    Window Manipulation with Keyboard Maestro

    Since last October, my main computer has been a 13” MacBook Air. It’s a great machine, perfect for what I do (write papers, read papers, code in Ocaml and Coq). Its screen is not that big, so at work I have an Apple Cinema Display hooked to it. I don’t have this luxury at home, thus many times a week I switch from a big display to the smaller integrated one, which means that I have to move windows around. This post is about how I use Keyboard Maestro to help me in one particular case.

    For my day to day window moving and resizing needs, I rely on Mercury Mover. I know there are many other applications that let you easily move windows using only the keyboard, but I’ve been using this one for years and it perfectly fits my needs. Unfortunately, moving windows sometimes is not enough. When I read a PDF on the MacBook Air, using Skim of course, I need the PDF to be displayed as “Single Page Continuous”, which along with “Automatically Resize” make it legible to my aging eyes. I set up Skim to use this and a reasonable window size by default. However, when I switch to the big screen, I prefer the PDF to be displayed as “Single Page”. This way, hitting the down arrow key then gets me to the next page. Mercury Mover can’t switch this setting for me, but Keyboard Maestro can.

    Keyboard Maestro: PDF Large Screen

    By the way, the interface to find the menu item to use is just great.

    Keyboard Maestro: Menu Selection

    Simple, repetivite task: that’s what Keyboard Maetro is for.

  • 2011年11月28日

    Sun, Earth, and Moon in Codea

    I spent a few hours last week-end coding a very simple simulation of the gravitational interaction of the Sun, Earth, and Moon system. Even if it’s very basic, I was so proud of it I posted a link to it on Twitter, which lead to the great honor or having Dr Drang himself ask for more details. Here they are, along with the code. (You can find the whole code as a gist, if you want to play with it. The code below has additional line breaks to make it look not to bad.)

    Screenshot of the simulation

    The simulation is written with Codea, a programming environment on the iPad. Codea is currently named Codify but this will change when Apple approves the latest version (soon, hopefully).

    The programming language Codea uses is Lua, with a few extensions: a class system, a drawing loop very similar to the one of Processing, and ways to interact with the iPad (touches, accelerometer, and so on). I did not know Lua before playing with Codea, so please excuse the ugly code that is coming.

    The code

    The goal of the simulation is to have several “bodies” which interact through gravity only. I don’t assume much knowledge of Codea, but if things are not clear there are some great Codea tutorials online.

    Let’s start with the class describing these bodies.

    Body = class()
    

    Our Body object creation method takes the initial position, speed, body mass, and color, and sets some instance variables.

    function Body:init(x,y,vx,vy,m,c)
     -- you can accept and set parameters here
     self.pos=vec2(x,y)
     self.speed=vec2(vx,vy)
     self.mass=m
     self.color=c
    end
    

    The vec2 object is a point. It has several interesting methods including one that computes the distance between two points. We make use of it in the next method, where v is a vec2.

    function Body:dist(v)
     return self.pos:dist(v)
    end
    

    We now get to the first method which is about physics: Newton’s second law of motion “F = m . a” (force is equal to mass times acceleration, where acceleration is the rate at which speed changes: a = (new_speed - old_speed)/time). This method applies a force f for a given time t on the body, which results on the body’s speed changing.

    function Body:applyForce(f,t)
     self.speed = self.speed + t * f / self.mass
    end
    

    Since speed is the rate at which position changes, we can describe how the position evolves during some time t.

    function Body:updatePos(t)
     self.pos = self.pos + t * self.speed
    end
    

    The last two methods are about the graphical interface. The first one returns the on-screen position of the body. The parameter z is the zoom factor (how many distance units are in a pixel), and c is the position that is at the center of the screen.

    function Body:screenPosition(z,c)
     return vec2((self.pos.x-c.x)*z + WIDTH/2,
     (self.pos.y-c.y)*z + HEIGHT/2)
    end
    

    Finally, we draw a body as a small circle of the body’s color at the body’s screen position.

    function Body:draw(z,c)
     local position = self:screenPosition(z,c)
     fill(self.color)
     ellipse(position.x,position.y,10)
    end
    

    Let’s now move to the next class, which will be at the heart of the simulation.

    Bodies = class()
    

    An instance of this class is an array of the bodies we want to simulate.

    function Bodies:init()
     self.blist={}
    end
    function Bodies:addBody(b)
     table.insert(self.blist,b)
    end
    function Bodies:bodyAtIndex(i)
     return self.blist[i]
    end
    

    The central method of the whole program is the next one. Given a body (identified by its position in the array), compute the force the other bodies exert on it. We accumulate this force in force, and heavily rely on the vector operations (addition or subtraction of vectors, multiplication and subtraction by a scalar) to compute it.

    More precisely, we enumerate the bodies of the simulation (in the for loop), and for each one, called b, we compute the force it exerts on body. This force is a vector from body to b whose length is (according to Newton’s law of universal gravitation) G * m1 * m2 / d^2, where m1 and m2 are the masses of the bodies, d is the distance between the bodies, and G is the gravitational constant.

    The code is almost exactly this formula, except that there is no G. As I’ll explain later, we choose our units such that G is equal to 1. The formula (b.pos - body.pos)/d is the vector from body to b whose size is 1. We multiply it by the masses of both bodies, and divide by the square of the distance.

    When we’re done accumulating the force, we simply return it.

    function Bodies:computeForce(bnum)
     local body=self.blist[bnum]
     local force=vec2(0,0)
     local d
     for i,b in ipairs(self.blist) do
     if i ~= bnum then
     d = body:dist(b.pos)
     if d ~= 0 then 
     force = force +
     ((b.pos - body.pos)/d) *
     body.mass * b.mass / (d*d)
     else
     print("null")
     end
     end
     end
     return force
    end
    

    We could optimize the previous method in two ways. We can delay the multiplication by body.mass until the end (since it can be factored in each individual force). We can even forget completely about body.mass and change Body:applyForce so that it does not divide the force by body.mass (as the multiplication and division cancel each other). As it makes the physics less clear in both cases, I decided to avoid these optimizations.

    The next method is the simulation main loop: for each body, compute the force and apply it to the body. (The astute reader will notice that applying the force changes the speed of the body, not its position. The position will remain unchanged for the computation of other forces and the speed is not used to compute the forces, so the order in which computations occur does not matter.)

    A second loop simulates the bodies moving, changing their position according to their speed.

    function Bodies:simulate(time)
     local force
     for i,b in ipairs(self.blist) do
     force = self:computeForce(i,b)
     b:applyForce(force,time)
     end
     for i,b in ipairs(self.blist) do
     b:updatePos(time)
     end
    end
    

    The last two methods are for the graphical interface. The first one searches whether there is a body close (less than 20 pixels) to a given screen point.

    function Bodies:bodyAtPosition(touch,zoom,center)
     for i,b in ipairs(self.blist) do
     if touch:distSqr(b:screenPosition(zoom,center))
     < 400 then
     return i
     end
     end
     return 0
    end
    

    The last function draws everything.

    function Bodies:draw(z,c)
     for i,b in ipairs(self.blist) do
     b:draw(z,c)
     end
    end
    

    We can now turn to the Main class, which will let us look deeper into Codea itself.

    We first set some parameters. They appear as sliders on the side of the screen. The first one controls the speed of the simulation (how much time passes for each simulation step), and the second how much zoomed in we are. (I should use the usual zooming gesture for that, but I haven’t taken the time to do so.)

    parameter("Speed",0,1000,10)
    parameter("Zoom",0,1000,10)
    

    We next set some constants. I’ll talk more about this below, but in a nutshell we are choosing the mass of the Earth as mass unit, and 1000 km as distance unit.

    SunMassInEarth = 330000
    EarthMassInMoon = 81
    EarthMoon = 384 -- thousands of kilometers
    EarthSun = 149600 -- thousands of kilometers
    

    The setup() function is called once when the program is launched. We use it to add three bodies (the Sun, the Earth, the Moon) at some initial positions (so that distances between them are as expected) and some initial velocity I tweaked to get the orbits to look right (more about this below).

    -- Use this function to perform your initial setup
    function setup()
     bl=Bodies()
     bl:addBody(Body(0,0,0,0,SunMassInEarth,
     color(255,255,0,255)))
     bl:addBody(Body(0,EarthSun,1.5,0,1,
     color(0,255,255,255)))
     bl:addBody(Body(EarthMoon,EarthSun,1.5,-0.05,
     1/EarthMassInMoon,
     color(255,255,255,255)))
     center = vec2(0,0)
     zfactor = 10000
     centeredOnBody = 0
     print("Tap on a body to track it")
     print("Tap anywhere else to stop tracking it")
     print("Drag to move to view point")
    end
    

    The touched(touch) function is called every time a touch event occurs. If the touch has just began, then we check if there is a body near. If so, we remember to track this body (to set the center of the display on this body). If the touch is MOVING, it means that the user is dragging his finger, and we move the center of the display accordingly.

    function touched(touch)
     if touch.state == BEGAN then
     centeredOnBody = bl:bodyAtPosition(
     vec2(touch.x,touch.y),
     Zoom/zfactor,center)
     elseif touch.state == MOVING then
     center.x = center.x +
     (lastx - touch.x)*zfactor/Zoom
     center.y = center.y +
     (lasty - touch.y)*zfactor/Zoom
     centeredOnBody = 0
     end
     lastx = touch.x
     lasty = touch.y
    end
    

    Finally, the draw() function is automatically called every frame (this is the main loop of the program). There we erase everything, and if the simulation is not paused, we make it progress for Speed amount of time. If we’re tracking a body, we set the center of the display to this body. Finally, we redraw everything.

    -- This function gets called once every frame
    function draw()
     background(0)
     if Speed > 0 then bl:simulate(Speed) end
     if (centeredOnBody ~= 0) then
     local b = (bl:bodyAtIndex(centeredOnBody))
     center.x = b.pos.x
     center.y = b.pos.y
     end
     bl:draw(Zoom/zfactor,center)
    end
    

    I could say “that’s it”, but there is one more thing…

    On units of measurement

    This simulation tries to be realistic. But except for the mass and distance of the bodies, there is no magic number. In particular, I chose G to be equal to 1 instead of it’s real value (6.673 10^-11). How can this work?

    It works because I never specify anywhere what the time unit is, and mass, distance, and time are independant base units. I thus have 3 degrees of freedom to use, and by choosing a unit of mass (weight of the Earth), a unit of distance (1000 km), and the constant G (1), I fix the time unit to something we can compute. This is how I did it using Soulver, keeping probably too many digits…

    #Soulver InputResult
    1In m3 kg-1 s-2, G: 6.673 × 10-116.673×10-11
    2In kg, Earth mass (EM): 5.974 × 10245.974×1024
    3In m3 EM-1 s-2, G: 6.673×1011 × 5.974×10243.987×1014
    4In (1000km)3 EM-1 s-2, G: 3.987×1014/((106)3)0.0003986
    5thus our time unit is: √(1/0.0003986)50.08
    6
    7Earth speed: 2 × π × 149,6 × 106 × 103/(365 × 24 × 3600)29806
    8in our units: 29806 × 50.08/1061.493
    9Moon speed: 2 × π × 384 × 103 × 103/(28 × 24 × 3600)997.3
    10In our units: 997.3 × 50.08/1060.04995

    As G is expressed in “something per kg”, to change it into “something per Earth mass”, we multiply it by the mass of the Earth (line 3). To go from meters to thousands of kilometers, we divide the result by one million thrice (since G is in meters cube, line 4). To conclude, let’s call foo our time units, which is a number of seconds. To convert G in this unit, we need to multiply it by foo twice (or by foo squared), and the result would be 1 (which is what we chose for G):

    line4 * foo^2 = 1
    foo^2 = 1 / line4
    foo = √(1 / line4)
    

    which is what we compute on line 5. Our time unit is thus 50 seconds. If we call our simulation with a speed of 10, for each frame we simulate 500 seconds.

    The last few lines are checks that everything is consistent. In line 7, I compute the Earth orbital speed in meters per seconds. It’s simply going around one orbit 2 × π × 149,6 × 10^6 × 10^3 (Earth is 150 million of kilometers away from the sun) in one year. When converting this to our units (thousands of kilometers and fifties of seconds), we get 1.49, which is quite close to the 1.5 I tweaked for initial Earth velocity to get a circular-looking orbit. Similarly, we can compute the orbital speed of the Moon, and find it’s consistent as well.

    Final words, or tldr

    There are many things I want to add to this, such as trails for trajectories, zooming using gestures, working in 3d (both for the simulation, easy, and for the interface, much harder)… But with this very simple program, I was able to get my son interested in celestial mechanics and in the mind-blowing distances in the solar system.

    This post was way longer than I expected, and probably too technical. If there is one thing to take away, try the following: Codea is a really cool app, and it’s quite easy and to play with physical simulations in it. And physics, even as simple as this, is fun!

  • 2011年08月25日

    How many calendars do I need?

    I was recently reading this review of the new Calvetica when I was enlightened by this footnote:

    Though I am rethinking this, I mean does it really matter if it is a work event or personal event? I still have to attend both…

    This small comment made me re-think how I organize my calendars. Previously, it was by areas of responsibility: work, home, teaching, and so on. But I don’t really care if an event is about “work” or “home”, what matters is what the event is (its title), and whether I’m available to attend it. So I now just have one calendar, called Alan. As we share calendars in the family, there is also a calendar for Christelle, one called Family (when both Christelle and I need to be available), one for each of the kids, and a general Info calendar for non-events.

    Since switching to this system, I’ve found that it provides enough information without requiring me to think to much when I enter an event: I just need to know whom it concerns.

  • 2011年05月24日

    Tweeting from LaunchBar

    Update (2011年05月28日) I was not correctly escaping the string (just putting in into quotes was not enough if the tweet had quotes). The code below has been updated to use the very useful quoted form of AppleScript construct.

    Following this great post on Hypertext, I’ve been using ttytter with LaunchBar recently to send some quick tweets. I was not 100% happy with the AppleScript used to launch it, so I tweaked it to get some feedback about whether the tweet succeeded, and to warn me if it is too long. Here is the result.

    on handle_string(tweet)
     if length of tweet ≤ 140 then
     set theResult to do shell script "~/bin/ttytter.pl -status=" & quoted form of tweet & " 2>&1"
     display dialog theResult buttons {"Dismiss"} default button 1
     else
     display dialog ("tweet too long: " & (length of tweet) & " characters") buttons {"Dismiss"} default button 1
     end if
    end handle_string
    
  • 2011年04月12日

    Downgrading my iPhone 3G to iOS 3.1.3

    I just got tired of waiting. Waiting for apps to launch. Waiting for Apple to release a version of iOS that would not suck. Yes, my iPhone 3G is old hardware, almost 3 years old. But, coming from Apple, I was expecting the experience to grow better through software updates, until the hardware stopped receiving them. As it recently happened (the iPhone 3G did not get the recent iOS 4.3), the hope of things being eventually fixed disappeared. So I just thought back to the best experience I had with my old iPhone, and downgraded to that.

    This, of course, boils down to a trade-off. The advantages of iOS 4 for the 3G are fairly small: folder and unified inbox are the only ones that come to mind. But then there are the two big issues in downgrading: apps that require recent versions of iOS, and the actual process which made me start from scratch, thus potentially lose data, as I did not have a 3.1.3 backup.

    Let’s first talk about the data, as things turned out quite well. I realized that, with a few exceptions, most of my data is either in the cloud, or live in external applications on my computer. Photos are in iPhoto; music, podcast, and movies in iTunes. Instapaper data is in the cloud, my text files live on Dropbox as well as my 1Password data, OmniFocus is OmniSynced, even Weightbot (a small app to track my weight) can backup to and restore from the web. Book reading is done on the iPad, so I did not even look into what would happen with the Kindle app. To summarize, I had to manually backup SMS and voice messages (using PhoneView), make sure everything was synchronized, and that was it. The only data I knowingly lost is from some games, and the only one I regret is my ongoing game of Dev Story. I’ll have to start a new one, for sure. Beside this, I lost some configuration data (favorite phone numbers, screen organization) which I’m slowly recreating.

    As concerns applications, I first went through the ones I have on my home screen to see if they were supported, and most are, with a few exceptions: Calvetica, Notesy, and Soulver. Unexpectedly, I discovered after downgrading that although Elements does not require iOS 4, it does require iOS 3.2 … but there never was a 3.2 for the iPhone, so Elements was also out of the picture. This was a bit bothersome, as until now I needed to use two notes applications: one for a subset of my notes that would load quickly, another for the whole set of more than 1300 (and growing) text files.1 I’m trying to use Simplenote with these many files, and even though it’s not as fast as dealing with the smaller set, it seems to be usable. I’ll see if I have to head back to DrDrang’s great iPhone notes app comparison to find a sidekick for Simplenote.

    Another small disappointment occurred when I discovered that the “swipe to turn pages” feature in Instapaper is iOS 4 only. So I’m back to “tilt scrolling” which, while not as convenient, still works quite well.

    But beyond these small issues, downgrading just brought my iPhone back to life. I had completely forgotten how responsive it could be. Apps launch instantly, and they even crash less. I used to be very eager to upgrade this phone, as it had grown so sluggish. But now I know I can wait, probably till some iPhone 5 comes along, happily using my blazingly fast iPhone 3G.

    iOS 3.1.3: doing less, but doing it well, is so much better.


    1. See this blog post if you want to know how I keep the subset in sync with the larger set of notes. ↩︎

  • 2011年04月08日

    Markdown with Keyboard Maestro

    Following this interesting video on Practically Efficient showing how to quickly write some Markdown markup using LaunchBar and TextExpander, I decided to write a few similar macros for Keyboard Mastro. Here they are.

    The first two ones are fairly simple: they make the text bold or italic. To use them, simply select the wanted word(s) and hit the shortcut. Here is how they are defined.

    The last one is to create a reference link. As before, select the text for the link, and hit the shortcut. A window will appear asking for the reference to use for the link (by default the highlighted text), and will create the syntax for the link, both around the highlighted text and at the end of the file. The user may then write down the url.

    The AppleScript in this macros is as follows.

    tell application "System Events"
     activate
     set theText to the clipboard
     display dialog "Edit link name" default answer theText
     set the clipboard to text returned of result
    end tell
    

    and the last inserted text is simply

    [%CurrentClipboard%]: 
    

    These macros could clearly be improved, but I had fun writing them, and I find them more efficient that going the TextExpander route (as I don’t have the extra copy step). I hope you enjoy them!

[フレーム]

AltStyle によって変換されたページ (->オリジナル) /