「タスク」から「予定」を分離してカレンダー表示，Org-mode仕立てで

# はじめに
Org-modeを使って「予定」だけをカレンダーに表示するお話です．途中，Org-mode ユーザしかわからない単語が乱舞しますが，知らなくても大丈夫だと思います．

> キーワード：Emacs, Org-mode, Calfw-org, iCal, Google Calendar, Calendar.app, Dropbox

## なぜこんなことをするか

Org-mode で GTD を始めると，すぐにアジェンダ機能を使うようになります．org-agenda によって提供されるアジェンダ機能は，日付に紐付いた「タスク」を一つのバッファに集約することです．

一方，Org-mode の「タスク」は，日付だけではなく時刻や行動の内容も表現できるため，「予定」として扱うこともできます．例えば，次のようなエントリーは，会議の予定を表す情報として適切です．

>  ** TODO 定例会に出席する
>    DEADLINE: \<2012-12-06 Thu 14:00-15:30 +1w\>
> 1. 前回議事録確認
> 2. 議論
> 3. まとめ

これは「毎週木曜日の午後2時から，90分間の会議をやる」という予定です．org-agenda でこのような予定を管理する最大の欠点は「管理するタスク項目が増えると他のタスクの中に埋もれていく」という点です．せっかく集中管理しているにも関わらず，重要な予定を見逃すのは嫌ですし，一覧表示した大量の「タスク」から重要な「予定」がどこに配置されているかを探し出すのも嫌です．

そこで，「タスク」群から「予定」だけを切り出して，スケジュールソフトあるいはEmacs上でチェックするというフローがあれば有用です．

![予定をカレンダーに](https://qiita-image-store.s3.amazonaws.com/0/12232/c008a746-fe5a-c249-08de-520e37b7be6f.png)


# 「タスク」ではなく「予定」をチェックしたい

やることは簡単です．org-agenda で扱う orgファイルとは別に，重要な予定（カレンダー）だけを扱うファイルを作るだけです．Org-mode のエクスポート機能で簡単にical形式に変換できますから，あとはgoogleカレンダーで見たり，Calendar.appで見たりできます．Emacs の中で完結したい人は，calfw-org を使えばいいのです．

``（注意）yasnippet との組み合わせで， `C-c &` が奪われてしまうかもしれません．その場合は， `(define-key org-mode-map (kbd "C-c 4") 'org-mark-ring-goto)` というように別なところに割り振ってください．
``

![データフロー](https://qiita-image-store.s3.amazonaws.com/0/12232/7ff15511-a62a-f0e0-e781-fbbec6b11e5c.png)


# カレンダーの元となるorgファイルを作る

まず重要な予定を格納するOrgファイルを作ります．org-directory 直下に org-ical.org というファイルを作成したとしましょう．

## とりあえず org-agenda でも扱えるようにする

org-agenda でも表示されるように，org-agenda-files を設定します．

````cl:
(setq org-agenda-files
         '("~/Dropbox/org/org-ical.org" "~/Dropbox/org/next.org"
           "~/Dropbox/org/work.org" "~/Dropbox/org/research.org"))
````

## バッファにキーバインドを当てておく（オプション）

以下の例では，C-M-cを押すと，一発で「予定」専用のorgファイルが開きます．

````cl:
(defun show-org-buffer (file)
    "Show an org-file on the current buffer"
    (interactive)
    (if (get-buffer file)
        (let ((buffer (get-buffer file)))
          (switch-to-buffer buffer)
          (message "%s" file))
     (find-file (concat "~/Dropbox/org/" file)))) 
(global-set-key (kbd "C-M-c") '(lambda () (interactive)
                                  (show-org-buffer "org-ical.org")))
```

# orgファイルに「予定」を追加する
## サクッと予定を追加できるようにする（オプション）

org-capture を使って，Emacsのどこからでも予定を追加できるようにします．この場合は，`C-c r c` と打つと新しい予定を編集するためのバッファが開かれ，編集後`C-c C-c`すれば，先ほど作成した org-ical.org に格納されます．

````cl:
(defvar org-capture-ical-file (concat org-directory "org-ical.org"))
 ;; see org.pdf:p73
 (setq org-capture-templates
      `(("t" "TODO 項目を INBOX に貼り付ける" entry
         (file+headline nil "INBOX") "** TODO %?\n\t")
         ("c" "同期カレンダーにエントリー" entry
          (file+headline ,org-capture-ical-file "Schedule")
          "** TODO %?\n\t")))
````

## どこかで見つけたタスクを予定専用のorgファイルに飛ばせるようにする（オプション）

org-refile を使うと，カレンダーに表示したいタスクをどこかのバッファで見つけた時に，瞬時に登録できます．登録したいタスクにカーソルを置いて，`C-c C-w` して予定専用のorgファイルを選べば，当該タスクが予定専用のorgファイルに転送されます．

````cl:
 (setq org-refile-targets
       (quote (("org-ical.org" :level . 1)
               ("next.org" :level . 1)
               ("sleep.org" :level . 1))))
````

# iCalを生成する
## ical ファイル生成のヘルパー関数を実装する

次の関数を実装しておけば，`C-c 1` を押すだけで ical 形式のファイルが生成されます．後段の転送方法と組み合わせれば，どこにでも公開できます．

