Development/Create new dialog in Impress

From The Document Foundation Wiki
Jump to: navigation, search

This article is about how to integrate a new dialog into Impress UI. Integrating a newly created dialog window may be a pain for anyone who does it for the first time (as it was for me). So, here it is. (Important note: I assume some C++, Glade and make skills). Don't worry, I'll provide examples at the end of this tutorial (diff files), so you can see the changes I've made.

Creating the dialog

First, we need to create the dialog window. In my case, the basic class was ModalDialog window. Also, Glade files are also supported in LibreOffice, so we will use one. See also Development/WidgetLayout.

Glade file

You need the Glade (you can download it from here) editor to create new dialogs In LibreOffice. Glade files must be created with GTK+ version 3 and higher (eg. Glade 3.12.1). LibreOffice can import Glade files, their extension is .ui . Both of them is an XML file, so there is no conversion required: just rename the extension to .ui and it does the rest.

Dialog prototype

The base of out dialog is a GtkDialog, so select "Dialog Box" from Toplevels to create our basic dialog. Set the dialog's Name property to HelloDialog (this is important, we'll need this later). Next, put two GtkButton into the GtkBox automatically added by GtkDialog (you can modify this later if you want, but dialog windows must have a "Cancel" option). So, name them eg. ok_btn, and the other cancel_btn (Name property in the property window).

Initial dialog prototype
Final version of our dialog prototype

Next, rewrite their Label property to "OK" and "Cancel".

