https://wiki.trinitydesktop.org/index.php?title=KMDI_Tutorial&feed=atom&action=historyKMDI Tutorial - Revision history2024-03-29T13:19:22ZRevision history for this page on the wikiMediaWiki 1.35.13https://wiki.trinitydesktop.org/index.php?title=KMDI_Tutorial&diff=1705&oldid=prevBlu256: +KDE32021-08-22T21:46:09Z<p>+KDE3</p>
<table class="diff diff-contentalign-left diff-editfont-monospace" data-mw="interface">
<col class="diff-marker" />
<col class="diff-content" />
<col class="diff-marker" />
<col class="diff-content" />
<tr class="diff-title" lang="en">
<td colspan="2" style="background-color: #fff; color: #202122; text-align: center;">← Older revision</td>
<td colspan="2" style="background-color: #fff; color: #202122; text-align: center;">Revision as of 21:46, 22 August 2021</td>
</tr><tr>
<td colspan="2" class="diff-lineno">Line 1:</td>
<td colspan="2" class="diff-lineno">Line 1:</td>
</tr>
<tr>
<td colspan="2" class="diff-empty"> </td>
<td class="diff-marker"><a class="mw-diff-movedpara-right" title="Paragraph was moved. Click to jump to old location." href="#movedpara_2_1_lhs">⚫</a></td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><a name="movedpara_0_0_rhs"></a>[[Category:Developers]]</div></td>
</tr>
<tr>
<td colspan="2" class="diff-empty"> </td>
<td class="diff-marker"><a class="mw-diff-movedpara-right" title="Paragraph was moved. Click to jump to old location." href="#movedpara_2_2_lhs">⚫</a></td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><a name="movedpara_0_1_rhs"></a>[[Category:KDE3]]</div></td>
</tr>
<tr>
<td colspan="2" class="diff-empty"> </td>
<td class="diff-marker"><a class="mw-diff-movedpara-right" title="Paragraph was moved. Click to jump to old location." href="#movedpara_2_3_lhs">⚫</a></td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><a name="movedpara_0_2_rhs"></a>[[Category:Tutorials]]</div></td>
</tr>
<tr>
<td colspan="2" class="diff-empty"> </td>
<td class="diff-marker">+</td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div>{{KDE3}}</div></td>
</tr>
<tr>
<td colspan="2" class="diff-empty"> </td>
<td class="diff-marker">+</td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"></td>
</tr>
<tr>
<td class="diff-marker"> </td>
<td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div>==Authors and thanks==</div></td>
<td class="diff-marker"> </td>
<td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div>==Authors and thanks==</div></td>
</tr>
<tr>
<td class="diff-marker"> </td>
<td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"></td>
<td class="diff-marker"> </td>
<td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"></td>
</tr>
<tr>
<td colspan="2" class="diff-lineno">Line 679:</td>
<td colspan="2" class="diff-lineno">Line 684:</td>
</tr>
<tr>
<td class="diff-marker"> </td>
<td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"></td>
<td class="diff-marker"> </td>
<td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"></td>
</tr>
<tr>
<td class="diff-marker"> </td>
<td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div>NOTE:This page started as a copy of the KMDI tutorial available here: [http://preview3.vivid-planet.com/3.0/doc/kmditutorial/ http://preview3.vivid-planet.com/3.0/doc/kmditutorial/]</div></td>
<td class="diff-marker"> </td>
<td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div>NOTE:This page started as a copy of the KMDI tutorial available here: [http://preview3.vivid-planet.com/3.0/doc/kmditutorial/ http://preview3.vivid-planet.com/3.0/doc/kmditutorial/]</div></td>
</tr>
<tr>
<td class="diff-marker">−</td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;"></td>
<td colspan="2" class="diff-empty"> </td>
</tr>
<tr>
<td class="diff-marker"><a class="mw-diff-movedpara-left" title="Paragraph was moved. Click to jump to new location." href="#movedpara_0_0_rhs">⚫</a></td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;"><div><a name="movedpara_2_1_lhs"></a>[[Category:Developers]]</div></td>
<td colspan="2" class="diff-empty"> </td>
</tr>
<tr>
<td class="diff-marker"><a class="mw-diff-movedpara-left" title="Paragraph was moved. Click to jump to new location." href="#movedpara_0_1_rhs">⚫</a></td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;"><div><a name="movedpara_2_2_lhs"></a>[[Category:KDE3]]</div></td>
<td colspan="2" class="diff-empty"> </td>
</tr>
<tr>
<td class="diff-marker"><a class="mw-diff-movedpara-left" title="Paragraph was moved. Click to jump to new location." href="#movedpara_0_2_rhs">⚫</a></td>
<td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;"><div><a name="movedpara_2_3_lhs"></a>[[Category:Tutorials]]</div></td>
<td colspan="2" class="diff-empty"> </td>
</tr>
</table>Blu256https://wiki.trinitydesktop.org/index.php?title=KMDI_Tutorial&diff=363&oldid=previmported>Eliddell: Created page with "==Authors and thanks== *Andrea Bergia - andreabergia2 yahoo.it - Author *Raphael Langerhorst - raphael-langerhorst gmx.at - Thanks for the spellcheck *Winfried Dobbe - winfri..."2014-06-16T21:30:05Z<p>Created page with "==Authors and thanks== *Andrea Bergia - andreabergia2 yahoo.it - Author *Raphael Langerhorst - raphael-langerhorst gmx.at - Thanks for the spellcheck *Winfried Dobbe - winfri..."</p>
<p><b>New page</b></p><div>==Authors and thanks==<br />
<br />
*Andrea Bergia - andreabergia2 yahoo.it - Author<br />
*Raphael Langerhorst - raphael-langerhorst gmx.at - Thanks for the spellcheck<br />
*Winfried Dobbe - winfried_mb2 zonnet.nl - Corrected some errors<br />
*Stefan Vunckx - bonkie skynet.be - Find out how to add multi tool boxes<br />
*Hiroshi Sasago - sasago a.phys.nagoya-u.ac.jp - Fixed a segmentation fault<br />
*Thanks to all the KDE developers!<br />
<br />
==Version==<br />
<br />
25 January 2004 - First version relased<br />
26 January 2004 - Spellchecked version; added the introduction<br />
28 February 2004 - Added the multiple tool boxes section<br />
<br />
==Introduction==<br />
<br />
KMDI is a new KDE library, which is available only from KDE 3.2. It allows you to create in an easy way complex MDI layout, like the one you can see in KDevelop. It's really simple to use, but no one had written a tutorial when I needed. So, I wrote this one. Hope you'll like it.<br />
<br />
==Let's start==<br />
<br />
Open KDevelop. Click on the Project menu, and then on the New project option. Select C++ application, KDE and then Simple KDE Application. Follow the wizard, and you will end up with a simple but working KDE application. I'll assume you called it KMDITest.<br />
<br />
Ok, now let's make ourselves a bit easier the life. First: open the KDevelop configuration dialog and set it up as you like. To open it, go on the Settings menu and select Set-up KDevelop.<br />
<br />
Next thing to do: set up the project. Open the Project menu, and select Project options item. On the documentation tab, disable everything you don't need for this project, like the Gnome things. This will make life a lot easier when you need the help.<br />
<br />
[[File:DocumentationTab.jpg|An image of how I've set up things in the documentation tab of the project options]]<br />
<br />
Ok, now let's enable one of the nicer things of KDevelop: the code completion. Open the C++ specific tab, and then the Code completion sub-tab. Click on the button labeled Add Persistant Class Store, follow the wizard and add the QT repository and the KDE one. It's trivial to follow them, and after you've done it you will really love KDevelop a lot more :-D.<br />
<br />
Play with things in the other tab for a while, if you wants, then accept your changes, and if asked to run the Automake & friends, say no. We have to do one things before this: we need to add the library KMdi to our program. Look at the left part of the KDevelop's main window (if you use the default layout). You should see two buttons: one is for the Documentation, and the other is for the Automake managment. Click on it, and on the bottom part right - click on the kmditest item, which should be in bold, and select the Options... menu item. This will bring up a dialog. Click on the libraries tab, and then on the Add button on the bottom, near the list of libraries outside the project. Add the library -lkmdi.<br />
<br />
[[File:TargLibsWithKMDI.jpg|The target libraries after adding the KMDI library.]]<br />
<br />
Ok, now it's time to build the project. Go on the Build menu, and select the item Run automake & friends. When you're done, go again on the Build menu, and select the item Run configure. After that, you're ready to build your project and test it. The default key-binding for this is F8. Of course, there's a menu item in the Build menu. It should build without errors. If all goes fine, let's try our program, by pressing CTRL+F9 (in the default configuration).<br />
<br />
[[File:KMDIApplication.jpg|An image of the application started.]]<br />
<br />
==Where's the MDI?==<br />
<br />
As you've probably noticed, the previous part didn't do anything on the KMDI side, but linking our program with the library. Now, we'll start to use this nice library.<br />
<br />
Open the file which contains the declaration of your class, which in my example is kmditest.h. Replace the line<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
#include <kmainwindow.h><br />
</syntaxhighlight><br />
<br />
with the following:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
#include <kmdimainfrm.h><br />
</syntaxhilight><br />
<br />
Of course, also the base class of KMDITest should be replaced, from KMainWindow?, into KMdiMainFrm?. In the kmditest.cpp file, change the call to the base constructor from this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
KMDITest::KMDITest()<br />
: KMainWindow( 0, "KMDITest" )<br />
</syntaxhighlight><br />
<br />
to this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
KMDITest::KMDITest()<br />
: KMdiMainFrm( 0, "KMDITest_MainWindow", KMdi::IDEAlMode )<br />
</syntaxhilight><br />
<br />
Done: your application uses KMDI. Now, let's add something nice to the main window.<br />
<br />
Remove all the code from ther KMDITest constructor except the setXMLFile call, and replace it with this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
setXMLFile("kmditestui.rc");<br />
<br />
// Add a sample child view<br />
KMdiChildView *view1 = new KMdiChildView( i18n( "View 1" ), this );<br />
( new QHBoxLayout( view1 ) )->setAutoAdd( true );<br />
( new QLabel( view1 ) )->setPixmap( BarIcon( "konsole" ) );<br />
new QLabel( i18n( "Test of KMDI: view 1" ), view1 );<br />
addWindow( view1 );<br />
<br />
// Add another sample child view<br />
KMdiChildView *view2 = new KMdiChildView( i18n( "View 2" ), this );<br />
( new QHBoxLayout( view2 ) )->setAutoAdd( true );<br />
( new QLabel( view2 ) )->setPixmap( BarIcon( "quanta" ) );<br />
new QLabel( i18n( "Test of KMDI: view 2" ), view2 );<br />
addWindow( view2 );<br />
</syntaxhilight><br />
<br />
Of course, be sure that you have the following file included:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
#include <qlabel.h><br />
#include <kmdichildview.h><br />
#include <klocale.h><br />
#include <qlayout.h><br />
#include <kiconloader.h><br />
</syntaxhilight><br />
<br />
Now give a try to the program. This is my version of the kmditest.h and kmditest.cpp files, if you need them.<br />
<br />
[[File:KMDIApplication2.jpg|An image of the application with KMDI enabled.]]<br />
<br />
==Creating new childs on request==<br />
<br />
Ok, now let's add something to our program. In the constructor of the class KMDITest, remove all the code and replace it with this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
setXMLFile("kmditestui.rc");<br />
<br />
KStdAction::openNew( this, SLOT( openNewWindow() ), actionCollection() );<br />
KStdAction::quit( kapp, SLOT( quit() ), actionCollection() );<br />
<br />
createGUI( 0 );<br />
</syntaxhighlight><br />
<br />
So simple right? Now, let's implement the slot openNewWindow. Declare it into the header file, and write this code as implementation:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::openNewWindow()<br />
{<br />
// Add a child view<br />
m_childs ++;<br />
KMdiChildView *view = new KMdiChildView( i18n( "View %1" ).arg( m_childs ), this );<br />
( new QHBoxLayout( view ) )->setAutoAdd( true );<br />
new QLabel( i18n( "Test of KMDI: view %1" ).arg( m_childs ), view );<br />
addWindow( view );<br />
}<br />
</syntaxhighlight><br />
<br />
As you see, every time you click on the New button, you'll add a child to your view. To make the whole thing build, add a declaration of m_childs as an unsigned to the header, and intialize it to zero in the constructor, and update the include list. Here you can find the code: kmditest.h and kmditest.cpp<br />
<br />
[[File:MDIWithChildren.jpg|An image of the application with some childs opened]]<br />
<br />
==Deleting childs==<br />
<br />
In the previous paragraph, we added a button which allowed the user to add new windows. Now, it's time to see how to close windows. We will also learn how to manage the selection of a child widget. Let's start!<br />
<br />
At first we will show the caption of the selected view in the status bar. To do so, add these two lines in the constructor code:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
// When we change view, change the status bar text<br />
connect( this, SIGNAL( viewActivated( KMdiChildView * ) ),<br />
this, SLOT( currentChanged( KMdiChildView * ) ) );<br />
<br />
// Create the status bar<br />
statusBar()->message( i18n( "No view!" ) );<br />
<syntaxhighlight><br />
<br />
Of course, declare the slot currentChanged into the header and add this implementation:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::currentChanged( KMdiChildView *current )<br />
{<br />
// We can access the current window via current, or via the protected<br />
// member we inerithed m_pCurrentWindow<br />
<br />
statusBar()->message( i18n( "%1 activated" ).arg( current->tabCaption() ) );<br />
}<br />
</syntaxhighlight><br />
<br />
This will show on the status bar the current view's caption.<br />
<br />
Now, let's allow the user to close a view. Add this line in the constructor between the KStdAction?::openNew and KStdAction?::quit calls:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
KStdAction::close( this, SLOT( closeCurrent() ), actionCollection() );<br />
</syntaxhighlight><br />
<br />
As always, declare the slot, and write this code for it:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::closeCurrent()<br />
{<br />
// If there's a current view, close it<br />
if ( m_pCurrentWindow != 0 ) {<br />
// Notify the removeal of the window into the status bar<br />
statusBar()->message(<br />
i18n( "%1 removed" ).arg( m_pCurrentWindow->tabCaption() ) );<br />
<br />
// We could also call removeWindowFromMdi, but it doesn't delete the<br />
// pointer. This way, we're sure that the view will get deleted.<br />
closeWindow( m_pCurrentWindow );<br />
}<br />
}<br />
</syntaxhighlight><br />
<br />
There is a bug: when you have created only one window, the m_pCurrentWindow is set to zero. But this is a bug in the KMDI library.<br />
<br />
Here there are the up-to-date version of the sources: kmditest.h and kmditest.cpp<br />
<br />
[[File:MDIWithMixedChildren.jpg|An image of the application with some childs opened and some closed]]<br />
<br />
==Adding a tool window==<br />
<br />
In this part we'll learn how to add a tool window. These are widgets that embed themselves on the left, right, top or bottom of your window, and place a button to show / hide them. As an example, we'll add a tool window with the list of our windows.<br />
<br />
===Keeping a window list===<br />
<br />
At first, we need some way to store the windows. So add, at the bottom of the header file, a private variable of type QValueList< KMdiChildView * >, and call it, for example, m_window. Then modify the slot openNewWindow to this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::openNewWindow()<br />
{<br />
// Add a child view<br />
m_childs ++;<br />
KMdiChildView *view = new KMdiChildView(<br />
i18n( "View %1" ).arg( m_childs ), this );<br />
( new QHBoxLayout( view ) )->setAutoAdd( true );<br />
new QLabel( i18n( "Test of KMDI: view %1" ).arg( m_childs ), view );<br />
<br />
// Add the item to the window list<br />
m_window.append( view );<br />
<br />
// Add to the MDI and set as current<br />
addWindow( view );<br />
currentChanged( view );<br />
}<br />
</syntaxhighlight><br />
<br />
Then modify the slot closeCurrent to this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::closeCurrent()<br />
{<br />
// If there's a current view, close it<br />
if ( m_pCurrentWindow != 0 ) {<br />
// Notify the removeal of the window into the status bar<br />
statusBar()->message(<br />
i18n( "%1 removed" ).arg( m_pCurrentWindow->tabCaption() ) );<br />
<br />
// Remove from the window list<br />
m_window.remove( m_window.find( m_pCurrentWindow ) );<br />
<br />
// We could also call removeWindowFromMdi, but it doesn't delete the<br />
// pointer. This way, we're sure that the view will get deleted.<br />
closeWindow( m_pCurrentWindow );<br />
}<br />
}<br />
</syntaxhighlight><br />
<br />
This way we will keep an up-to-date list of window opened in m_window. Arrange the include list and be sure that the code builds.<br />
<br />
===Adding the tool box===<br />
<br />
Now let's add the tool window. This will be a panel, placed on the left, with a KListBox?. Of course, this list box will be synchronized with the m_window content.<br />
<br />
Simply add these lines at the bottom of the constructor of KMDITest:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
// Create the list of the opened windows<br />
m_listBox = new KListBox( this );<br />
m_listBox->setCaption( i18n( "Opened windows" ) );<br />
addToolWindow( m_listBox, KDockWidget::DockLeft, getMainDockWidget() );<br />
</syntaxhighlight><br />
<br />
Of course, declare m_listBox as a protected KListBox? in the header file. Here are the sources: kmditest.h and kmditest.cpp<br />
<br />
[[File:OpenToolBox.jpg|An image of the application with the tool box opened]]<br />
<br />
===Synchronize the list box with the window list===<br />
<br />
Ok, now it's time to show something useful in the list box, like the window list. To do this, we'll modify again the slot openNewWindow. This is the new version:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::openNewWindow()<br />
{<br />
// Add a child view<br />
m_childs ++;<br />
KMdiChildView *view = new KMdiChildView(<br />
i18n( "View %1" ).arg( m_childs ), this );<br />
( new QHBoxLayout( view ) )->setAutoAdd( true );<br />
new QLabel( i18n( "Test of KMDI: view %1" ).arg( m_childs ), view );<br />
<br />
// Add the item to the window list<br />
m_window.append( view );<br />
m_listBox->insertItem( view->tabCaption() );<br />
<br />
// Add to the MDI and set as current<br />
addWindow( view );<br />
currentChanged( view );<br />
}<br />
</syntaxhighlight><br />
<br />
We need to modify the closeCurrent slot too. Replace its code with this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::closeCurrent()<br />
{<br />
// If there's a current view, close it<br />
if ( m_pCurrentWindow != 0 ) {<br />
// Notify the removeal of the window into the status bar<br />
statusBar()->message(<br />
i18n( "%1 removed" ).arg( m_pCurrentWindow->tabCaption() ) );<br />
<br />
// Remove from the window list<br />
m_window.remove( m_window.find( m_pCurrentWindow ) );<br />
<br />
// Remove from the list box<br />
QListBoxItem *item = m_listBox->findItem( m_pCurrentWindow->tabCaption() );<br />
assert( item );<br />
delete item;<br />
<br />
// We could also call removeWindowFromMdi, but it doesn't delete the<br />
// pointer. This way, we're sure that the view will get deleted.<br />
closeWindow( m_pCurrentWindow );<br />
}<br />
<br />
// Synchronize combo box<br />
if ( m_pCurrentWindow ) {<br />
currentChanged( m_pCurrentWindow );<br />
}<br />
}<br />
</syntaxhighlight><br />
<br />
Ok, now we have to synchronize the current tab with the list box. So, replace currentChanged's code with this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::currentChanged( KMdiChildView *current )<br />
{<br />
// Update status bar and list box<br />
statusBar()->message( i18n( "%1 activated" ).arg( current->tabCaption() ) );<br />
<br />
QListBoxItem *item = m_listBox->findItem( current->tabCaption() );<br />
assert( item );<br />
m_listBox->setCurrentItem( item );<br />
}<br />
</syntaxhighlight><br />
<br />
This way when the user changes current tab, we will get the list box updated. And now we need to change the current tab when the user selects an item of the list box. So add this line at the bottom of the constructor:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
connect( m_listBox, SIGNAL( executed( QListBoxItem * ) ), this,<br />
SLOT( listBoxExecuted( QListBoxItem * ) ) );<br />
</syntaxhighlight><br />
<br />
Declare the slot listBoxExecuted in the header file, and write this code as the following:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::listBoxExecuted( QListBoxItem *item )<br />
{<br />
// Get the current item's text<br />
QString text = item->text();<br />
<br />
// Active the corresponding tab<br />
for ( QValueList< KMdiChildView *>::iterator it = m_window.begin();<br />
it != m_window.end(); ++it ) {<br />
// Get the view<br />
KMdiChildView *view = *it;<br />
assert( view );<br />
<br />
// Is the view we need to show?<br />
if ( view->caption() == text ) {<br />
view->activate();<br />
break;<br />
}<br />
}<br />
}<br />
</syntaxhighlight><br />
<br />
Arrange things to make the program build, and play a bit with it. Don't you think it's really nice? big grin Here are the sources: kmditest.h and kmditest.cpp<br />
<br />
[[File:ToolBoxWorking.jpg|An image of the application with the tool box working]]<br />
<br />
==Other layout==<br />
<br />
So far, we only allowed the user to have the MDI mode called IDEAl. Let's allow him/her to choose every kind of layout he/she likes.<br />
<br />
In the constructor, just before the call to createGUI, add these lines:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
// Allow the user to change the MDI mode<br />
KToggleAction *tl = new KRadioAction( i18n( "Top level mode" ), KShortcut(),<br />
this, SLOT( switchToToplevelMode() ), actionCollection(), "toplevel" );<br />
tl->setExclusiveGroup( "MDIMode" );<br />
KToggleAction *cf = new KRadioAction( i18n( "Child frame mode" ), KShortcut(),<br />
this, SLOT( switchToChildframeMode() ), actionCollection(), "childframe" );<br />
cf->setExclusiveGroup( "MDIMode" );<br />
KToggleAction *tp = new KRadioAction( i18n( "Tab page mode" ), KShortcut(),<br />
this, SLOT( switchToTabPageMode() ), actionCollection(), "tabpage" );<br />
tp->setExclusiveGroup( "MDIMode" );<br />
KToggleAction *id = new KRadioAction( i18n( "IDEAl mode" ), KShortcut(),<br />
this, SLOT( switchToIDEAlMode() ), actionCollection(), "ideal" );<br />
id->setExclusiveGroup( "MDIMode" );<br />
id->setChecked( true );<br />
</syntaxhighlight><br />
<br />
Replace the kmditestui.rc file with the following:<br />
<br />
<syntaxhighlight lang="xml"><br />
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"><br />
<kpartgui name="kmditest" version="1"><br />
<MenuBar><br />
<Menu name="MDIMode"><text>MDI Mode</text><br />
<Action name="toplevel" /><br />
<Action name="childframe" /><br />
<Action name="tabpage" /><br />
<Action name="ideal" /><br />
</Menu><br />
</MenuBar><br />
</kpartgui><br />
</syntaxhighlight><br />
<br />
If you haven't yet done it, install your application, and play a bit with it. As you can see, we've got some problems to solve.<br />
<br />
===Removal of childs===<br />
<br />
You can see that when you switch to child mode you have some buttons to close the child windows. We'll need to handle this, and to remove the child view from the window list. So let's add this to the end of openNewWindow:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
// Handle the request of killing<br />
connect( view, SIGNAL( childWindowCloseRequest( KMdiChildView * ) ),<br />
this, SLOT( childClosed( KMdiChildView * ) ) );<br />
</syntaxhighlight><br />
<br />
This way, we get the slot childClosed called every time a child gets closed. This is the slot's source:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::childClosed( KMdiChildView * w )<br />
{<br />
assert( w );<br />
<br />
// Set as active<br />
w->activate();<br />
assert( w == m_pCurrentWindow );<br />
<br />
// Notify the removeal of the window into the status bar<br />
statusBar()->message(<br />
i18n( "%1 removed" ).arg( w->tabCaption() ) );<br />
<br />
// Remove from the window list<br />
m_window.remove( m_window.find( w ) );<br />
<br />
// Remove from the list box<br />
QListBoxItem *item = m_listBox->findItem( w->tabCaption() );<br />
assert( item );<br />
delete item;<br />
<br />
// Remove the view from MDI, BUT NOT DELETE IT! It gets automatically<br />
// deleted by QT, since it was closed.<br />
removeWindowFromMdi( w );<br />
}<br />
</syntaxhighlight><br />
<br />
As you can see, childs get nicely removed from the list box.<br />
<br />
===The Top Level creation problem===<br />
<br />
Try to create at least one child window, and then switch to Top Level mode. Then, create a new child window. Notice that the new label gets embedded into the main frame. What a bad thing... Let's fix it! Replace the openNewWindow's code with this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
void KMDITest::openNewWindow()<br />
{<br />
// Add a child view<br />
m_childs ++;<br />
<br />
// The child view will be our child only if we aren't in Toplevel mode<br />
KMdiChildView *view = new KMdiChildView(<br />
i18n( "View %1" ).arg( m_childs ),<br />
( mdiMode() == KMdi::ToplevelMode ) ? 0 : this );<br />
( new QHBoxLayout( view ) )->setAutoAdd( true );<br />
new QLabel( i18n( "Test of KMDI: view %1" ).arg( m_childs ), view );<br />
<br />
// Add the item to the window list<br />
m_window.append( view );<br />
m_listBox->insertItem( view->tabCaption() );<br />
<br />
// Add to the MDI and set as current<br />
if ( mdiMode() == KMdi::ToplevelMode ) {<br />
addWindow( view, KMdi::Detach );<br />
} else {<br />
addWindow( view );<br />
}<br />
currentChanged( view );<br />
<br />
// Handle the request of killing<br />
connect( view, SIGNAL( childWindowCloseRequest( KMdiChildView * ) ),<br />
this, SLOT( childClosed( KMdiChildView * ) ) );<br />
}<br />
</syntaxhighlight><br />
<br />
We simply create the view with us as parent if we aren't in Toplevel mode, and as a top level widget (parent == 0) if we are in the Toplevel mode. Also, we add the window with the Detach flag if we are in Toplevel mode. Quite easy, I think.<br />
<br />
Here's the up-to-date version of the sources: kmditest.h and kmditest.cpp<br />
<br />
[[File:MDITopLevelMode.jpg|An image of the application in top-level mode]]<br />
<br />
[[File:MDIChildFrameMode.jpg|An image of the application in child frame mode]]<br />
<br />
[[File:MDITabPageMode.jpg|An image of the application in tab page mode]]<br />
<br />
===Saving user preferences===<br />
<br />
This is really simple. First, get the current mode saved. We will override the virtual function KMainWindow?::queryClose. Let's write this code:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
bool KMDITest::queryClose()<br />
{<br />
// Save current MDI mode<br />
KConfig *c = kapp->config();<br />
<br />
// Put in value a matching string value<br />
QString value;<br />
switch ( mdiMode() ) {<br />
case KMdi::ToplevelMode:<br />
value = "toplevel";<br />
break;<br />
<br />
case KMdi::ChildframeMode:<br />
value = "childframe";<br />
break;<br />
<br />
case KMdi::TabPageMode:<br />
value = "tabpage";<br />
break;<br />
<br />
case KMdi::IDEAlMode:<br />
value = "ideal";<br />
break;<br />
<br />
case KMdi::UndefinedMode:<br />
value = "ideal";<br />
break;<br />
}<br />
<br />
// Write it!<br />
c->writeEntry( "MDIMode", value );<br />
c->sync();<br />
<br />
// Allow to close the window<br />
return true;<br />
}<br />
</syntaxhighlight><br />
<br />
Then we need to restore the mode. I think that putting it into the main function simplifies things, so let's replace your main with this:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
int main(int argc, char **argv)<br />
{<br />
// Default start-up routine<br />
KAboutData about("kmditest", I18N_NOOP("KMDITest"), version, description,<br />
KAboutData::License_GPL, "(C) 2004 Andrea Bergia", 0, 0, "andreabergia2@yahoo.it");<br />
about.addAuthor( "Andrea Bergia", 0, "andreabergia2@yahoo.it" );<br />
KCmdLineArgs::init(argc, argv, &about);<br />
KApplication app;<br />
<br />
// Read MDI mode<br />
KConfig *c = app.config();<br />
QString strMode = c->readEntry( "MDIMode", "ideal" );<br />
KMdi::MdiMode mode = KMdi::IDEAlMode;<br />
if ( strMode == "toplevel" ) {<br />
mode = KMdi::ToplevelMode;<br />
} else if ( strMode == "childframe" ) {<br />
mode = KMdi::ChildframeMode;<br />
} else if ( strMode == "tabpage" ) {<br />
mode = KMdi::TabPageMode;<br />
}<br />
<br />
// Create main window<br />
KMDITest *mainWin = new KMDITest( mode );<br />
app.setMainWidget( mainWin );<br />
mainWin->show();<br />
<br />
// mainWin has WDestructiveClose flag by default, so it will delete itself.<br />
return app.exec();<br />
}<br />
</syntaxhighlight><br />
<br />
We need to change something in the constructor too. This is the final version:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
KMDITest::KMDITest( KMdi::MdiMode mode )<br />
: KMdiMainFrm( 0, "KMDITest", mode ), m_childs( 0 )<br />
{<br />
setXMLFile("kmditestui.rc");<br />
<br />
// Create some actions<br />
KStdAction::openNew( this, SLOT( openNewWindow() ), actionCollection() );<br />
KStdAction::close( this, SLOT( closeCurrent() ), actionCollection() );<br />
KStdAction::quit( this, SLOT( close() ), actionCollection() );<br />
<br />
// Allow the user to change the MDI mode<br />
KToggleAction *tl = new KRadioAction( i18n( "Top level mode" ), KShortcut(),<br />
this, SLOT( switchToToplevelMode() ), actionCollection(), "toplevel" );<br />
tl->setExclusiveGroup( "MDIMode" );<br />
tl->setChecked( mode == KMdi::ToplevelMode );<br />
KToggleAction *cf = new KRadioAction( i18n( "Child frame mode" ), KShortcut(),<br />
this, SLOT( switchToChildframeMode() ), actionCollection(), "childframe" );<br />
cf->setExclusiveGroup( "MDIMode" );<br />
cf->setChecked( mode == KMdi::ChildframeMode );<br />
KToggleAction *tp = new KRadioAction( i18n( "Tab page mode" ), KShortcut(),<br />
this, SLOT( switchToTabPageMode() ), actionCollection(), "tabpage" );<br />
tp->setExclusiveGroup( "MDIMode" );<br />
tp->setChecked( mode == KMdi::TabPageMode );<br />
KToggleAction *id = new KRadioAction( i18n( "IDEAl mode" ), KShortcut(),<br />
this, SLOT( switchToIDEAlMode() ), actionCollection(), "ideal" );<br />
id->setExclusiveGroup( "MDIMode" );<br />
id->setChecked( mode == KMdi::IDEAlMode );<br />
<br />
createGUI( 0 );<br />
<br />
// When we change view, change the status bar text<br />
connect( this, SIGNAL( viewActivated( KMdiChildView * ) ),<br />
this, SLOT( currentChanged( KMdiChildView * ) ) );<br />
<br />
// Create the status bar<br />
statusBar()->message( i18n( "No view!" ) );<br />
<br />
// Create the list of the opened windows<br />
m_listBox = new KListBox( this );<br />
m_listBox->setCaption( i18n( "Opened windows" ) );<br />
addToolWindow( m_listBox, KDockWidget::DockLeft, getMainDockWidget() );<br />
<br />
connect( m_listBox, SIGNAL( executed( QListBoxItem * ) ), this,<br />
SLOT( listBoxExecuted( QListBoxItem * ) ) );<br />
}<br />
</syntaxhighlight><br />
<br />
As you can see, I changed the receiver of the action quit to the slot close of the main window. Otherwise, we would never get queryClose called, if it was connected to KApplication::quit. The sources are here: main.cpp, kmditest.h and kmditest.cpp<br />
<br />
===Problems with IDEAl===<br />
<br />
Try this: when the program start, before creating child views, switch to Toplevel mode and then to IDEAl. The program crashes. Why? I think this is a bug in KMDI. In fact, if you see, KDevelop doesn't allows you to switch from and to IDEAl without a restart. But this is a test program, so we can live with this problem. If you want, write some slots which check the mode you came from and the mode you're going to, and allow the change only if it doesn't come from / go to IDEAl.<br />
<br />
==Multiple tool boxes==<br />
<br />
Now, let's suppose you want to add another tool view to your main window. To do this, you'll probably simple write something like that in the constructor:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
// Create the list of the opened windows<br />
m_listBox = new KListBox( this );<br />
m_listBox->setCaption( i18n( "Opened windows" ) );<br />
addToolWindow( m_listBox, KDockWidget::DockLeft, getMainDockWidget() );<br />
<br />
// Create another tool box<br />
QLabel *label = new QLabel( i18n( "Yet to implement, sorry!" ), this );<br />
label->setCaption( i18n( "Help" ) );<br />
addToolWindow( label, KDockWidget::DockLeft, getMainDockWidget() );<br />
</syntaxhighlight><br />
<br />
But this won't work! If you're asking why, I had a hard time finding it out. It's a simple problem, anyway. Replace the lines above with the following:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
// Create the list of the opened windows<br />
m_listBox = new KListBox( this, "opened_windows" );<br />
m_listBox->setCaption( i18n( "Opened windows" ) );<br />
addToolWindow( m_listBox, KDockWidget::DockLeft, getMainDockWidget() );<br />
<br />
// Create another tool box<br />
QLabel *label = new QLabel( i18n( "Yet to implement, sorry!" ), this, "help" );<br />
label->setCaption( i18n( "Help" ) );<br />
addToolWindow( label, KDockWidget::DockLeft, getMainDockWidget() );<br />
</syntaxhighlight><br />
<br />
We added a name (the last parameter to the constructor) to the widget. Of course, all the names have to be different. Really simple, right?<br />
<br />
==Conclusion==<br />
<br />
Well, I think we're almost finished. I can't think of any other things you'll need to know before programming with KMdi. So, happy coding, and hope to see your application soon :-D<br />
<br />
Anyway, if you have any comment, question, suggestion or whatever, feel free to contact me. My email address is andreabergia2 yahoo.it.<br />
<br />
==Bug-fix==<br />
<br />
As Hiroshi Sasago signaled to me, my code contains an error. Sometimes you can get a Segmentation fault when you close the main window. Rewrite the destructor as this to fix the error:<br />
<br />
<syntaxhighlight lang="cpp-qt"><br />
KMDITest::~KMDITest()<br />
{<br />
while( m_pCurrentWindow ) {<br />
closeCurrent();<br />
}<br />
}<br />
</syntaxhighlight><br />
<br />
Thanks to Hiroshi!!<br />
<br />
NOTE:This page started as a copy of the KMDI tutorial available here: [http://preview3.vivid-planet.com/3.0/doc/kmditutorial/ http://preview3.vivid-planet.com/3.0/doc/kmditutorial/]<br />
<br />
[[Category:Developers]]<br />
[[Category:KDE3]]<br />
[[Category:Tutorials]]</div>imported>Eliddell