title.png

^<< 2011.07/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 >>$

Trackback の仕組みはありませんので、コメントにでも残していただくと嬉しいかも、です。

(2011.07.24)

Qt開発メモ(4)

QDesktopService::openUrl()

あるファイルを、そのデフォルトのアプリで開きたい場合(WindowsならExplorerで、Mac OS X ならFinderで ダブルクリックして開くのと同じようにする)は、Qt でやるなら QDesktopService クラスの staticメソッド openUrl() を使う。

このメソッドの返値は bool なのですが、 私が試してみた限りでは、少なくとも Windowsでも Mac OS X でもこのメソッドはいつも trueを返すようです。 たとえ関連づけられたアプリがない場合でも false を返さないので、任意のファイルを開こうとするが、関連づけられたアプリがない時は警告メッセージをだしたい、という場合にはこれだと困るわけで。

それでも、Windows の場合は Explorer が「関連づけられたアプリがないけど、どうする?」ダイアログをだしてくれるようですが、Mac OS X の場合は本当になにも出てこないので、困ります。

結局、自分で書くことにしました。(とりあえずMac側だけ、Winの場合はそのまま QDesktopServic::openUrl() を使う)

Launch Services の API LSOpenCFURLRef() を使えば、ほぼ所望のことが実現できます。もちろん、ファイルパスを FileSystemRepresentation にしてあげる必要があるのは先日書いたとおり。

Qtはよくできたクロスプラットフォームフレームワークだとは思いますが、この手のフレームワークはその限界を超えたことがしたい場合にどうするか、というのも結構問題だと思います。用意された材料ではどうしようもなくて、箱庭の外に出るしかない時、多少フレームワークの調和は乱れても思い切って出てみるのか、それとも用意された材料だけで作れるように仕様の方を変更するのか、はバランスの問題としか言いようがないのだけど。私だってせっかくQt使ってるのに #ifdef の嵐にしたいわけじゃありません(^^;

Qtは トリプルライセンスで、GPL/LGPL でもあるわけなので、自分で箱庭の材料を増やす、って手ももちろんあるけれど:-)

namespace
{

#if defined(Q_OS_WIN)
inline bool openAFileWithDefaultApplication(const QString& target)
{
    return QDesktopServices::openUrl(QUrl::fromLocalFile(target));
}

#elif defined(Q_OS_MAC)
CFURLRef convertToCFURLRef(const QString& in)
{
    CFStringRef before = CFStringCreateWithCString(kCFAllocatorDefault, in.toUtf8().constData(), kCFStringEncodingUTF8);
    Q_ASSERT(before);

    std::vector<char> buff(CFStringGetMaximumSizeOfFileSystemRepresentation(before), '\0');
    if (!CFStringGetFileSystemRepresentation(before, &buff.at(0), buff.size()))
    {
        Q_ASSERT(false);
    }
    CFRelease(before);

    return CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)&buff.at(0), buff.size(), false);
}

inline bool openAFileWithDefaultApplication(const QString& target)
{
    CFURLRef theTargetURL = convertToCFURLRef(target);
    Q_ASSERT(theTargetURL);

    const OSStatus osStatus = LSOpenCFURLRef(theTargetURL, NULL);

    CFRelease(theTargetURL);

    return (noErr == osStatus);
}
#endif

}

あ、あともうひとつ手はあって、昔からの Mac開発者ならよくご存じのあの方法、「Finderにお願いする」ってのもありますね。 これだと、Finderが「関連付けられたアプリないけど、どする?」ダイアログを出してくれます。 odoc のAppleEventを飛ばしてもいいんですが、これだとデスクリプタの構築などがめんどくさいので、 お手軽なのは oscript コマンドでApppleScript を発行することかな。

inline bool openAFileWithDefaultApplication(const QString& target)
{
    QStringList scriptArgs;
    scriptArgs << QString("-e")
               << QString::fromLatin1("tell application \"Finder\" to open POSIX file \"%1\"").arg(target);
    return (0 == QProcess::execute("/usr/bin/osascript", scriptArgs));
}

ツッコミ

Kawano 委細はよくわかりませんが、コメントしてみます。
ぼくにわかるのは Define とか elif ていうのはC言語っぽい命令や定義文だなあということくらいですが
・・・
(2011/07/27 10:04:24)