Then, add the content you want to add to the dialog. In my case, I'm going to add some simple elements (buttons). So, select Box from Containers, and insert it with 3 elements. Then, add three buttons into each element. Name one of them as btn_hello, and set it's label to "Say Hello" (others are not needed, they are to demonstrate Box container, so I'm not going to use the other two).

Adding our dialog to Impress

Now we're going to save the dialog where it belongs. As I wrote before, it must be saved with a .ui extension (not with the default .glade extension), and there are no other things to do with it.

Placing the dialog file

So, the dialog file must be placed inside Impress directory in : <your LO copy>/sd/uiconfig/simpress/ui/<filename.ui> (<filename.ui> means your file's name). Next, open file <your LO copy>/sd/UIConfig_simpress.mk (this file in OpenGrok: http://opengrok.libreoffice.org/xref/core/sd/UIConfig_simpress.mk) , this is the makefile that will include our dialog .ui file to the build. Next, search up for the other dialog file includes (something like this: "$(eval $(call gb_UIConfig_add_uifiles,modules/simpress"), and add the following line (inside the brackets): sd/uiconfig/simpress/ui/<dialog file name, without .ui> \ (eg. sd/uiconfig/simpress/ui/hellodialog \). Now, we have to write the corresponding C++ file for our dialog.

Coding the dialog

As I wrote previously, we're going to use the ModalDialog (reference: https://docs.libreoffice.org/vcl/html/classModalDialog.html) class to be our dialog's base class. So, create the header file (HelloDialog.hxx), and the source file (HelloDialog.cxx), and place the header file under sd/source/ui/dlg/HelloDialog.hxx and also the source file can go here. It is important to call the ModalDialog's constructor with the right arguments: window pointer, name of the dialog and the .ui file's path (modules/simpress/ui/hellodialog.ui in this case). The destructor can be left blank. This will be enough to create the dialog window. Interaction will be added later.

Inserting the dialog

Now, we want to integrate our newly created dialog class. We'll need to edit a couple of files for this:

  • sd/uiconfig/simpress/menubar/menubar.xml : Impress's menubar in .xml format. Here, you need to edit this file to add a new menu item for your dialog box. I'm going to add mine under Insert menu ant it will be the last (under File), separated with a line.
  • sd/sdi/drviewsh.sdi : View Shell Interface, there are some similar files here (like outlnvsh.sdi for Draw View Shell). In this case, out dialog will only open in "Normal" view mode. Here, we set property ExecMethod to FuTemporary (later, in file drviews6.cxx, in function FuTemp04, we'll create the dialog), and StateMethod to GetMenuState. Here, we use the slot name we defined in app.hrc (SID_HELLODIALOG in my case). If you want your dialog to appear in the other views, check on SID_PHOTOALBUM in OpenGrok, that can appear in every view. It is similar to this, but you need to modify the other view's source files (like outlnvs2.cxx, outlnvsh.sdi, ...).
  • sd/inc/app.hrc : here we need to register our dialog, I'll use this name: SID_HELLODIALOG. Like the others, you'll need to specify a number for it: look for an empty slot, and assign the empty slot to SID_HELLODIALOG.
  • sd/sdi/sdraw.sdi : drawing options for the dialog. This describes how to create, draw our dialog, and which options will belong to it (eg. where will it appear in toolbar customization, ...). It is enough to copy from another one (like SID_PHOTOALBUM), and customization can be made later.
  • sd/inc/sdcommands.h : here, you assign a command name to your dialog box. This will be used for creating it. I'll use the name CMD_SID_HELLODIALOG and it's value is ".uno:HelloDialog". This value is used in your dialog's constructor.
  • sd/source/ui/dlg/sddlgfact.hxx : this is the dialog factory, which will create your dialog later. You have to define the corresponding create function for it, and in sd/source/ui/dlg/sddlgfact.cxx implement it. I'll use the name SdCreateHelloDialog for the function name.
  • sd/source/ui/dlg/sddlgfact.cxx : the dialog's create function implementation goes here. It is simple: use the predefined SdVclAbstractDialog_Impl function and pass to it SdCreateHelloDialog with it's arguments.
  • sd/inc/sdabstdlg.hxx : here, you also define SdCreateHelloDialog function as virtual, and is is important to set it to 0.
  • sd/source/ui/view/drviews2.cxx : one of the ViewShell's source files, here we create our dialog window. Remember, we have set the execution method to FuTemporary, so place your code inside that function (FuTemporary is splitted in several cxx file, here it is called FuTemporary)
  • sd/Library_sdui.mk : Impress makefile, here we add our dialog's name: HelloDialog, just like the others.
  • sd/UIConfig_simpress.mk : Impress UI configuration makefile, same things as above.
  • officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu : this is the menu configuration file for Impress. Here, you define a new node, and add the text which you want to display. I'm going to add "Open Hello Dialog".

After, you've successfully made and saved your changes, don't forget to issue a make sd && make dev-install command. This will build Impress module (it is called "sd"), and make dev-install is needed because we've modified the UI. More on LibreOffice build system can be found here (you build modules with make <module_name> command).

HelloDialog's menu entry
Our dialog window in action

Implement the dialog's function

We have successfully created and inserted a new dialog window into Impress. Now, we're going to implement it to be interactive.

Dialog elements

When creating a dialog in LibreOffice, it's widgets are converted into LibreOffice's Vcl widgets. Each of the Gtk components has its corresponding Vcl widget. Here is a list of the most popular Vcl widgets: Development/WidgetLayout.

Modify the source file

Include the headers

Now, we'll modify our HelloDialog class. First, we include headers vcl/button.hxx for PushButton, OKButton and CancelButton, and header vcl/msgbox.hxx for InfoBox (this we need only in cxx file, so include it there).

Set up variables for widgets

Set up private variables for buttons pOk_btn, pCancel_btn, pHello_btn (note: widgets should be private variables, to make them inaccessible from the outside). These variables should be pointers.

Define and implement handlers

Next, in the constructor we need to assign the .ui file's components to our pointers. This is done with the get() function: get(pOk_btn, "ok_btn"); , here the first parameter is a pointer to the widget, and the second is a string, the widget's name in the .ui file (Name property, not Label). Now we just need to define handler functions to them. This can be done in the header file, with the DECL_LINK macro, eg. DECL_LINK(OkHdl, void*) declares a link to our Ok button (this also must be private). The links must be connected to their widget in the constructor: we'll connect OkHdl to pOk_btn's Click event, like this: pOk_btn->SetClickHdl(LINK(this, SdHelloDialog, OkHdl). When this is done, we just need to implement it in the cxx file. I use the macro IMP_LINK_NOARG for this (this can't pass arguments to the handler, but there is another one, which can): IMPL_LINK_NOARG(SdHelloDialog, OkHdl){ ... } (notice that I don't use the :: operator). I'm not going to implement it now(I'll only put a return 0; statement in it). For the CloseHdl, call Close() before the return statement (this is the way to close the dialog). In the HelloHdl, we're going to display an InfoBox with the classical "Hello, World!" text. So, create an InfoBox: InfoBox aInfo(this, OUString("Hello, World!")); and aInfo.Execute() will execute it.

Example

As I promised, here are the sources and the diff files, so you can see the concrete changes.

hellodialog.ui
It is available in zip format from File:Hellodialog.zip.

HelloDialog.hxx

 0 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 1 /*
 2 * This file is part of the LibreOffice project.
 3 *
 4 * This Source Code Form is subject to the terms of the Mozilla Public
 5 * License, v. 2.0. If a copy of the MPL was not distributed with this
 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 7 */
 8 
 9 #ifndef _SD_HELLODIALOG_HXX
10 #define _SD_HELLODIALOG_HXX
11 
12 #include "tools/link.hxx"
13 
14 #include <vcl/button.hxx>
15 #include <vcl/dialog.hxx>
16 
17 namespace sd
18 {
19 
20 class SdHelloDialog: public ModalDialog
21 {
22 
23 public:
24     SdHelloDialog(Window* pWindow);
25     ~SdHelloDialog();
26 
27     virtual short Execute();
28 
29 private:
30     OKButton* pOk_btn;
31     CancelButton* pCancel_btn;
32     PushButton* pHello_btn;
33 
34     DECL_LINK(OkHdl, void*);
35     DECL_LINK(CancelHdl, void*);
36     DECL_LINK(HelloHdl, void*);
37 };
38 
39 } // end of namespace sd
40 
41 #endif // _SD_HELLODIALOG_HXX
42 
43 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

HelloDialog.cxx

 0 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 1 /*
 2 * This file is part of the LibreOffice project.
 3 *
 4 * This Source Code Form is subject to the terms of the Mozilla Public
 5 * License, v. 2.0. If a copy of the MPL was not distributed with this
 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 7 */
 8 
 9 #include "HelloDialog.hxx"
10 #include "strings.hrc"
11 #include "sdresid.hxx"
12 
13 #include <vcl/msgbox.hxx>
14 
15 namespace sd
16 {
17 
18 SdHelloDialog::SdHelloDialog(Window* pWindow)
19 : ModalDialog(pWindow, "HelloDialog", "modules/simpress/ui/hellodialog.ui")
20 {
21     get(pOk_btn, "ok_btn");
22     get(pCancel_btn, "cancel_btn");
23     get(pHello_btn, "hello_btn");
24 
25     pOk_btn->SetClickHdl(LINK(this, SdHelloDialog, OkHdl));
26     pCancel_btn->SetClickHdl(LINK(this, SdHelloDialog, CancelHdl));
27     pHello_btn->SetClickHdl(LINK(this, SdHelloDialog, HelloHdl));
28 }
29 
30 SdHelloDialog::~SdHelloDialog()
31 {
32 }
33 
34 short SdHelloDialog::Execute()
35 {
36     return ModalDialog::Execute();
37 }
38 
39 IMPL_LINK_NOARG(SdHelloDialog, OkHdl)
40 {
41     return 0;
42 }
43 
44 IMPL_LINK_NOARG(SdHelloDialog, CancelHdl)
45 {
46     Close();
47     return 0;
48 }
49 
50 IMPL_LINK_NOARG(SdHelloDialog, HelloHdl)
51 {
52     InfoBox aInfo(this, OUString("Hello, World!"));
53     aInfo.Execute();
54     return 0;
55 }
56 
57 } // end of namespace sd
58 
59 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

app.hrc

 0 diff --git a/sd/inc/app.hrc b/sd/inc/app.hrc
 1 index d421a9b..4a7d454 100644
 2 --- a/sd/inc/app.hrc
 3 +++ b/sd/inc/app.hrc
 4 @@ -356,6 +356,7 @@
 5  #define SID_CUSTOM_ANIMATION_SCHEMES_PANEL  (SID_SD_START+333)
 6  #define SID_SLIDE_TRANSITIONS_PANEL         (SID_SD_START+334)
 7  // free
 8 +#define SID_HELLODIALOG                     (SID_SD_START+335)
 9  #define SID_PHOTOALBUM                      (SID_SD_START+336)
10  #define SID_REMOTE_DLG                      (SID_SD_START+337)
11  #define SID_CONNECTION_DLG                  (SID_SD_START+338)

sdraw.sdi

 0 diff --git a/sd/sdi/sdraw.sdi b/sd/sdi/sdraw.sdi
 1 index 83b4d04..f8ce2a5 100644
 2 --- a/sd/sdi/sdraw.sdi
 3 +++ b/sd/sdi/sdraw.sdi
 4 @@ -7251,3 +7251,27 @@ SfxVoidItem PhotoAlbumDialog SID_PHOTOALBUM
 5      ToolBoxConfig = TRUE,
 6      GroupId = GID_OPTIONS;
 7  ]
 8 +
 9 +SfxVoidItem HelloDialog SID_HELLODIALOG
10 +()
11 +[
12 +    /* flags: */
13 +    AutoUpdate = FALSE,
14 +    Cachable = Cachable,
15 +    FastCall = TRUE,
16 +    HasCoreId = FALSE,
17 +    HasDialog = TRUE,
18 +    ReadOnlyDoc = FALSE,
19 +    Toggle = FALSE,
20 +    Container = FALSE,
21 +    RecordAbsolute = FALSE,
22 +    RecordPerItem;
23 +    Synchron;
24 +
25 +    /* config: */
26 +    AccelConfig = FALSE,
27 +    MenuConfig = TRUE,
28 +    StatusBarConfig = FALSE,
29 +    ToolBoxConfig = TRUE,
30 +    GroupId = GID_OPTIONS;
31 +]

DrawImpressCommands.xcu

 0 diff --git a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
 1 index d135cd13..e1a7ba1 100644
 2 --- a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
 3 +++ b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
 4 @@ -61,6 +61,14 @@
 5            <value>1</value>
 6          </prop>
 7        </node>
 8 +     <node oor:name=".uno:HelloDialog" oor:op="replace">
 9 +        <prop oor:name="Label" oor:type="xs:string">
10 +          <value xml:lang="en-US">Open Hello Dialog</value>
11 +        </prop>
12 +        <prop oor:name="Properties" oor:type="xs:int">
13 +          <value>1</value>
14 +        </prop>
15 +      </node>
16        <node oor:name=".uno:Dia" oor:op="replace">
17          <prop oor:name="Label" oor:type="xs:string">
18            <value xml:lang="en-US">SlideTransition</value>

drviewsh.sdi

 0 diff --git a/sd/sdi/drviewsh.sdi b/sd/sdi/drviewsh.sdi
 1 index 5f58cb8..1268425 100644
 2 --- a/sd/sdi/drviewsh.sdi
 3 +++ b/sd/sdi/drviewsh.sdi
 4 @@ -218,6 +218,11 @@ interface ImpressEditView : DrawView
 5          ExecMethod = FuTemporary ;
 6          StateMethod = GetMenuState ;
 7      ]
 8 +    SID_HELLODIALOG
 9 +    [
10 +        ExecMethod = FuTemporary ;
11 +        StateMethod = GetMenuState ;
12 +    ]
13  }
14  
15  shell DrawViewShell

