に投稿

[Qt] QtでLet’s MDI

QtでMDIを扱うときの助けになればと思い書いてみました。でも、文章力が無いので、細かくは説明できません……。ここに書くコードはパブリックドメイン・ライセンスとします。煮るなり焼くなりしてください。ただ、実際に使用したりブログなどで記事として使う場合はここにあるソースのAniLaの部分は自分のプロジェクト名などに書き換えてくださいねっ。

MDIスケルトンのための基底クラスを作る

まずは、私はMDIソフトの骨格となる部分を作っています。複数のアプリが同じ形式でできるようにするために…です。

私がやっている方法では pure virtual な関数を作って、自分が作るアプリのメインウィンドウはこのクラスから派生して作っています。

普通にQtCreaterやVS Qt add in などで雛形を作ると、QMainWindowクラスのサブクラスが作られますが、下記のクラスを挟みこんで使っています。

使用前

QMainWindow — 派生 –> MyWidget

使用後

QMainWindow — 派生 –> AniLaMdi — 派生 –> MyWidget

そして、そのままではSDIのままですので、Qtデザイナーを使って、MyWidget の ui クラスのに QMdiArea を貼り付けます。

QMdiArea はcentralWidgetの中にいっぱいに広がるようにレイアウトを設定しましょう。

そして、AniLaMdi にある関数を呼び出して色々と設定します。設定する過程で自動的に QMidArea のポインターを取得するようになっています。

いろいろな設定といっても、主に pure virtual な関数をメンバに設定するという作業と、各種コネクション用の呼び出しをすることです。実は、下記のファイルだけでは説明不足です。関数にコメントも付けてませんし…。

このクラスを使って実際にどうすればMDIの操作できるウィンドウが出来るのかというのは、また後日、暇なときにでも解説してみたいと思います。

とりあえず、このソースからだけでも何かのヒントを得られる人は得られると思いますし、私ももっとこうしたほうがいいとかアドバイスを貰いたいと思っていますので、この部分をまず公開してみた所存であります。

本当は、MyWidgetに当たる部分も合わせて公開しないとわからない部分も多いと思いますが、なにぶんサンプル用としてのMyWidgetを作っていなくって…、現在実際に売り物として作っているペイントソフトのMyWidgetに当たる部分しか無く…、また、ブログ用にMyWidgetに当たる部分を作成する時間も作れず……、じゃぁ、公開しないほうがいいのかというと、Twitterなどの影響で Low な情報でも公開しちゃうのもアリかなと思えた所存で…、この数日の(本来の自分ではあまりやらないような技術的な記事の)投稿を行っています。

つまり、Lowな情報でも出してしまえ的な風潮にのってみようか…というノリで描いた記事です。

参考にして頂ければ…と思います。

● anilamdi.h ヘッダーファイル

#ifndef ANILAMDI_H
#define ANILAMDI_H

#include <QtDotNetStyle>
#include "anilatoolbarmanager.h"

#define KEY_GEOMETRY            "mdi/geometry"
#define KEY_DOCKSTATE           "mdi/dockState"
#define KEY_TOOLBARSTATE        "mdi/toolBarState"
#define KEY_RECENTLYPATH        "mdi/recentlyPath"
#define TOOLBARSTATE_SUFFIX     ".TBState"

