ALGOBIT > 離散的な気まぐれ

2010/06/18

MacOS X のキーボードショートカット

Filed under: 離散的な気まぐれ — タグ: — Kohyama @ 01:44

Windows には, 修飾キーとアルファベットキーを組み合わせてカーソルを動かそうという考えは無い.
従って UNIX 生活では必要の無い, 四つの矢印キー, Home, End, PageUp および PageDown などによるカーソル移動, Shift を押しながらこれらの移動を行うことによる選択範囲の調整, を頻繁に行うことになる.
(といっても常用しているのが HHK なので, Fn キーを押しながら某なのだが,)

iPhone 開発のために Macintosh を購入し, XCode と iPhone SDK のインストールの次にやったことは, XCode のキーボードショートカットを変更し, PageUp キーと PageDown キーをページアップ, ページダウン機能に割り当て直すことだった.

さて, その後 Windows7 / Parallels が実用に耐えそうなので, 基本生活を MacOS X に移行してしばらく.
各アプリケーションで, ではなく, システムデフォルトでテキストを入力する際のキーボードショートカットがある程度定義されている(もちろんアプリケーションあたりで上書き定義できる)ことが分かってきた.
これを変更するにはどうしたら良いか.
Translations/Mac OS X Key Bindings
にすべて書いてあった. 著者さんありがとう.

一番困るのは, システムデフォルトで

Home 文書の先頭にスクロール(カーソルは移動しない)
End 文書の末尾にスクロール(カーソルは移動しない)
PageUp 1画面前へ(カーソルは移動しない)
PageDown  1画面後へ(カーソルは移動しない)

となっていること. これらは Windows 環境では

Home 行頭へカーソル移動
End 行末へカーソル移動
PageUp 1画面前へ(カーソル移動)
PageDown  1画面後へ(カーソル移動)

に割り当てられていることが多く, 非常に頻繁に利用するので, MacOS X で作業中に無意識に押下すると, 予想とまったく異なる機能が呼び出され, プチパニックになる.

そこでこれらのキーは取り敢えず Windows と同じ機能を割り当てておく.
ただし, デフォルト状態の MacOS X を触ったときに困らないように, なるべく別のキーバインドでこれらの機能を呼び出すよう気をつける.
Shift-Home, Shift-End も同様.

システムデフォルトで部分的に Emacs 風のキーバインドが採用されているので, 定義を追加し, 移動系のキーバインドは Emacs 風にしておく.
もちろん vi を使っているとき以外の話だけど.
以下は(自分が)よく使う Emacs バインドのうち, ‘○’ は MacOS X システムデフォルトで採用されているもの, ‘×’ はされていないもの.

○  C-f 1文字進む
C-b 1文字戻る
C-a 行頭へ
C-e 行末へ
C-n 次の行へ
C-p 前の行へ
C-v 1画面分進む(ページダウン)
× M-v 1画面分戻る(ページアップ)
× M-< 文書の先頭へ
× M-> 文書の末尾へ
× C-Space  現在のカーソル位置をマーク
C-k 行末まで削除
C-y 現在のカーソル位置に記憶した内容を書き出す
× C-w 現在のカーソル位置からマーク位置までの内容を削除して記憶
× M-w 現在のカーソル位置からマーク位置までの内容を記憶
× C-x C-x 現在のカーソル位置をマークし, カーソルはマークされていた位置へ移動
× C-x C-m マークまでを選択

上記の採用されていない定義を追加する.
また, M- については Emacs 風に, Meta(Alt, Opt)を押しながらでも, Escape を押してからというのでも機能するように定義を追加する.

