mdiapplication.h

Go to the documentation of this file.
00001 /*
00002  *  mdiapplication.cpp
00003  *  Nodal
00004  *
00005  *  Imported into Nodal by Aidan Lane on Thu Feb 24 2005.
00006  *  Copyright (c) 2005 CEMA, Monash University. All rights reserved.
00007  *
00008  *  Original file:
00009  *
00010  *    mdiapplication.h
00011  *    EverGreen
00012  *
00013  *    Created by Aidan Lane on Mon Jul 19 2004.
00014  *    Copyright (c) 2004 Aidan Lane. All rights reserved.
00015  *
00016  *  This program is free software; you can redistribute it and/or modify
00017  *  it under the terms of the GNU General Public License as published by
00018  *  the Free Software Foundation; either version 2 of the License, or
00019  *  (at your option) any later version.
00020  *
00021  *  This program is distributed in the hope that it will be useful,
00022  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00023  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024  *  GNU General Public License for more details.
00025  *
00026  *  You should have received a copy of the GNU General Public License
00027  *  along with this program; if not, write to the Free Software
00028  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00029  */
00030 
00031 /*
00032  * Multi-Document-Interface Manager - manage "root-less" windows
00033  * This is similar at an abstact level to a widget layout manager:
00034  * - We layout QWidget MDI "children" (as "root-less" windows - e.g. UNIX or MacOS style)
00035  * - We layout QWidget "tool windows" and connect them to the active/focused MDI children
00036  *
00037  * An MDIApplication object instance does NOT show itself - TODO: revise this
00038  *
00039  * Note: MDIApplication only shown when there are no MDIChild objects that it is managing
00040  */
00041  
00042 /* From the old MDIParent system:
00043  * I wrote this so that MDI could be supported by Qt on Mac OS X properly.
00044  * MDIChild objects are NOT children of MDIParent in the eyes of Qt, as they
00045  * need to be "root-less" work properly (instead we maintain track of it here).
00046  * Using addToolwindow and removeToolwindow, you can set which tool windows the
00047  * MDIChild objects are to "own" when they are focused.
00048  * Note: these tool windows can be ANY widget - but ideally of type QDialog.
00049  */
00050 
00051 
00052 #ifndef MDIAPPLICATION_H
00053 #define MDIAPPLICATION_H
00054 
00055 
00056 #include <QApplication>
00057 #include <QDesktopWidget>
00058 #include <QStyle>
00059 
00060 
00061 // TODO: make the following settable members of MDIApplication
00062 #define CASCADE_BASE_X 50
00063 #define CASCADE_BASE_Y 00
00064 
00065 
00066 class MDIChildBase {
00067  public:
00068   MDIChildBase() {}
00069   virtual ~MDIChildBase() {}
00070   virtual QWidget* widgetPtr() const = 0;
00071 };
00072 
00073 
00074 
00077 class MDIApplication : public QApplication {
00078 
00079  public:
00080   MDIApplication(int& argc, char** argv )
00081     : QApplication(argc, argv) { m_activeChildWindow = 0; }
00082   virtual ~MDIApplication() {}
00083 
00084   virtual QWidget* topLevel() const { return 0; } // i.e. no parent, but could be used on Windows to provide a common workspace - FIXME: implement this system!
00085   QList<MDIChildBase*> childWindows() const { return m_childWindows; } // QList is implicitly shared, so we don't need to return a "const ref"
00086   MDIChildBase* activeChildWindow() const { return m_activeChildWindow; }
00087 
00110   virtual bool childWindowCloseRequest( MDIChildBase* child )
00111   {
00112     Q_ASSERT( child != 0 );
00113     // WARNING!!! don't call close() on the child -> we would loop infinitely!
00114     // WARNING!!! MDIChild's destructor will call our removeChildWindow() to remove itself
00115     delete child;
00116     return true;
00117   }
00118         
00119         
00120   // TODO: implement better window positioning - CURRENTLY CASCADING
00121   //
00122   // Asserts that the child is non-null.
00123   virtual void addChildWindow( MDIChildBase* child )
00124   { 
00125     Q_ASSERT( child != 0 );
00126                 
00127     m_childWindows.append( child );
00128 
00129     QWidget* w = child->widgetPtr();
00130 
00131     Q_ASSERT( QApplication::desktop() != 0 );
00132     QRect availableGeometry = QApplication::desktop()->availableGeometry();
00133                 
00134     // TODO: implement better window positioning - CURRENTLY CASCADING
00135     if ( m_childWindows.count() == 1 )
00136       {
00137         w->move( availableGeometry.left() + CASCADE_BASE_X,
00138                  availableGeometry.top() + CASCADE_BASE_Y );
00139       }
00140     else // note: we assume that m_childWindows.count() > 1 (we append one just above and it !=1)
00141       {
00142         QPoint secLast = (m_childWindows[m_childWindows.count()-2]->widgetPtr())->pos(); // and get the SECOND LAST element
00143         Q_ASSERT( QApplication::style() != 0 );
00144         int cascadeOffset = QApplication::style()->pixelMetric( QStyle::PM_TitleBarHeight, 0, w );
00145         if ( cascadeOffset <= 0 ) cascadeOffset = 22; // TODO: remove the need for this?
00146         int tryX = secLast.x() + cascadeOffset;
00147         int tryY = secLast.y() + cascadeOffset;
00148 
00149         if ( (tryX + w->width()) <= availableGeometry.right()
00150              && (tryY + w->height()) <= availableGeometry.bottom() )
00151           w->move( tryX, tryY );
00152         else
00153           w->move( availableGeometry.left() + CASCADE_BASE_X,
00154                    availableGeometry.top() + CASCADE_BASE_Y );
00155       }
00156   }
00157 
00158 
00159   // TODO: UPDATE DOC!!!
00160   // If no child windows are left, then make ourself the active window so that
00161   // the active menu bar is ours.
00162   // If "w" == 0, the we return without doing anything
00163   // Calls setActiveChild(0) if no children are left.
00164   // Note that if 1+ still exist, then one will automatically become active, automatically calling setActiveWindow for us.
00165   //
00166   // Asserts that the child is non-null.
00167   virtual void removeChildWindow( MDIChildBase* child )
00168   {
00169     Q_ASSERT( child != 0 );
00170     m_childWindows.removeAll( child );
00171     if ( m_childWindows.isEmpty() ) setActiveChild(0);
00172   }
00173 
00174 
00175   // TODO: update my doc!
00176   // This "reparents" the child windows to "belong" to the MDI child
00177   // Won't reparent the tool window if the "new" MDI child is already its parent
00178   // Warning: this should only be called by the QWidget object themselves - don't touch!
00179   // Returns true iff OUR child was CHNAGED
00180   // If the child is not registed with us or the child is already the active one, then this will return w/o doing anything.
00181   // It IS valid to pass 0 in as the child, the the case where no child is active
00182   virtual void setActiveChild( MDIChildBase* child )
00183   {
00184     m_activeChildWindow = child; // ALWAYS updates this!
00185   }
00186 
00187 
00188  private:
00189   QList<MDIChildBase*> m_childWindows;
00190   MDIChildBase* m_activeChildWindow;
00191 };
00192 
00193 
00194 
00195 // WARNING!  WARNING!  WARNING! :
00196 // - We can't use Qt's signals'n'slots to communicate with the manager, as both of
00197 //   these are templates!
00198 // - We need to be able to speak to the manager, but because it is a template, we
00199 //   need to be told what the manager's definite class is (e.g. MDIApplication<QObejct,QWidget>).
00200 //
00201 // TODO: rewrite the following!
00202 /*
00203  * Convenience class
00204  *
00205  * Looks after the adding, removal and activation to/from the MDIApplication
00206  * Designed to be used with existing classes - such as those created with Qt Designer
00207  *
00208  * T should be a QWidget or a derivative of it.
00209  *
00210  * managerT should beMDIApplication or a derivative of it.
00211  *
00212  * Note: the DESTRUCTOR calls removeChildWindow(), NOT close()
00213  */
00214 template <class T, class managerT>
00215   class MDIChild : public T, public MDIChildBase { // note: T is a QObject, so it must be first
00216 
00217  public:
00227     MDIChild( managerT* manager, QWidget* parent = 0 )
00228       : T(parent), m_manager(manager)
00229       {
00230         Q_ASSERT( m_manager != 0 );
00231         m_manager->addChildWindow( this );
00232       }
00233 
00235       // Absolutely must be virtual, see MDIApplication::childWindowCloseRequest().
00236       virtual ~MDIChild()
00237         {
00238           Q_ASSERT( m_manager != 0 );
00239           m_manager->removeChildWindow( this );
00240         }
00241 
00243       virtual QWidget* widgetPtr() const
00244         {
00245           return (QWidget*)( this ); // note: a dynamic_cast or static_cast won't work here
00246         }
00247 
00255       virtual bool event( QEvent* e )
00256       {
00257         Q_ASSERT( e != 0 );
00258         Q_ASSERT( m_manager != 0 );
00259 
00260         // FIXME: DOC:
00261         // Update eveything first, making sure that the window states are updated before be check them!
00262         bool ret = false;
00263 
00264         // TODO: optimise me!
00265         switch( e->type() )
00266           {
00267           case QEvent::Close: // TODO: doc that this is here!
00268             /* WARNING!!! We are _NOT_ to de-allocate our self!
00269              * The manager may need to ask the user something before closing the window
00270              * (e.g. "Save changes before closing?) or perform some other processing
00271              * before we are closed.
00272              */
00273             if ( m_manager->childWindowCloseRequest(this) )
00274               return true; // if closed -> nothing left to do! (and if we made any further calls, we would be in trouble!)
00275             else {
00276               e->ignore(); // VERY IMPORTANT! Ignore the close event!
00277               return true; // don't process the event any further
00278             }
00279 
00280           case QEvent::WindowActivate:
00281             m_manager->setActiveChild( this );
00282             break;
00283 
00284           case QEvent::WindowDeactivate:
00285 // TODO: re-doc me: - PUT IN A __BIG__ warning of never to do this!!!
00286 // only setActiveChild(0) when we remove the last child window OR if the only window we have has been minimized!
00287 //          if ( m_manager->activeChildWindow() == this ) m_manager->setActiveChild( 0 );
00288 //          ret = true;
00289             break;
00290 
00291             // TODO: the following should not be needed, as I consider it a bug in Qt that windowActivationChange is not called on minimize or restore...
00292             // REMOVE ME ONCE FIXED!
00293           case QEvent::WindowStateChange:
00294             //i.e. minimized, maximized, restored or made full-screen
00295             // WARNING! When I used windowState() != Qt::WindowMinimized it returned false-positives (mac-snapshot-20050318)
00296             if ( ! T::isMinimized() ) { // this is not the same as == maximized, as there are other states
00297               m_manager->setActiveChild( this );
00298             }
00299             else if ( m_manager->activeChildWindow() == this ) { // redundant: && isMinimized() ) {
00300               m_manager->setActiveChild( 0 );
00301             }
00302             ret = true;
00303             break;
00304 
00305           default:
00306             break;
00307           }
00308 
00309         // WARNING!!! Try the T's event (always give it a go, which may call close() or windowActivationChange()).
00310         //            If that fails, then and only then, default to the value of ret.
00311         return T::event(e) || ret;
00312       }
00313 
00314 
00315  private:
00316       managerT* m_manager;
00317   };
00318 
00319 
00320 #endif // ! MDIAPPLICATION_H

Generated on Mon Jul 30 09:46:50 2007 for Digest by  doxygen 1.5.2