class AniLaMdi : public QMainWindow
{
    Q_OBJECT
public:
                    AniLaMdi( int dockBarVersion = 0 );
signals:;
            void    existSubWindows( bool isExist );
            void    existSubWindow( bool isExist );
public:
            void    initinstance();
            void    exitinstance();
public slots:;
    virtual bool    fileOpen( const QString & path ) = 0;
            void    styleWinClassic();
            void    styleWinXP();
            void    styleWinVista();
            void    styleWinDotNet();
            void    styleWinOfficeBlue();
            void    styleWinOfficeSilver();
            void    styleWinOfficeOlive();
            void    styleMotif();
            void    styleCDE();
            void    stylePlastique();
            void    styleCleanlooks();
            void    aboutQt();
protected:
    // Pure virtual function
    virtual QString subTitle( QMdiSubWindow * subWindow ) = 0;
    virtual bool    maybeSave() = 0;
    virtual void    onInitInstance() = 0;
    virtual void    onExitInstance() = 0;
    virtual void    onReadSettings( QSettings & settings ) = 0;
    virtual void    onWriteSettings( QSettings & settings ) = 0;
    virtual void    onConnectMainWindow( bool isConnect );
    virtual void    onConnectSubWindow( bool isConnect,
                                        QMdiSubWindow * subWindow );
    virtual void    onSubWindowAllClosed();
    // functions
    static  bool    connection( bool isConnect,
                                const QObject * sender,
                                const char * signal,
                                const QObject * receiver,
                                const char * method,
                                const Qt::ConnectionType & type
                                    = Qt::AutoConnection );
            bool    setConnection( bool isConnect,
                                   QAction * sender,
                                   const char * signal,
                                   const QObject * receiver,
                                   const char * method );
            bool    connectToolBarManager( bool isConnect,
                                           QAction * sender,
                                           const char * signal,
                                           const char * method );
            bool    connectChanged( bool isConnect,
                                    QAction * sender,
                                 const char * method );
            bool    connectHovered( bool isConnect,
                                    QAction * sender,
                                    const char * method );
            bool    connectToggled( bool isConnect,
                                    QAction * sender,
                                    const char * method );
            bool    connectTriggered( bool isConnect,
                                      QAction * sender,
                                      const char * method );
            bool    connectTriggeredMdiArea( bool isConnect,
                                             QAction * sender,
                                             const char * method );
            void    connectRecentlyList( bool isConnect,
                                         QMenu * menu );
            void    connectSubWindowListTail( bool isConnect,
                                              QMenu * menu );
            void    connectPainListHead( bool isConnect,
                                         QMenu * menu );
            void    connectForSubWindow( bool isConnect,
                                         QAction * action );
            void    connectForSubWindows( bool isConnect,
                                          QAction * action );
            void    addToolBarManager( QAction * action );
            void    removeToolBarManager( QAction * action );
            void    updateSubWindowTitles();
            void    addRecentlyPath( const QString & path );
            QString windowFilePath( QMdiSubWindow * subWindow );
    QMdiSubWindow * findSubWindow( const QString & path,
                                   const QMdiArea::WindowOrder & order
                                       = QMdiArea::ActivationHistoryOrder,
                                   bool isReverse = true );
    // Events
    virtual void    closeEvent( QCloseEvent * event );
    virtual void    dragEnterEvent( QDragEnterEvent * event );
    virtual void    dropEvent( QDropEvent * event );
protected slots:;
            void    setActiveSubWindow( QWidget * widget );
            void    setActiveSubWindow( QMdiSubWindow * subWindow );
            void    clearRecentlyPath();
private:
            QMenu * senderMenu();
            void    removeRecentlyList();
            void    removeSubWindowListTail();
            void    removePainListHead();
            void    readMdiSettings();
            void    writeMdiSettings();
            void    connectMdiWidgets( bool isConnect );
private slots:;
            void    appendRecentlyList();
            void    appendSubWindowListTail();
            void    appendPainListHead();
            void    onSubWindowActivated( QMdiSubWindow * subWindow );
private:
            int     m_dockBarVersion;
            bool    m_isInitialize;
      QSignalMapper m_windowMapper;
      QSignalMapper m_recentlyMapper;
AniLaToolBarManager m_toolBarManager;
        QStringList m_recentlyPath;
   QList<QAction *> m_actionForSubWindow;
   QList<QAction *> m_actionForSubWindows;
         QMdiArea * m_mdiArea;
    QMdiSubWindow * m_mdiSubWindow;
QHash<QAction*,bool>m_afterActionEnable;
QHash<QWidget*,bool>m_afterWidgetEnable;
};