Library_sdui.mk

 0 diff --git a/sd/Library_sdui.mk b/sd/Library_sdui.mk
 1 index 14f4fbb..7e85166 100644
 2 --- a/sd/Library_sdui.mk
 3 +++ b/sd/Library_sdui.mk
 4 @@ -106,6 +106,7 @@ $(eval $(call gb_Library_add_exception_objects,sdui,\
 5      sd/source/ui/dlg/tpoption \
 6      sd/source/ui/dlg/vectdlg \
 7      sd/source/ui/dlg/PhotoAlbumDialog \
 8 +    sd/source/ui/dlg/HelloDialog \
 9  ))
10  
11  # $(WORKDIR)/inc/sd/sddll0.hxx :

UIConfig_simpress.mk

 0 diff --git a/sd/UIConfig_simpress.mk b/sd/UIConfig_simpress.mk
 1 index b134d89..c605112 100644
 2 --- a/sd/UIConfig_simpress.mk
 3 +++ b/sd/UIConfig_simpress.mk
 4 @@ -73,6 +73,7 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/simpress,\
 5          sd/uiconfig/simpress/ui/presentationdialog \
 6          sd/uiconfig/simpress/ui/printeroptions \
 7          sd/uiconfig/simpress/ui/photoalbum \
 8 +       sd/uiconfig/simpress/ui/hellodialog \
 9  ))
