月別アーカイブ: 2015年6月

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 でやってみました。

#!/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

#-*- 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
#    https://wiki.gnome.org/Apps/EyeOfGnome/Plugins
#    Eye of GNOME Reference Manual
#    http://developer.gnome.org/eog/stable/index.html
#
#   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!

Terminal.app

Fedora 使いが MacBook Air を使って約二週間。

実は一番困ったのが端末エミュレーター(Terminal.app)だったりする。
いや普通の人は端末はあまり使わないだろうけど。

Fedora のように Ctrl+D で終了すると bash のみが終了しウインドウが残る。
終了するには command+Q を使うのだが癖でつい何度もやってしまう。
exit を打たなければいけない某 OS よりはいいけど。

それと US キーボードなのにバックスラッシュが¥記号に。
コレは設定で変更できるようだ。

backslash

プロンプト表示でユーザー名が最後というのも気に入らない。
bash なので ~/.bash_profile で PS1 変数を指定すればいいはず。
gnome-terminal の使い方 – L’Isola di Niente
せっかくだから Fedora 等と同じように ~/.bashrc も用意して。

~/.bash_profile

if [ -f ~/.bashrc ]; then
	. ~/.bashrc
fi

PATH=$PATH:$HOME/.local/bin:$HOME/bin
export PATH

~/.bashrc

PS1='[\u@\h \W]$ '

を $HOME に用意して再ログイン。
見事に Fedora と同じプロンプトになりました。

ps1

ついでに Fedora デフォルト同様に ~/bin へのパスを通した。
ホームに bin ディレクトリを作りこんな感じのスクリプトを。

edit

#!/bin/bash

for param in $@; do
	open -a /Applications/TextEdit.app $param
done

edit

実行パーミッションン (chmod +x) を忘れずに。
これで Mac でも端末から GUI アプリが使えます。
Linux はこんなことしなくても使えるのに面倒臭い…

てゆーか、もっと Mac らしい使い方をしろよ俺。

Mac x86_64

さて、筆者もついに Mac を手に入れた。
これで Mac 向けアプリの開発、をするかどうか今は解らない。

ところで Mac 向けアプリの C コンパイラは UNIX 系なので gcc である。
困ったことに Xcode 付属品らしい、つまり Xcode を入れる必要がある。

App Store から落とす、って Xcode って 2.57GB もあるんかい!
単なるアプリケーションなのに Fedora iso の二倍って何だよ。
Linux アプリはデカくてもキロバイト単位なので本当にウンザリする。

インストールが終わって即使おうと思ったが gcc が見つからない。
C言語入門 – MacでC言語 – コンパイラ(gcc)のインストール – Xcode – Command Line Tools – Webkaru
こんなページを見つけたけど Yosemite では一度起動すれば自動で入った。

gcc で C/C++ – L’Isola di Niente
OS X はたしか 64bit だったはず。
コンパイラが gcc だから Linux x86_64 と同じ結果になると思う。
上記ページの x86_64 サイズ確認コードを動かしてみる。

以前は sizeof の値を %d にしていたのだが %lu にしろと Warning が出た。
Mac の gcc は Linux より警告レベルを高く設定しているようだ。
ということで %lu 指定に書き換えて普通にビルド、そして実行。

x86_64

やはり Linux x86_64 とまったく同じだね。
日本語を扱う場合は UCS-4 だということに注意しよう。
てか UTF-16 なのは Windows だけなんだが。

しかし /usr/include ディレクトリが存在しないぞ。
スタジオエッチはドコにあるのだ?

stdioh

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include
にあった、なるほど Xcode 必須なわけだ。

まあ Xcode のエディタは思っていたよりシンプルで使えそう。
エディタを探そうと思ったけどコレを使いこなしたほうがよさげ。
というか、なるべくアプリは少なくしたい、デカいのはもう嫌。
私的に必須な逆インデントのショートカットキーもあるので問題ない。

no_modeline

さすがに modeline は使えなかった、惜しい。
ちなみに Gedit は Yosemite では起動すらできなかった。