inline void AniLaMdi::initinstance()
{
    if (m_isInitialize) return;
    readMdiSettings();
    onInitInstance();
    show();
    connectMdiWidgets( true );
    m_isInitialize = true;
}

inline void AniLaMdi::exitinstance()
{
    if ( !m_isInitialize ) return;
    connectMdiWidgets( false );
    hide();
    onExitInstance();
    writeMdiSettings();
    m_isInitialize = false;
}

inline void AniLaMdi::aboutQt()
{
    qApp->aboutQt();
}

inline void AniLaMdi::styleWinClassic()
{
    QApplication::setStyle( new QWindowsStyle );
}

inline void AniLaMdi::styleWinXP()
{
    QApplication::setStyle( new QWindowsXPStyle );
    QSysInfo::WinVersion ver = QSysInfo::windowsVersion();
    if ( ver != QSysInfo::WV_XP ){
        QMessageBox::warning( this,
            tr( "Warning" ),
            tr( "This style is only available on the Windows XP platform \n"
                "because it makes use of Windows XP's style engine." ) );
    }
}

inline void AniLaMdi::styleWinVista()
{
    QApplication::setStyle( new QWindowsVistaStyle );
    QSysInfo::WinVersion ver = QSysInfo::windowsVersion();
    if ( ver != QSysInfo::WV_VISTA && ver != QSysInfo::WV_WINDOWS7 ){
        QMessageBox::warning( this,
            tr( "Warning" ),
            tr( "This style is only available on the Windows Vista platform \n"
                "because it makes use of Windows Vista's style engine." ) );
    }
}

inline void AniLaMdi::styleWinDotNet()
{
    QApplication::setStyle( new QtDotNetStyle(QtDotNetStyle::System) );
}

inline void AniLaMdi::styleWinOfficeBlue()
{
    QApplication::setStyle( new QtDotNetStyle(QtDotNetStyle::Blue) );
}

inline void AniLaMdi::styleWinOfficeSilver()
{
    QApplication::setStyle( new QtDotNetStyle(QtDotNetStyle::Silver) );
}

inline void AniLaMdi::styleWinOfficeOlive()
{
    QApplication::setStyle( new QtDotNetStyle(QtDotNetStyle::Olive) );
}

inline void AniLaMdi::styleMotif()
{
    QApplication::setStyle( new QMotifStyle );
}

inline void AniLaMdi::styleCDE()
{
    QApplication::setStyle( new QCDEStyle );
}

inline void AniLaMdi::stylePlastique()
{
    QApplication::setStyle( new QPlastiqueStyle );
}

inline void AniLaMdi::styleCleanlooks()
{
    QApplication::setStyle( new QCleanlooksStyle );
}

inline bool AniLaMdi::connection( bool isConnect,
                                  const QObject * sender,
                                  const char * signal,
                                  const QObject * receiver,
                                  const char * method,
                                  const Qt::ConnectionType & type )
{
    if ( isConnect ) return connect( sender, signal, receiver, method, type );
    return disconnect( sender, signal, receiver, method );
}

inline bool AniLaMdi::setConnection( bool isConnect,
                                     QAction * sender,
                                     const char * signal,
                                     const QObject * receiver,
                                     const char * method )
{
    Q_ASSERT( sender );
    bool ret = connection( isConnect, sender, signal, receiver, method );
    m_afterActionEnable.insert( sender, (isConnect && ret ) );
    return ret;
}

inline bool AniLaMdi::connectToolBarManager( bool isConnect,
                                             QAction * sender,
                                             const char * signal,
                                             const char * method )
{
    return setConnection( isConnect, sender, signal, &m_toolBarManager, method );
}

inline bool AniLaMdi::connectChanged( bool isConnect,
                                      QAction * sender,
                                      const char * method )
{
    return setConnection( isConnect, sender, SIGNAL(changed()), this, method );
}

inline bool AniLaMdi::connectHovered( bool isConnect,
                                      QAction * sender,
                                      const char * method )
{
    return setConnection( isConnect, sender, SIGNAL(hovered()), this, method );
}