というわけで, ~/Library/KeyBindings ディレクトリが無いので作り,
~/Library/KeyBindings/DefaultKeyBinding.dict として以下を作成.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
    "\UF729" = "moveToBeginningOfLine:";    /* Home */
    "\UF72B" = "moveToEndOfLine:";          /* End */
    "\UF72C" = "pageUp:";                   /* PgUp */
    "\UF72D" = "pageDown:";                 /* PgDn */
    "$\UF729" = "moveToBeginningOfLineAndModifySelection:";  /* Shift-Home */
    "$\UF72B" = "moveToEndOfLineAndModifySelection:";        /* Shift-End */
    "~v" = "pageUp:";                       /* M-v */
    "~<" = "moveToBeginningOfDocument:";    /* M-< */
    "~>" = "moveToEndOfDocument:";          /* M-> */
    "^ " = "setMark:";                      /* C-space */
    "^w" = "deleteToMark:";                 /* C-w */
    "~w" = ("selectToMark:", "copy:");      /* M-w */
    "^x" = {
        "^x"  = "swapWithMark:";            /* C-x C-x */
        "^m"  = "selectToMark:";            /* C-x C-m */
    };
    "\U001B" = {
        "v" = "pageUp:";                    /* M-v */
        "<" = "moveToBeginningOfDocument:"; /* M-< */
        ">" = "moveToEndOfDocument:";       /* M-> */
        "w" = ("selectToMark:", "copy:");   /* M-w */
    };
}

「快適」というよりは「ストレスフルではなくなった」というところ.

2010/06/17

シェルから Cocoa (2)

Filed under: 離散的な気まぐれ — タグ: , , , — Kohyama @ 13:59

UNIX で 100行程度で記述できる処理をするなら, シェルスクリプトを書くか, C のソースを vi で書いて, そのままシェルからコンパイルして実行することが多い. (多かった.)
C で書くことは, UNIX C 開発の経験値を上げるという意義を含んでいる. (含んでいた.)

その経験自体に意味がある人にとっては, 目の前の処理のための最適手法でなくても採用する意味がある. (これはもちろん他の言語で書いたり, わざわざ IDE を起動する場合にも当てはまる. それを経験すること自体に価値があるのだ.)

さて最近主な環境として採用した MacOS X ですが, シェルスクリプトはそのまま使えるので, その範囲を越える場合は Objective-C 言語で Cocoa フレームワークを使います. アップルスクリプトはまた今度.
実はここには, MacOS X 開発の経験値をあげるだけでなく, iPhone, iPad 開発の技能に直結するという隠れた(隠れてないか)意義があります.
ただし, 100行以下の処理なら, XCode 立ち上げるのは面倒なので, vi で編集して, シェルから cc して, 実行.

口上はともかく, 例えば, 目の前の作業.
同じメーカーの機器から取り込んだ写真や動画のファイル名が重複しないように,
また, ファイル名でソートした時に撮影順になるように, “YYYYmmddHHMMSS” の14桁の数字にフォーマットした「撮影日時」を含むファイル名に元のファイルのファイル名を一括変換したい.
ただし, コピーしたり, メールでファイルを送ってもらったりしている間に「ファイルの作成日時」は簡単に更新されてしまいますので, あくまで「撮影日時」です.

Windows 常用中は, VisualBasic .NET で書いたプログラム(数十行)使ってましたが, Exif のみ対応でした.

今回は Cocoa と QTKit で, JPEG に入っている Exif と, QuickTime な動画(mov とか 3gp とか) のヘッダに格納されている撮影日時を認識しましょう.

datenname.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#import <Cocoa/Cocoa.h>
#import <QTKit/QTKit.h>
 