````cl:
(require 'org-icalendar)
(defun my-org-export-icalendar ()
  (interactive)
  (org-export-icalendar nil "~/Dropbox/org/org-ical.org"))
(define-key org-mode-map (kbd "C-c 1") 'my-org-export-icalendar)
````

## 生成される ical ファイルのプロパティを設定する

````cl:
;; iCal の説明文
(setq org-icalendar-combined-description "OrgModeのスケジュール出力")
;; カレンダーに適切なタイムゾーンを設定する（google 用には nil が必要）
(setq org-icalendar-timezone "Asia/Tokyo")
;; DONE になった TODO は出力対象から除外する
(setq org-icalendar-include-todo t)  
;; （通常は，<>--<> で区間付き予定をつくる．非改行入力で日付がNoteに入らない）
(setq org-icalendar-use-scheduled '(event-if-todo))
;; DL 付きで終日予定にする：締め切り日（スタンプで時間を指定しないこと）
(setq org-icalendar-use-deadline '(event-if-todo))
````

# カレンダーを公開する
## Dropbox 経由で公開するアプローチ

生成した ical を Dropbox の Public フォルダに置けば，外部サービスからそのファイルを参照できます．セキュリティの問題があるので，例えば次のようなディレクトリを生成して配置すればよいでしょう．ディレクトリの名称をpwgenなどのコマンドを使って作成します．

> ~/Dropbox/Public/ical/03zxQZ1abIyPOMFM/org-ical.ics

## 自分のサーバを経由して公開するアプローチ

````cl:
;; エクスポート後に外部サーバに飛ばす
(add-hook 'org-after-save-iCalendar-file-hook
          (lambda ()
            (shell-command-to-string
             "scp ~/Dropbox/org/org-ical.ics hoge:~/public_html/ical")
            (message "Uploading ... [DONE]")))
````

毎度パスワードを入力したくないので，鍵交換で自動ログインできるようにしておきます．また，次のように .ssh/config を書き込んでおけば，エイリアスを使ってサーバを指定できますね．

>  Host hoge
>    HostName hoge.org
>    User hoge
>    Port 2222

## 定期的に公開する（オプション）

定期的にヘルパー関数を呼べば，常に同期出来ます．

````cl:
(run-with-idle-timer 600 t 'reload-ical-export)
(defun reload-ical-export ()
  "Export org files as an iCal format file"
  (interactive)
  (when (string= major-mode 'org-mode)
    (my-org-export-icalendar)))
````

# カレンダーを見る
## カレンダーソフトで閲覧する
  - google
	グーグルカレンダーを開いて「他のカレンダー」から「URLから追加」を選びます．ただし，グーグルカレンダーは，参照したカレンダーをリアルタイムで更新してくれないので，orgで予定を変更しても反映されるまでに時間がかかることがあります．

  - Calendar.app
	「ファイル」 => 「新しいカレンダーの購読」でファイルの場所を指定します．こちらは強制リロードができるので，比較的リアルタイムに情報を更新できます．

## calfw-org で見る

calfw-org で見ると，こうなります．

![Calfwでみる](https://qiita-image-store.s3.amazonaws.com/0/12232/a4784cb0-aa56-eda7-aea0-6e6ada1e983d.png)


このようにcalfw-orgを使うと，orgバッファにある予定を綺麗なインターフェイスでEmacsに表示できます．[Kiwanami先生](https://github.com/kiwanami/emacs-calfw)のファイルに，今回の記事で紹介している特別なOrgファイルをカレンダーとして利用するための修正とリンク機能の強化を行なっています．導入してみてください．

> https://github.com/takaxp/emacs-calfw/blob/master/calfw-org.el

その上で，以下の設定を.emacsに追加してください．

````cl:
(when (autoload-if-found 'cfw:open-org-calendar "calfw-org"
                         "Rich calendar for org-mode" t)
  (eval-after-load "calfw-org"
    '(progn
       ;; calfw-org で表示する org バッファを指定する
       (setq cfw:org-icalendars '("~/Dropbox/org/org-ical.org"))  
       ;; org で使う表にフェイスを統一
       (setq cfw:fchar-junction ?+
               cfw:fchar-vertical-line ?|
               cfw:fchar-horizontal-line ?-
               cfw:fchar-left-junction ?|
               cfw:fchar-right-junction ?|
               cfw:fchar-top-junction ?+
               cfw:fchar-top-left-corner ?|
               cfw:fchar-top-right-corner ?| ))))
````

~~変数cfw:org-icalendarsは，上記のパッチにしかありませんので気をつけてください．~~
本家に取り込んでいただきました．

この状態で，M-x open-org-calendar とすれば，リッチなカレンダーで予定を見られます．

## 予定を編集する

calfw-org でリッチなカレンダーを表示したら，気になる予定にカーソルを置いて（`C-c C-o`）すると，対応する「予定」にバッファが移動します（これもパッチで追加した機能です）．適当に予定を編集して（`C-c &`: M-x org-mark-ring-goto）を押すと，カレンダーに戻れます．表示をリフレッシュするために（`r`）を押してください．（そのうち自動化したいところ）

# おわりに
今回の記事は，Org-modeを利用する上で「タスク」と「予定」を区別しませんか？という提案です．そのために専用のバッファを作り，iCal形式のファイルを出力する手順を示しました．少し手を加えたcalfw-org.elを利用することで，「タスクはagendaで確認／編集」し，「予定はcalfw-orgで確認／編集」する手順について説明しました．

マニアックな方，どうですか！

なお，[worg](http://orgmode.org/worg/) に calfw-org についてオープンに議論するページを近々設ける予定です．コメントがある方はぜひお願いします m(_ _)m

P.S.

org界隈ではカレンダー周りが少しホットです．
  - [org-caldav](https://github.com/dengste/org-caldav/blob/master/README.md)
  - [org-gnome-calendar](https://github.com/llvilanova/org-gnome-calendar/blob/master/README.md)