inline bool AniLaMdi::connectToggled( bool isConnect,
                                      QAction * sender,
                                      const char * method )
{
    return setConnection( isConnect, sender, SIGNAL(toggled(bool)), this, method );
}

inline bool AniLaMdi::connectTriggered( bool isConnect,
                                        QAction * sender,
                                        const char * method )
{
    return setConnection( isConnect, sender, SIGNAL(triggered(bool)), this, method );
}

inline bool AniLaMdi::connectTriggeredMdiArea( bool isConnect,
                                               QAction * sender,
                                               const char * method )
{
    if ( !m_mdiArea ) return false;
    return setConnection( isConnect, sender, SIGNAL(triggered(bool)), m_mdiArea, method );
}

inline void AniLaMdi::addToolBarManager( QAction * action )
{
    m_toolBarManager.addToolBarManager( action );
}

inline void AniLaMdi::removeToolBarManager( QAction * action )
{
    m_toolBarManager.removeToolBarManager( action );
}

inline void AniLaMdi::setActiveSubWindow(QWidget * widget)
{
    setActiveSubWindow( qobject_cast<QMdiSubWindow *>(widget) );
}

inline void AniLaMdi::setActiveSubWindow(QMdiSubWindow * subWindow)
{
    if ( m_mdiArea && subWindow )
        m_mdiArea->setActiveSubWindow( subWindow );
}

inline void AniLaMdi::onConnectMainWindow( bool isConnect )
{
#ifndef QT_NO_DEBUG
    QString str = QString( "%1( %2 )" )
        .arg("connectMainWindow")
        .arg((isConnect)?"true":"false");
    qDebug( str.toAscii() );
#endif //QT_NO_DEBUG
}

inline void AniLaMdi::onConnectSubWindow( bool isConnect,
                                          QMdiSubWindow * subWindow )
{
#ifndef QT_NO_DEBUG
    QString str = QString( "%1( %2, %3 ) [ %4 ]" )
        .arg("connectSubWindow")
        .arg((isConnect)?"true":"false")
        .arg((qlonglong)subWindow)
        .arg(subWindow->windowTitle());
    qDebug( str.toAscii() );
#endif //QT_NO_DEBUG
}

inline void AniLaMdi::onSubWindowAllClosed()
{
#ifndef QT_NO_DEBUG
    qDebug( "onSubWindowAllClosed()" );
#endif //QT_NO_DEBUG
}

inline void AniLaMdi::connectRecentlyList( bool isConnect,
                                           QMenu * menu )
{
    Q_ASSERT( menu );
    m_afterWidgetEnable.insert( menu,
        connection( isConnect, menu, SIGNAL(aboutToShow()),
                    this, SLOT(appendRecentlyList()) ) );
}

inline void AniLaMdi::connectSubWindowListTail( bool isConnect,
                                                QMenu * menu )
{
    Q_ASSERT( menu );
    m_afterWidgetEnable.insert( menu,
        connection( isConnect, menu, SIGNAL(aboutToShow()),
                    this, SLOT(appendSubWindowListTail()) ) );
}

inline void AniLaMdi::connectPainListHead( bool isConnect,
                                           QMenu * menu )
{
    Q_ASSERT( menu );
    m_afterWidgetEnable.insert( menu,
        connection( isConnect, menu, SIGNAL(aboutToShow()),
                    this, SLOT(appendPainListHead()) ) );
}

inline void AniLaMdi::connectForSubWindow( bool isConnect,
                                           QAction * action )
{
    Q_ASSERT( action );
    connection( isConnect, this, SIGNAL(existSubWindow(bool)),
                action, SLOT(setEnabled(bool)) );
    if ( m_mdiArea ){
        QList<QMdiSubWindow *> list = m_mdiArea->subWindowList();
        emit existSubWindow( list.count() > 0 );
    } else  emit existSubWindow( false );
}