10  
11  # vim: set noet sw=4 ts=4:

menubar.xml

 0 diff --git a/sd/uiconfig/simpress/menubar/menubar.xml b/sd/uiconfig/simpress/menubar/menubar.xml
 1 index 73fa4fa..468269c 100644
 2 --- a/sd/uiconfig/simpress/menubar/menubar.xml
 3 +++ b/sd/uiconfig/simpress/menubar/menubar.xml
 4 @@ -216,6 +216,8 @@
 5              <menu:menuitem menu:id=".uno:InsertObjectFloatingFrame"/>
 6              <menu:menuseparator/>
 7              <menu:menuitem menu:id=".uno:ImportFromFile"/>
 8 +            <menu:menuseparator/>
 9 +            <menu:menuitem menu:id=".uno:HelloDialog"/>
10          </menu:menupopup>
11      </menu:menu>
12      <menu:menu menu:id=".uno:FormatMenu">

sdabstdlg.hxx

 0 diff --git a/sd/inc/sdabstdlg.hxx b/sd/inc/sdabstdlg.hxx
 1 index 21c78b7..da912fe 100644
 2 --- a/sd/inc/sdabstdlg.hxx
 3 +++ b/sd/inc/sdabstdlg.hxx
 4 @@ -215,6 +215,7 @@ public:
 5  
 6      virtual VclAbstractDialog* CreateSdPhotoAlbumDialog( ::Window* pWindow, SdDrawDocument* pDoc) = 0;
 7  
 8 +    virtual VclAbstractDialog* CreateSdHelloDialog( ::Window* pWindow) = 0;
 9  protected:
