12.8. Embedding More Than One Part in the Same Window

The previous example showed how to embed a part as the single widget of a window. KParts also makes it possible to embed more than one part in the same window, and it handles the activation of a part when the user clicks it (or uses Tab to give it the focus). This is the task of the PartManager.

To display more than one part in a window, the solution is usually to use a splitter, or even nested splitters, such as in Konqueror. KOffice has another way of embedding several parts—by using frames for the child parts—but it still uses PartManager.

Now modify the example to make it display, in addition to the PostScript document, the PostScript code for it. To display the text, the application uses the Notepad part in read-only mode. The two widgets will be hosted by a splitter.

Displaying raw PostScript is not very useful, but this example could, for instance, be turned into an application showing the LaTeX source and the PostScript result side by side.

ghostviewtest.h needs to be modified slightly to add the following private members:


   1 
   2 KParts::ReadOnlyPart *m_notepadpart;
   3 KParts::PartManager *m_manager;
   4 QSplitter *m_splitter;
   5 

ghostviewtest.cpp needs to be more modified. To include the PartManager definition, use the following:


   1 
   2 #include <kparts/partmanager.h>
   3 

In the constructor, create the part manager and connect its main signal, activePartChanged, to your createGUI slot. This means you don't need to call createGUI directly; it is called every time the active part changes.


   1 
   2 m_manager = new KParts::PartManager( this );
   3 // When the manager says the active part changes,
   4 // the builder updates (recreates) the GUI
   5 connect( m_manager, SIGNAL( activePartChanged( KParts::Part * ) ),
   6          this, SLOT( createGUI( KParts::Part * ) ) );
   7 

Then create the splitter and transform the setView statement into the following:


   1 
   2 m_splitter = new QSplitter( this );
   3 setView( m_splitter );
   4 

so that the main widget is now the splitter. Both parts need to be created with the splitter as a parent (instead of the window):


   1 
   2 KLibFactory *factory = KLibLoader::self()->factory( "libkghostview" );
   3 if (factory)
   4 {
   5   m_gvpart = (KParts::ReadOnlyPart *)factory->create( m_splitter,
   6               "kgvpart", "KParts::ReadOnlyPart" );
   7 }
   8 else
   9    kdFatal() << "No libkghostview found !" << endl;
  10 
  11 factory = KLibLoader::self()->factory( "libnotepad" );
  12 if (factory)
  13   m_notepadpart = (KParts::ReadOnlyPart *)factory->create( m_splitter,
  14                    "knotepadpart", "KParts::ReadOnlyPart" );
  15 else
  16    kdFatal() << "No libnotepad found !" << endl;
  17 

After the parts are created, they should be added to the part manager. At the same time, you can specify which one should initially be active:


   1 
   2 m_manager->addPart( m_gvpart, true ); // sets as the active part
   3 m_manager->addPart( m_notepadpart, false );
   4 

Then the splitter can be set to a minimum size, as shown:


   1 
   2 m_splitter->setMinimumSize( 400, 300 );
   3 m_splitter->show();
   4 

Finally, add the following line to openURL() to open the same URL in both parts:


   1 
   2 m_notepadpart->openURL( url );
   3 

As you can see, the main idea is that the mainwindow creates a main widget (here, the splitter), creates all parts inside it, and registers the part to a part manager. Try clicking one part and then the other; each time the active part changes, the GUI is updated (both menus and toolbars) to show the GUI of the active part.

Note also the change in the window caption. This is handled by the Part class, which receives the GUIActivateEvent from the mainwindow when the part is activated or deactivated. To set a different caption for a part, you need to emit setWindowCaption both in openFile() and in guiActivateEvent().