inline void AniLaMdi::connectForSubWindows( bool isConnect,
                                            QAction * action )
{
    Q_ASSERT( action );
    connection( isConnect, this, SIGNAL(existSubWindows(bool)),
                action, SLOT(setEnabled(bool)) );
    if ( m_mdiArea ){
        QList<QMdiSubWindow *> list = m_mdiArea->subWindowList();
        emit existSubWindows( list.count() > 1 );
    } else emit existSubWindow( false );
}

inline QMenu * AniLaMdi::senderMenu()
{
    if ( !m_mdiArea || !sender() ) return 0;
    return qobject_cast<QMenu *>( sender() );
}

inline void AniLaMdi::clearRecentlyPath()
{
    m_recentlyPath.clear();
}

#endif // ANILAMDI_H

AniLaMdi.cpp cppファイル

#include "stdafx.h"
#include "anilamdi.h"

AniLaMdi::AniLaMdi( int dockBarVersion )
  : QMainWindow(),
    m_windowMapper( this ),
    m_toolBarManager( this ),
    m_mdiArea( 0 ),
    m_mdiSubWindow( 0 ),
    m_dockBarVersion( dockBarVersion ),
    m_isInitialize( false )
{
}

void AniLaMdi::closeEvent( QCloseEvent * event )
{
    if ( !m_mdiArea ) return;
    m_mdiArea->closeAllSubWindows();
    QList<QMdiSubWindow*> list = m_mdiArea->subWindowList();
    if ( list.isEmpty() ) event->accept();
    else event->ignore();
}

void AniLaMdi::dragEnterEvent( QDragEnterEvent * event )
{
    if ( event->mimeData()->hasFormat( "text/uri-list" ) ){
        QList<QUrl> urlList = event->mimeData()->urls();
        bool isImage = false;
        foreach ( QUrl url, urlList ){
            QString path = url.toLocalFile();
            if ( !path.isEmpty() && !QImageReader::imageFormat( path ).isEmpty() )
                    isImage = true;
        }
        if ( isImage ) event->acceptProposedAction();
    }
}

void AniLaMdi::dropEvent( QDropEvent * event )
{
    QList<QUrl> urlList = event->mimeData()->urls();
    if ( urlList.isEmpty() ) return;
    bool isError = false;
    foreach ( QUrl url, urlList ){
        if ( isError && QMessageBox::question(
            this, tr("Question ?"),
            tr( "Do you continue reading of a file?" ),
            QMessageBox::No | QMessageBox::Yes, QMessageBox::No ) ==
            QMessageBox::No ) return;
        QString path = url.toLocalFile();
        if ( !path.isEmpty() && !QImageReader::imageFormat( path ).isEmpty() )
            isError = !fileOpen( path );
    }
}

void AniLaMdi::onSubWindowActivated( QMdiSubWindow * subWindow )
{
    if ( !m_mdiArea ) return;
    // No subWindow or Non-Activate MainWindow
    QList<QMdiSubWindow *> list = m_mdiArea->subWindowList();
    if ( subWindow ){
        // Change activate subWindow
        if ( m_mdiSubWindow != subWindow ){
            // disconnect old subWindow
            if ( m_mdiSubWindow )
                onConnectSubWindow( false, m_mdiSubWindow );
            // connect new subWindow
            onConnectSubWindow( true, subWindow );
            m_mdiSubWindow = subWindow;
        }
    } else {
        if ( list.isEmpty() ){
            if ( m_mdiSubWindow ){
                // disconnect old subWindow
                onConnectSubWindow( false, m_mdiSubWindow );
                m_mdiSubWindow = subWindow;
                onSubWindowAllClosed();
            }
        }
    }
    updateSubWindowTitles();
    emit existSubWindow( list.count() > 0 );
    emit existSubWindows( list.count() > 1 );
}