10      ~SdAbstractDialogFactory() {}
11  };

sdcommands.h

 0 diff --git a/sd/inc/sdcommands.h b/sd/inc/sdcommands.h
 1 index daecfe8..6683f80 100644
 2 --- a/sd/inc/sdcommands.h
 3 +++ b/sd/inc/sdcommands.h
 4 @@ -117,6 +117,7 @@
 5  #define CMD_SID_TP_EDIT_MASTER                      ".uno:TaskPaneEditMaster"
 6  #define CMD_SID_INSERTPAGE_LAYOUT_MENU              ".uno:TaskPaneInsertPage"
 7  #define CMD_SID_PHOTOALBUM                          ".uno:PhotoAlbumDialog"
 8 +#define CMD_SID_HELLODIALOG                         ".uno:HelloDialog"
 9  
10  #endif

sddlgfact.hxx

 0 diff --git a/sd/source/ui/dlg/sddlgfact.hxx b/sd/source/ui/dlg/sddlgfact.hxx
 1 index bd44d54..272ff47 100644
 2 --- a/sd/source/ui/dlg/sddlgfact.hxx
 3 +++ b/sd/source/ui/dlg/sddlgfact.hxx
 4 @@ -294,6 +294,8 @@ public:
 5      virtual CreateTabPage               GetSdOptionsMiscTabPageCreatorFunc();
 6      virtual CreateTabPage               GetSdOptionsSnapTabPageCreatorFunc();
 7  
 8 +    virtual VclAbstractDialog* CreateSdHelloDialog( ::Window* pWindow);
 9 +
10  };
11  
12  #endif

sddlgfact.cxx

 0 diff --git a/sd/source/ui/dlg/sddlgfact.cxx b/sd/source/ui/dlg/sddlgfact.cxx
 1 index 0103a32..d5f9f0c 100644
 2 --- a/sd/source/ui/dlg/sddlgfact.cxx
 3 +++ b/sd/source/ui/dlg/sddlgfact.cxx
 4 @@ -47,6 +47,7 @@
 5  #include "masterlayoutdlg.hxx"
 6  #include "headerfooterdlg.hxx"
 7  #include "PhotoAlbumDialog.hxx"
 8 +#include "HelloDialog.hxx"
 9  
10  IMPL_ABSTDLG_BASE(SdVclAbstractDialog_Impl);
11  IMPL_ABSTDLG_BASE(AbstractCopyDlg_Impl);
12 @@ -579,4 +580,9 @@ VclAbstractDialog * SdAbstractDialogFactory_Impl::CreateSdPhotoAlbumDialog( ::Wi
13       return new SdVclAbstractDialog_Impl( new ::sd::SdPhotoAlbumDialog( pWindow, pDoc ) );
14  }
15  
16 +VclAbstractDialog * SdAbstractDialogFactory_Impl::CreateSdHelloDialog( ::Window* pWindow)
17 +{
18 +     return new SdVclAbstractDialog_Impl( new ::sd::SdHelloDialog( pWindow ) );
19 +}
20 +
21  /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

drviews2.cxx

 0 diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx
 1 index caadb1e..06f79b5 100644
 2 --- a/sd/source/ui/view/drviews2.cxx
 3 +++ b/sd/source/ui/view/drviews2.cxx
 4 @@ -2860,9 +2873,90 @@ void DrawViewShell::FuTemporary(SfxRequest& rReq)
 5          }
 6          break;
 7  
 8 +       case SID_HELLODIALOG:
 9 +       {
10 +            SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create();
11 +            VclAbstractDialog* pDlg = pFact ? pFact->CreateSdHelloDialog(GetActiveWindow()) : 0;
12 +
13 +            if (pDlg)
14 +            {
15 +                pDlg->Execute();
16 +                delete pDlg;
17 +            }
18 +            Cancel();
19 +            rReq.Ignore ();
20 +        }
21 +        break;
22          default:
23          {
24              DBG_ASSERT( 0, "Slot without function" );