Mac gvfs-open

Mac で”デフォルトアプリにて開く”は open コマンド。
Linux の gvfs-open と同じ、説明が逆の気がするけど。

ということは Finder で /usr/bin を開くには

cd /usr/bin
open .

usr_bin

おぉ、コマンドからなら普通に開くことができるんだ。
open `pwd` でもいいけどドットのほうが簡単。

端末もいいけどたまに GUI でファイルを確認したい時があるのよ。
隠し属性ファイルを Finder に表示する必要なんて無いんだな。
finder コマンドが存在しなくて困っていたけどこんな方法が。

ドットディレクトリ内を探したい時は ls -a コマンドで。
Linux を使っている人なら常識だけど。

後はパスの通った場所にコレを用意して。

#!/bin/sh
open -a /Applications/Atom.app $@
#!/bin/sh
open -a /Applications/iHex.app $@

osx_support

よしコレでファイルの確認作業が格段に楽になった。
本当は Nautilus の拡張みたくやりたいけどまだ手段がワカラン。
iHex は無償だったので選んだけど確認用途なら十分かな。
しかしまあ、同じ UNIX というだけでありがたいYO!

Mac JavaScript

mac ではスクリプトは何を使おう?

筆者が主に Python を使っているのは Linux だったから。
書くまでもなく PyGObject という究極便利なものがあったから。
Linux なんてアップデートマネージャや dnf が Python 製。

使う言語はフレームワークやライブラリで決まる。
簡易な CMS ツールなら wordpress ベースなので PHP に。
スマホゲームなら Unity 一択に近いので C# に。

Mac で Python はどう考えたって役立たずだよなぁ。
Apple スクリプトを覚えても他で応用が効かないのが痛い。
ならば JavaScript なんてどうだ?

なんたって上記のドレも JavaScript が絡む。
今や GNOME デフォルトアプリでさえ Gjs 製がある。
Unity(mono) は JavaScript も使える、というように。
mac でも Gjs のようなものがあれば便利だけど。

知らないうちにMacがシステム標準でJavaScriptで操作できるようになってた (JXA) – Qiita

って思っていたら Yosemite なら普通に使えるんかい!

早速 .bashrc にエイリアスを用意しよう、mjs でいいかな。
どうでもいいけど mac には標準で nano が入っている。
ドットファイルの編集をする場合なんかに便利だね。

nano

1
2
3
4
5
6
7
8
9
10
PS1='[\u@\h \W]$ '
 