void AniLaMdi::updateSubWindowTitles()
{
    if ( !m_mdiArea ) return;
    QList<QMdiSubWindow*> list = m_mdiArea->subWindowList();
    // Add a number to the view of the same file.
    int count = list.count();
    QMap<QString, QList<QMdiSubWindow*>> pathToSubWindow;
    if ( count <= 0 ) return;
    for ( int i=0; i < count; i++ ){
        QString path = windowFilePath( list[i] );
        if ( !pathToSubWindow.contains( path ) ){
            QList<QMdiSubWindow*> subList;
            subList.append( list[i] );
            for ( int j=i+1; j < count; j++ )
                if ( path == windowFilePath( list[j] ) )
                    subList.append( list[j] );
            pathToSubWindow.insert( path, subList );
        }
    }
    for ( QMap<QString, QList<QMdiSubWindow*>>::iterator
        it = pathToSubWindow.begin(), end = pathToSubWindow.end();
        it != end; ++it ){
        int num = 0;
        QList<QMdiSubWindow*> subList = it.value();
        if ( subList.count() == 1 )
            subList[0]->setWindowTitle( QFileInfo( it.key() ).fileName()
                + QString(" [*]") + subTitle( subList[0] ) );
        else {
            foreach ( QMdiSubWindow * sub, subList ){
                sub->setWindowTitle( QFileInfo( it.key() ).fileName()
                    + QString(":%1 [*]").arg(++num) + subTitle( sub ) );
            }
        }
    }
}

void AniLaMdi::readMdiSettings()
{
    QSettings settings;
    // read main window settings
    restoreGeometry( settings.value( KEY_GEOMETRY ).toByteArray() );
    restoreState( settings.value( KEY_DOCKSTATE ).toByteArray(),
                  m_dockBarVersion );
    m_recentlyPath = settings.value( KEY_RECENTLYPATH ).toStringList();
    // read application settings
    onReadSettings( settings );
    // read tool bar manager settings
    m_toolBarManager.initialization();
    m_toolBarManager.restoreState( settings.value(KEY_TOOLBARSTATE).toByteArray(),
                                   m_dockBarVersion );
}

void AniLaMdi::writeMdiSettings()
{
    QSettings settings;
    // write main window settings
    settings.setValue( KEY_RECENTLYPATH, m_recentlyPath );
    settings.setValue( KEY_GEOMETRY, saveGeometry() );
    settings.setValue( KEY_DOCKSTATE, saveState(m_dockBarVersion) );
    // write application settings
    onWriteSettings( settings );
    // write tool bar manager settings
    settings.setValue( KEY_TOOLBARSTATE,
                       m_toolBarManager.saveState(m_dockBarVersion) );
}

void AniLaMdi::connectMdiWidgets( bool isConnect )
{
    // Connect central widget
    QWidget * widget = centralWidget();
    if ( widget ){
        foreach ( QObject * object, widget->children() ){
            QMdiArea * mdiArea = qobject_cast<QMdiArea*>( object );
            if ( mdiArea ){
                m_mdiArea = mdiArea;
                connection( isConnect,
                    m_mdiArea,  SIGNAL(subWindowActivated(QMdiSubWindow*)),
                    this,       SLOT(onSubWindowActivated(QMdiSubWindow*)) );
                setAcceptDrops( true );
            }
        }
    }
    if ( !isConnect ) m_mdiArea = 0;
    // Connect recently mappaer
    connection( isConnect,
        &m_recentlyMapper,      SIGNAL(mapped(QString)),
        this,                   SLOT(fileOpen(QString)) );
    // Connect window mapper
    connection( isConnect,
        &m_windowMapper,        SIGNAL(mapped(QWidget *)),
        this,                   SLOT(setActiveSubWindow(QWidget *)) );
    // Connect subclass(MainWindow) widget
    onConnectMainWindow( isConnect );
    // after action enbale
    for ( QHash<QAction*,bool>::iterator
        it = m_afterActionEnable.begin(), end = m_afterActionEnable.end();
        it != end; ++it ) it.key()->setEnabled( *it );
    m_afterActionEnable.clear();
    // after widget enable
    for ( QHash<QWidget*,bool>::iterator
        it = m_afterWidgetEnable.begin(), end = m_afterWidgetEnable.end();
        it != end; ++it ) it.key()->setEnabled( *it );
    m_afterWidgetEnable.clear();
}