int
main(int argc, char *argv[])
{
    NSAutoreleasePool *ap = [[NSAutoreleasePool alloc] init];
    NSFileManager *fm;
    NSString *path, *newpath, *prefix, *dt, *suffix;
    NSImage *img;
    NSArray *arr;
    NSDictionary *dic;
    QTMovie *mov;
    NSError *err;
    NSDateFormatter *df;
 
    if (argc < 2)
        exit(1);
 
    /*
     * prefix and suffix should be variable
     * but now hard coded.
     */
    prefix = @"iph";
    suffix = @"";
 
    /*
     * to format an NSDate as an NSString of format "YYYYmmddHHMMSS"
     */
    df = [[[NSDateFormatter alloc]
        initWithDateFormat:@"%Y%m%d%H%M%S"
           allowNaturalLanguage:FALSE] autorelease];
 
    fm = [[NSFileManager alloc] init];
    while (0 < --argc) {
        path = [NSString stringWithCString: *++argv encoding: NSUTF8StringEncoding];
        dt = nil;
        err = nil;
 
        if (img = [[NSImage alloc] initWithContentsOfFile:path]) {
            if ((arr = [img representations]) &&
                (dic = [[arr objectAtIndex:0] valueForProperty: @"NSImageEXIFData"])) {
                /* now
                 *   [dic objectForKey:@"DateTimeOriginal"]
                 * returns string like "2010:05:13 12:36:45"
                 * let's retrieve colon(':') and space(' ') from this.
                 */
                dt = [[[dic objectForKey:@"DateTimeOriginal"]
                    stringByReplacingOccurrencesOfString:@":" withString:@""]
                    stringByReplacingOccurrencesOfString:@" " withString:@""];
            } else {
                NSLog(@"Can't recognize NSImage.DateTimeOriginal field of the file : %@", path);
            }
 
        } else if (mov = [QTMovie movieWithFile:path error:&err]) {
            if (!err) {
                /* now
                 *   [mov attributeForKey:QTMovieCreationTimeAttribute]
                 * returns the datetime as NSDate
                 * let's format it to "YYYYmmddHHMMSS"
                 */
                dt = [df stringFromDate:
                    [mov attributeForKey:QTMovieCreationTimeAttribute]];
            } else {
                NSLog(@"Can't recognize QTMovie.CreationTimeAttributes field of the file : %@",
                    path);
            }
        } else {
            NSLog(@"Can't recognize format of the file : %@", path);
        }
 
        if (dt != nil) {
            newpath = [[[path stringByDeletingLastPathComponent]
                stringByAppendingPathComponent: [[prefix stringByAppendingString: dt]
                    stringByAppendingString:suffix]]
                stringByAppendingPathExtension: [path pathExtension]];
            if ([fm fileExistsAtPath:newpath]) {
                NSLog(@"'%@' already exists.", newpath);
            } else {
                err = nil;
                [fm moveItemAtPath:path toPath:newpath error:&err];
                if (err)
                    NSLog(@"failed to rename '%@' to '%@'.", path, newpath);
                else
                    NSLog(@"'%@' has been renamed as '%@'.", path, newpath);
            }
        }
    }
 
    [ap release];
    return 0;
}

を作っておいて

cc -o datenname datenname.m -framework Cocoa -framework QTKit

でコンパイルし, datenname をパスの通っている場所にコピーしておきます.
使い方は, ファイル名を変換したいところのファイルをワイルドカードなど使って全部引数として渡して実行.

対象とする API の出版元以外が提供する書籍やサイトのサンプルソースは, 関連すると思われるソースコード断片があってもコピーペーストはしません, 出てきたクラスやメソッドをリファレンス で確認し, 一から自分で書くようにします.

経験を貯めるのが目的の一部なので, 自分のソースは再利用しても良いけど, 人のソースは貼付けません. 文法, クラス, メソッドで分からないものがあれば, リファレンスで調べて, できるだけ一から書きます.
部分的にコードをそのまま利用する場合でも, なるべくコピーペーストはせず, 自分の手で入力し, また, 自分が変更してもよい識別子と, API などが提供する識別子を明確に区別するために, 変数名などはなるべく再命名します.

しかし現時点では, Objective-C のメモリ管理モデルがまだ理解できていないので, NSAutoreleasePool まわりは, 参考にしたソース断片を書き写しただけです. これでいいのか分かりません.
次の課題とします.

2010/06/13

シェルから Cocoa

Filed under: 離散的な気まぐれ — タグ: , , , — Kohyama @ 17:36

ターミナルで作業している率が高いひとが, APIの試験とか, 自分向けツールとかで, 小さな Cocoa アプリを書く.
そんな時は vi でソース編集して cc で.

% cat hoge.m
#import <Foundation/Foundation.h>
int main() {
    NSLog(@"Hello Cocoa");
}
% cc -o hoge hoge.m -framework Foundation
% ./hoge
2010-06-13 17:16:42.470 hoge[88380:903] Hello Cocoa

2010/05/25

pFlipFlick

Filed under: 離散的な気まぐれ — タグ: , , , — Kohyama @ 03:16


HTML5 の canvas の試験ですが, 素材が寂しいので, Flickr からクリエイティブコモンズ画像を借りて来るので, Flickr API も使ってみました.
「Loading…」が終わらない時は検索キーワードを変えてみてください.
Chrome, Opera, Firefox, Safari で動くと思います.
遅いけど iPhone Safari でも一応動いてます.

2010/04/23

canvas についてお話させていただきました