mjs()
{
        if [ $# -eq 0 ]; then
                osascript -l JavaScript -i
        else
                osascript -l JavaScript $@
        fi
}

普通にエイリアスを登録でもいいけど筆者は関数にした。
これなら Gjs や Python 同様に引数の有無でインタラクティブシェルに。
同じようにしたい人は関数名をお好みで変更してね。

再ログインで使えるようになったのを確認してと。
control+D で終了できるけど終了時に改行はしてくれないようだ。
さて、Gjs 並には使えるのかな?

mac_js

let は無理か、まあ Safari でも使えないからね。
JavaScript エンジンは Safari と同じ Nitro だろうし。
でも Gjs のようにタプルは問題なく使えるようです。
SpiderMonky に統一ってわけにはいかないよな。

標準出力は console.log() を使えば可能。
print() を用意してほしいな、手段を知らないだけかもだが。

さて言語は使えることが解ったので GUI を作る方法でも。

OS X YosemiteからJSでMacアプリを作れるようになったって!?と聞いてみたものの | mah365

こんなのを見つけたけど osascript では動かないや。
スクリプトエディタ.app で command+R の必要がある。
うーん、これじゃたいして役に立たないぞ。

何か手段があるのだろうけど検索しても出てこない。
そりゃこんな Mac らしくないことがしたい人は少ないよNE。
もう少し調べる。

Atom(TextEditor)

GitHub、オープンソースのテキストエディター「Atom」の正式版v1.0.0をリリース – 窓の杜
MS、Win/Linux/Mac対応のコードエディター「Visual Studio Code」をプレビュー公開 – 窓の杜

こんなのあるんだね。
Mac と Fedora で共通のテキストエディタが欲しかった自分にピッタリかな。

Electronでアプリケーションを作ってみよう – Qiita

Electron というクロスプラットホームライブラリで GUI 構築らしい。
ゴミ糞(mono+WindowsForm)よりは圧倒的にまともなクロスプラットホームになりそう。
いや、ウチ姫で mono(Unity) にはお世話になっているけど。

ということで。
Visual Studio Code は大きなお世話なので Atom だけ試す。

Atom

Linux rpm で 56.8MB
Mac は Atom.app のまま配布で 203.5MB

やはり Mac のアプリってやたらとデカいよ…
/Applications ディレクトリに移動するだけで動くのね。

両方起動すると唖然とするほど共通な GUI に感心する。
メニューや設定は英語だけど Linux, Mac 使いなら普通のこと。

Fedora では日本語が豆腐になるが Font Family を変更すれば即時解決。
Mac はそのまま日本語表示できる。

vlgothic

mac_atom

起動時間もキー入力の反応速度もまったく問題無し。
使い勝手は Mac では Ctrl が command になるだけで基本同じ。
保存ダイアログ等は OS 標準のものが出る。

肝心のエディタ部分もかなり優秀。
拡張子で色分けは常識、HTML 中の StyleSeet, JavaScript も認識。
Gedit と比較しても同等かそれ以上。

atom_highlight

設定の Soft Tabs で Tab キーを半角スペースにできる。
BackSpace(Mac は Delete) でインデント分を削除。
Shift+Tab も使えた、マジで Gedit の代わりになる。

閉括弧自動補完は大きなお世話だが人によるだろう。
設定は最小限、でも不満は特にない。
Line Height は 1.35 にすると Gedit とほぼ同じになるね。

Home(command+←) キーでインデントの先頭にカーソル移動する。
Xcode や TextEdit.app の不満点だったのでコレは嬉しい。

面白いのは Ctrl+マウスホイールでズームできる。
Mac はタッチパッドでズームできる、使い道はともかく。
Web ブラウザと同じ、なるほど HTML5 ベースだなと納得する所。

一つだけ気になるのがアプリ再起動で前回開いていたファイルを開くこと。
Mac やスマホアプリのデフォルト動作がコレだし時代なんだろうね。
Ctrl+W(command+W) で全部閉じてから終了すればいいだけ。

試すだけのつもりが基本機能には文句のつけようがない。
私的に Mac 用はコレに決まり、Gedit みたく使えればいいんだよ。
検索するとメニューの日本語化もできるようだけど筆者は別にいいかな。

後は自作 Gedit プラグインと同等な拡張を作ればもっと便利に。
いやコードスニペット機能は必須だぞ、既に拡張があるかな?

というか Electron ってスゲェな。
クロスプラットホームアプリはゴミ糞という常識が通じない。
今後はデスクトップもコレがデファクトになる可能性すら感じる。

GtkPopoverMenu

Fedora 22 にしてから一ヶ月近いが GTK3 関連でヤル気が出ない。
新規 Widget が GtkPopoverMenu くらいしかないので。
GtkPopover とたいして違わなそうだし。

多分 Nautilus のメニューがコレだと思うけど。
GTK+ Inspector で確認してみるか。

nautilus316menu

やはりっっってあれ、GtkModelButton って何?
devhelp で確認するとコレも 3.16 からの新規 Widget のようだ。
何故こんなにヒッソリと追加なのよ、よし調べて使ってみよう。

gtk3-demo には無い、海外を検索してもまだ誰もやっていない。
また devhelp のみが頼りか、いつものことだ。

GtkPopoverMenu: GTK+ 3 Reference Manual

GtkPopover のサブクラスなのでやはり GtkBox に積み重ねる必要あり。
action-name プロパティに GAction のアクション名指定だけでイケるっぽい。
今回は devhelp のように GtkBuilder でやってみました。

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
#!/usr/bin/env python3
  
from gi.repository import Gtk, Gio
 
POPOVER = '''<interface>
<object class="GtkPopoverMenu" id="menu">
  <child>
    <object class="GtkBox">
      <property name="visible">True</property>
      <property name="margin">10</property>
      <property name="orientation">vertical</property>
      <child>
        <object class="GtkModelButton">
          <property name="visible">True</property>
          <property name="active">True</property>
          <property name="action-name">win.hello</property>
          <property name="text" translatable="yes">Hello</property>
        </object>
      </child>
      <child>
        <object class="GtkModelButton">
          <property name="visible">True</property>
          <property name="action-name">win.world</property>
          <property name="text" translatable="yes">World</property>
        </object>
      </child>
    </object>
  </child>
</object>
</interface>'''
  
class PopoverMenu(Gtk.ApplicationWindow):
    """
        GtkPopoverMenu Test
    """
    def __init__(self):
        Gtk.ApplicationWindow.__init__(self)
        # DrawingAre
        area = Gtk.DrawingArea.new()
        # Popup Button
        button = Gtk.Button.new_with_label("Option")
        button.connect("clicked", self.on_option_button_clicked)
        # ActionBar
        bar = Gtk.ActionBar.new()
        bar.pack_end(button)
        #
        # Popup Contents
        builder = Gtk.Builder.new_from_string(POPOVER, -1)
        self.menu = builder.get_object("menu")
        self.menu.set_relative_to(button)
        action = Gio.SimpleAction.new("hello", None)
        action.connect("activate", self.hello_cb)
        self.add_action(action)
        action2 = Gio.SimpleAction.new("world", None)
        action2.connect("activate", self.world_cb)
        self.add_action(action2)
        #
        # pack
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(area, True, True, 0)
        vbox.pack_start(bar, False, False, 0)
        # self
        self.add(vbox)
        self.connect("delete-event", Gtk.main_quit)
        self.set_title("PopUp")
        self.resize(300, 200)
        self.show_all()
 
    def hello_cb(self, action, parameter):
        self.set_title("Hello")
 
    def world_cb(self, action, parameter):
        self.set_title("World")
 
    def on_option_button_clicked(self, widget):
        """
            Show Menu
        """
        self.menu.show_all()
  
PopoverMenu()
Gtk.main()

gtkpopovermenu

Gedit や eog のプラグインを作っておいて良かった。
多分やっていなければ action-name property でドンズマリしていた。

つまり action-name プロパティには win のプリフィクスが必要。
win なんて自分ではどこにも定義していないけど固定値みたい。
GtkApplication を使わなくてもいるのね、なんでだろう。

margin は 10 にすると Nautilus のメニューとほぼ一致する。
妙にデカい気がするけど多分タッチパネル化の準備だろう。

GtkBuilder の XML で vertical と書くことができるのか。
あまり使わないから知らなかった、整数や文字列だけかと思っていた。
つか devhelp のサンプルコードにコレ入れてくれよ。

この程度なら GtkBuilder よりコードで作ったほうが簡単そう。
他人が読むことを考えると XML のほうが理解しやすいだろうけどNE。

eog plugin 3.16

随分前に Fedora を 22 にアップデートしたような。
そうだ、自作 eog プラグインの更新を忘れていた!

私的に傑作プラグインだけど、そんなにリネームなんてしないジャン。
ということでとっとと更新しなければ。

Apps/EyeOfGnome/Plugins – GNOME Wiki!

公式は相変わらずヤル気ネェ…
とにかく Gedit 同様に GtkUIManager を排除する必要があるだろう。

GEdit 用に作った奴をコピペして Eog に書き換えてみた。
Eog.App なんて無いよと Python に怒られる、なんでじゃ!

dir(Eog) で調べると Eog.Application らしい、まぎらわしいわ!
AppActivatable も ApplicationActivatable だ、統一してよ。
同じ GNOME 標準アプリといっても企業ではなく GPL だからしかたがないが。

メニューを作ろうとしたけど extend_menu メソッドが使えない。
よく解らないのでソースを落とし reload プラグインを見てみる。
GtkApplicationWindow の activate で突っ込んでいた。
だから統一、、、まあいいか。

この方法だとメニュー排除が凄く面倒臭いようだ。
というか g_menu_item_set_attribute が何故か上手くいかない。
ええい面倒だ、メニューは廃止にしてしまえwww

昨今の GNOME に合わせシンプルにしたといえば皆納得するだろう。
実際筆者はメニューからリネームなんてしたことないもん。

Mac でも F2 をつい何度と押したことか、Mac は Enter なんだよね。
はどうでもよくて。
ということでこんなソースになりました。

renamedlg.py

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#-*- coding:utf-8 -*-
 
#    Eye of GNOME renamedlg plugin version 3.16.0
#    Copyright © 2012 sasakima-nao <sasakimanao@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    Eye of GNOME Plugins
#    Eye of GNOME Reference Manual
#
#   2015.06.16 3.16.0
#   Support eog 3.16 (Remove GtkUIManager)
#
#   2012.08.28 3.12.0
#   Support eog 3.12 (Python3)
#
#   2012.08.28 3.0.0
 
from gi.repository import GObject, Gtk, Eog, Gio, GLib
import os
 
class RenameDlgAppActivatable(GObject.Object, Eog.ApplicationActivatable):
    """
        Set GMenu and Accelerator
    """
    app = GObject.property(type=Eog.Application)
  
    def __init__(self):
        GObject.Object.__init__(self)
  
    def do_activate(self):
        self.app.add_accelerator("F2", "win.rename", None)
  
    def do_deactivate(self):
        self.app.remove_accelerator("win.rename", None)
 
class RenameDlgPlugin(GObject.Object, Eog.WindowActivatable):
    """
        Rename Dialog Plugin for eog 3.6
        Actibate from F2 key
    """
    __gtype_name__ = "Rename"
    window = GObject.property(type=Eog.Window)
    def __init__(self):
        GObject.Object.__init__(self)
 
    def do_activate(self):
        # Add Action
        self.action = Gio.SimpleAction.new("rename", None)
        self.action.connect('activate', self.on_rename)
        self.window.add_action(self.action)
 
    def do_deactivate(self):
        # Remove Action
        self.window.remove_action("rename")
 
    def do_update_state(self):
        #self.action.set_enabled(not self.window.is_empty())
        self.action.set_enabled(True)
        pass
 
    def on_rename(self, action, data=None):
        img = self.window.get_image()
        if img == None:
            return
        fullname = img.get_uri_for_display()[7:]
        path, name = os.path.split(fullname)
        label = Gtk.Label(name)
        entry = Gtk.Entry()
        entry.set_text(name)
        d = Gtk.Dialog( "Rename",
                        self.window,
                        Gtk.DialogFlags.MODAL,
                        (Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
                        Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT) )
        vbox = d.get_content_area()
        try:
            vbox.pack_start(label, False, False, 0)
            vbox.pack_start(entry, False, False, 0)
            d.show_all()
            def dlg_ok(self):
                d.response(Gtk.ResponseType.ACCEPT)
            def on_focus_in(self, widget):
                try:
                    # Calculate the number of characters
                    # Converted to a Display Name
                    s = entry.get_text()
                    displayname = GLib.filename_display_name(s)
                    i = displayname.rindex(".")
                    entry.select_region(0, i)
                except ValueError:
                    # Full select
                    pass
            def on_focus_out(self, widget):
                entry.select_region(0, 0)
            entry.connect("activate", dlg_ok)
            entry.connect("focus-in-event", on_focus_in)
            entry.connect("focus-out-event", on_focus_out)
            # Loop until success or Cancel
            while 1:
                if d.run() == Gtk.ResponseType.ACCEPT:
                    text = entry.get_text()
                    if text == "":
                        self.messagebox("File name is empty")
                        entry.set_text(name)
                    elif text == name:
                        self.messagebox("Have not changed")
                    elif  text in os.listdir(path):
                        self.messagebox("Found the same file name")
                    else:
                        # Get the EogListStore
                        store = self.window.get_store()
                        # Rename
                        newname = os.path.join(path, text)
                        os.rename(fullname, newname)
                        # Turn the queue
                        while Gtk.events_pending():
                            Gtk.main_iteration()
                        # Create EogImage
                        f = Gio.file_new_for_path(newname)
                        newimg = Eog.Image.new_file(f)
                        # Insert EogListStore
                        store.append_image(newimg)
                        # EogThumbView
                        tv = self.window.get_thumb_view()
                        tv.set_current_image(newimg, True)
                        # Turn the queue
                        while Gtk.events_pending():
                            Gtk.main_iteration()
                        # Remove Image from EogListStore
                        store.remove_image(img)
                        break
                else:
                    # Cancel Button
                    break
        finally:
            d.destroy()
 
    def messagebox(self, text):
        dlg = Gtk.MessageDialog(
                self.window,
                Gtk.DialogFlags.MODAL,
                Gtk.MessageType.WARNING,
                Gtk.ButtonsType.OK,
                text)
        dlg.set_title("Eye of GNOME"
        r = dlg.run() 
        dlg.destroy()
        return r

koruri

Gedit 及び Eye of Gnome プラグイン – L’Isola di Niente

よしよし、キチンとリネーム可能だぞと。
よく見ると update_state ハンドラが実験用のままじゃないか。
特に問題は無いから次の更新で修正しよう、オープンソースはそれでイイ。

*.plugin ファイルの仕様は 3.14 までと変わっていない。
IAge が今でも 2 のまま動く、多分ガン無視しているだけだろうけど。

ところで。
Gedit で今頃気が付いたが 3.16 は単語 W クリックの仕様が変わっていた。
do_update_state 等の W クリック単語選択はアンダーバーを含めるようになた。
以前は区切りになっていたはずなんだが、あの動作に慣れているのでとまどうYO!