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 では起動すらできなかった。

Connect of Linux and Mac

実は最近 MacBook Air 13inch を買った。
US キーボードだぜ、これでスタバでドヤ顔できるze!
行かないけど、おまけに中古だけど。

GNOME べったりな筆者に何が起こったのだ?
と思われても困る、ずっと前から欲しかったんだし。

サイトを持っていると Mac Safari での表示確認をしたくなるジャン。
プログラミングネタで Mac でのコンパイル結果も書きたいジャン。
US キーボードでないと打ち間違える筆者にはありがたい豊富な US キー中古。
しかも筆者は iPhone 持ちだ、欲しいに決まっている。

いや、MacBook を買おうか本気で迷って出るのを待っていたんだが。
実物に触りペコペコキーボードに萎えて勢いで中古を選んでしまったオチ。

中古だけどとっとと Yosemite に更新。
Safari で自サイトの表示確認、うん問題無かった。
WebKit だから CSS3 は問題ないと思っていたけど安心した。

さて、筆者がまず行うのは Linux との接続ですよね。
共有設定を開きコンピュータ名を決め [ファイル共有] を有効にする。

mac_setting

Fedora を立ち上げ Nautilus から [ネットワークを表示] をクリック。

nautilus_mba

あら、それしかやっていないのにアッサリ。
W クリックして Mac のユーザー名とパスワードを入力。

mba_connect

AFC だけでなく AFP プロトコルにも対応していたのか。
iPhone mount in GNOME | PaePoi

afp

Nautilus をもう一つ立ち上げ普通にドラッグアンドドロップ。
あたりまえのようにコピーできる、もちろん双方向で。
普通にファイル編集もできる、AFP はかなり柔軟なようで。
Mac からのアクセスは知らないけど別にイイよね、SELinux あるし。

Macintosh HD と表示されて驚いたけど /usr/bin とかは表示されない。
でも普通に Application ディレクトリにはアクセスできるという。

applications_directory

Mac のアプリ実体って実行パーミッションの付いたディレクトリなのね。
だからアプリを削除しただけでアンインストール終了なのか、ふむふむ。
同じ UNIX ベースとはいっても Linux とは随分違うんだな。

ということで、Linux は Mac にも普通にアクセスできる。

しかし、もっと早くコレを買えば良かった。
たまに Windows を使うみたいなイライラがまったく無い。

タッチパッドには驚愕した、マジでマウスはいらない。
二本指タップでコンテキストメニュー、三本指でウインドウの移動。
二本指スクロールは GNOME 同様にアクティブでなくとも使える、素晴らしい。

ウインドウは全部 command+Q で閉じる一貫性も GNOME 同様。
おかげで閉じるボタンが左にあっても困らない、まったく使わない。
Control を command に置き換えればほぼ GNOME アプリと同じに使える。
F11 がフルスクリーンならもっと良かったんだが。

ps, ls, grep 等 UNIX コマンドが一通り使えるのでありがたい。
bc も使える、計算もこれで困らないな。

Mac でさえ Linux 風に使おうとする筆者であった。