Filed under: 離散的な気まぐれ — タグ: , , — Kohyama @ 23:59

「第二回プログラマーズカフェナイト(pgcafe nite 2nd)@新宿」にて HTML5 の canvas についてお話させていただきました.
資料アップしておきます.
slide4pgcafenite2nd

自分のことはさておき, 皆様の発表とても参考になり, また刺激になりました.
予定されていたスピーカーはもちろんなんですが, 本会で募集した飛び入り方3名, 二次会で急遽決定した方々も, 内容がすばらしいばかりか, 楽しい発表でした.
運営も少々手伝わせていただいた身としては楽しい会になって本当にうれしいです.
ustream + twitter で遠隔参加の皆さん, 現地参加の皆さん,
スピーカーの皆さん, pgcafe 運営方々, 幹事の Tommy さん, 会場提供いただきましたIDCフロンティア様. ありがとうございました. お疲れ様でした.

2010/04/03

iPhone 開発始めの一歩

Filed under: 離散的な気まぐれ — タグ: , , — Kohyama @ 00:54


Xcode と iPhone SDK 入れました.
コードエディタはなんか Emacs っぽいバインドなんですね.
Emacs キーバインドも, Mac のコマンドキー主体のキーバインドも, 手が覚えているので, 頭のスイッチをいれればそれぞれはすんなりいきますが, 二つを頻繁に切り替えるには, ちょっと頭が固くなってる.
シミュレータで, ボタンを押すとテキストフィールドからテキストフィールドへ文字列をコピーする動作を実装してみました. 新しい開発or動作環境でまずやってみるチュートリアルです.

今日はここで終わり.

実機で動かないと意味ない訳ですが, その辺は手続き的な敷居があるようです.
そのあたりもご助言くださる方がおります.
ご相談に乗ってくださる方はもちろん, 開発の初期段階を解説してくださっているブログ, 無料記事など. 心から感謝します.

2010/03/28

MacとiPhone

Filed under: 離散的な気まぐれ — タグ: , , — Kohyama @ 01:18


何周か分からないほど周回遅れで iPhone 開発に参画すべく, 社で MacBook Pro と iPhone を調達しました.
これからセットアップです.
先輩諸氏には折に触れご指導を仰ぎたいと思いますので, 何卒よろしくお願いいたします.

2010/03/24

Google App Engine で java.util.Date

Filed under: 離散的な気まぐれ — タグ: , — Kohyama @ 01:50


GAE/J では java.util.Date 型を new すると, タイムゾーンがUTC のオブジェクトができます.
タイムゾーン JST で, 文字から Date 型にしたり, Date 型の値を JST で文字列化する場合の覚書.
宣言:

private java.text.SimpleDateFormat sdf;

初期化:

sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
sdf.setTimeZone(TimeZone.getTimeZone("JST"));

JSTの時刻の書かれた String s をパースして Date d へ代入:

d = sdf.parse(s);

Date d を文字列化して String s に代入:

s = sdf.format(d);

2010/03/22

引越し

Filed under: 離散的な気まぐれ — タグ: — Kohyama @ 08:04

個人ページ用のレンタルサーバですが, いろいろやろうとすると, SSHに対応していないことの辛さが効いてきたので, 引っ越すことにしました.
これを機会にレンタルサーバサイトの提供するドメインではなく, ドメインを独自に取得しました.
 サイトトップ: ALGOBIT
 ブログ: 離散的な気まぐれ
よろしくです.

2010/03/04

第1回プログラマーズカフェナイト@原宿 行ってきました

Filed under: 離散的な気まぐれ — タグ: — Kohyama @ 02:11

開催 2/22 なので遅すぎるエントリですが.
発表は GAE, Slim3, HTML5, ExtJS, PhoneGap, ギークハウスの紹介 など. どれもためになりました.
本会も去ることながら, 閉会後のお話も楽しかったです. 愚痴みたいになってしまう自分の話をもっと抑えて, 皆さんの話をもっと聞けばよかったと反省. お話してくださった方々どうもありがとうございました.

会場提供 FLATz さんのブログエントリ「第1回プログラマーズカフェナイト@原宿 を開催しました!」
三鷹プログラマーズカフェ

« Newer PostsOlder Posts »

Copyright © 2010 Yoshinori Kohyama All Rights Reserved.