void AniLaMdi::addRecentlyPath( const QString & path )
{
    QStringList list;
    list.append( path );
    int count = 1;
    foreach ( QString file, m_recentlyPath ){
        if ( file != path && QFileInfo( file ).isFile() ){
            list.append( file );
            count ++;
            if ( count == 20 ) break;
        }
    }
    m_recentlyPath.clear();
    m_recentlyPath.append( list );
}

QString AniLaMdi::windowFilePath( QMdiSubWindow * subWindow )
{
    if ( !subWindow ) return QString();
    QString path = subWindow->windowFilePath();
    if ( !path.isEmpty() ) return path;
    QWidget * widget = subWindow->widget();
    if ( !widget ) return QString();
    return widget->windowFilePath();
}

QMdiSubWindow * AniLaMdi::findSubWindow( const QString & path,
                                         const QMdiArea::WindowOrder & order,
                                         bool isReverse )
{
    if ( !m_mdiArea || path.isEmpty() ) return 0;
    QList<QMdiSubWindow*> list = m_mdiArea->subWindowList( order );
    int count = list.count();
    int i = 0, add = 1;
    if ( isReverse ){
        i = count - 1;
        add = -1;
    }
    for ( ; 0 <= i && i < count ; i += add ){
        QMdiSubWindow * subWindow = list[i];
        if ( windowFilePath( subWindow ) == path ) return subWindow;
    }
    return 0;
}

void AniLaMdi::appendRecentlyList()
{
    removeRecentlyList();
    QMenu * menu = senderMenu();
    if ( !menu ) return;
    if ( m_recentlyPath.isEmpty() ){
        QAction * action =
            menu->addAction( tr("There is not the recent file.") );
        action->setEnabled( false );
        return;
    }
    int j=0;
    foreach ( QString path, m_recentlyPath ){
        QString title = path;
        if ( j<9 ) title += QString( " (&%1)" ).arg( ++j );
        QAction * action = menu->addAction( title );
        connect( action,            SIGNAL(triggered()),
                 &m_recentlyMapper, SLOT(map()) );
        m_recentlyMapper.setMapping( action, path );
    }
    menu->addSeparator();
    QAction * action =
        menu->addAction( tr("Clear the list of the recent file.") );
    connect( action,        SIGNAL(triggered()),
             this,          SLOT(clearRecentlyPath()) );
}

void AniLaMdi::removeRecentlyList()
{
    QMenu * menu = senderMenu();
    if ( !menu ) return;
    bool isRecentlyList = false;
    QList<QAction*> list = menu->actions();
    foreach ( QAction * action, list ){
        if ( !isRecentlyList ){
            if ( !action->isSeparator() ){
                m_recentlyMapper.removeMappings( action );
                disconnect( action,             SIGNAL(triggered()),
                            &m_recentlyMapper,  SLOT(map()) );
            } else isRecentlyList = true;
            menu->removeAction( action );
        } else disconnect( action,  SIGNAL(triggered()),
                           this,    SLOT(clearRecentlyPath()) );
    }
    menu->clear();
}

#define FLAG_WLT "$$window_list_tail$$"

void AniLaMdi::appendSubWindowListTail()
{
    removeSubWindowListTail();
    QMenu * menu = senderMenu();
    if ( !menu ) return;
    QList<QMdiSubWindow*> windowList =
        m_mdiArea->subWindowList( QMdiArea::StackingOrder );
    if ( windowList.count() < 2 ) return;
    QMdiSubWindow * activeWindow = m_mdiArea->activeSubWindow();
    bool hasSeparator = ( menu->actions().count() > 0 );
    QAction * flagAct = menu->addAction( FLAG_WLT );
    flagAct->setVisible( false );
    if ( hasSeparator ) menu->addSeparator();
    for ( int i=windowList.count()-1, j=1; i>=0; i--, j++ ){
        QString title;
        if ( windowList[i]->isWindowModified() )
             title = windowList[i]->windowTitle().remove('[').remove(']');
        else title = windowList[i]->windowTitle().remove("[*]");
        if ( j < 10 ) title += QString(" (&%1)").arg( j );
        QAction * action = menu->addAction( title );
        action->setCheckable( true );
        action->setChecked( windowList[i]==activeWindow );
        connect( action,            SIGNAL(triggered()),
                 &m_windowMapper,   SLOT(map()) );
        m_windowMapper.setMapping( action, (QWidget*)windowList[i] );
    }
}

void AniLaMdi::removeSubWindowListTail()
{
    QMenu * menu = senderMenu();
    if ( !menu ) return;
    bool isWindowList = false;
    QString flagText = FLAG_WLT;
    foreach ( QAction * action, menu->actions() ){
        if ( isWindowList ){
            if ( !action->isSeparator() ){
                m_windowMapper.removeMappings( action );
                disconnect( action,             SIGNAL(triggered()),
                            &m_windowMapper,    SLOT(map()) );
            }
            menu->removeAction( action );
        } else {
            if ( action->text() == flagText ){
                menu->removeAction( action );
                isWindowList = true;
            }
        }
    }
}

#define FLAG_PLH_START  "$$pain_list_head_start$$"
#define FLAG_PLH_END    "$$pain_list_head_end$$"

void AniLaMdi::appendPainListHead()
{
    removePainListHead();
    QMenu * menu = senderMenu();
    if ( !menu ) return;
    QMenu * viewPainMenu = createPopupMenu();
    if ( !viewPainMenu ) return;
    QList<QAction*> pains = viewPainMenu->actions();
    if ( !pains.isEmpty() ){
        QList<QAction*> list = menu->actions();
        QAction * before = list.isEmpty() ? 0 : list[0];
        QAction * act = new QAction( FLAG_PLH_START, menu );
        act->setVisible( false );
        menu->insertAction( before, act );
        foreach ( QAction * action, pains ){
            if ( action->isSeparator() )
                menu->insertSeparator( before );
            else menu->insertAction( before, action );
        }
        if ( before ) menu->insertSeparator( before );
        act = new QAction( FLAG_PLH_END, menu );
        act->setVisible( false );
        menu->insertAction( before, act );
    }
    delete viewPainMenu;
}

void AniLaMdi::removePainListHead()
{
    QMenu * menu = senderMenu();
    if ( !menu ) return;
    QList<QAction*> list = menu->actions();
    QString flagStart( FLAG_PLH_START );
    QString flagEnd( FLAG_PLH_END );
    bool isRemove = false;
    foreach( QAction * action, list ){
        QString text = action->text();
        if ( text == flagStart ) isRemove = true;
        if ( isRemove ) menu->removeAction( action );
        if ( text == flagEnd ) break;
    }
}

これらのソースは、まだ作りかけのソフトの一部分なので欠陥なども色々とあると思います。参考程度にとどめてください。
また、より分かりやすい解説などを目的にブログ等で活用されたら嬉しいです。
なお、ここに載っていないファイルは以下のzipにあります。

anilamdi-bata01.zip (パブリックドメイン・ライセンス)

※上記のファイルには一切try catch節が一切ありません。まだ書いていないだけですが…。

このままだと例外セーフじゃないので、その辺のところは自己責任でよろしくです。

このソースには、Qtソリューションの中からつぎの2つのプロジェクトで作られたdllを外部リンクで使用しています。
若干改造して使用していますので、次のコードもダウンロードしてみてください。

qttoolbardialog-2.2-opensource-kaizou01.zip (LGPL/GPL)

qtdotnetstyle-2.3-opensource-kaizou01.zip (LGPL/GPL)

この記事の続きは何時になるかわかりませんが、いずれ、MyWidgetに当たる部分の雛形も作って公開してみたいと思います。

それでは、ごきげんよう(^_^)/~