Development/Create new dialog in Impress

    From The Document Foundation Wiki
    Note pin.svg

    Note:
    This article needs to be updated.

    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. (Important note: some C++, Glade and make skills are assumed). Don't worry, examples are provided at the end of this tutorial (diff files), so you can see the changes that were made.

    Creating the dialog

    First, we need to create the dialog window. In this 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 our 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). 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 this case, we are going to add some simple elements (buttons). 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 the other two will not be used).

    Adding our dialog to Impress

    Now we're going to save the dialog where it belongs. As written 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

    The dialog file must be placed inside the Impress directory in: <your LO copy>/sd/uiconfig/simpress/ui/<filename.ui> (<filename.ui> means your file's name). Open the file <your LO copy>/sd/UIConfig_simpress.mk (the file in OpenGrok: https://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 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 written 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. Create the header file (HelloDialog.hxx) and the source file (HelloDialog.cxx) and place the header file under sd/source/ui/dlg/HelloDialog.hxx. The source file can go there as well. 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. You can add it under the Insert menu and 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, our 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 in 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. We will 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. We will 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. We will use the name SdCreateHelloDialog for the function.
    • 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. We will add "Open Hello Dialog".

    After, you've successfully made and saved your changes, don't forget to issue a make sd. This will build the Impress module (it is called "sd"). 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, its 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. We 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 we don't use the :: operator). We are not going to implement it now (we will 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. Create an InfoBox: InfoBox aInfo(this, OUString("Hello, World!")); and aInfo.Execute() will execute it.

    Example

    As 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

    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
    * This file is part of the LibreOffice project.
    *
    * This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.0. If a copy of the MPL was not distributed with this
    * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    */
    
    #pragma once
    
    #include "tools/link.hxx"
    
    #include <vcl/button.hxx>
    #include <vcl/dialog.hxx>
    
    namespace sd
    {
    
    class SdHelloDialog: public ModalDialog
    {
    
    public:
        SdHelloDialog(Window* pWindow);
        ~SdHelloDialog();
    
        virtual short Execute();
    
    private:
        OKButton* pOk_btn;
        CancelButton* pCancel_btn;
        PushButton* pHello_btn;
    
        DECL_LINK(OkHdl, void*);
        DECL_LINK(CancelHdl, void*);
        DECL_LINK(HelloHdl, void*);
    };
    
    } // end of namespace sd
    
    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

    HelloDialog.cxx

    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
    * This file is part of the LibreOffice project.
    *
    * This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.0. If a copy of the MPL was not distributed with this
    * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    */
    
    #include "HelloDialog.hxx"
    #include "strings.hrc"
    #include "sdresid.hxx"
    
    #include <vcl/msgbox.hxx>
    
    namespace sd
    {
    
    SdHelloDialog::SdHelloDialog(Window* pWindow)
    : ModalDialog(pWindow, "HelloDialog", "modules/simpress/ui/hellodialog.ui")
    {
        get(pOk_btn, "ok_btn");
        get(pCancel_btn, "cancel_btn");
        get(pHello_btn, "hello_btn");
    
        pOk_btn->SetClickHdl(LINK(this, SdHelloDialog, OkHdl));
        pCancel_btn->SetClickHdl(LINK(this, SdHelloDialog, CancelHdl));
        pHello_btn->SetClickHdl(LINK(this, SdHelloDialog, HelloHdl));
    }
    
    SdHelloDialog::~SdHelloDialog()
    {
    }
    
    short SdHelloDialog::Execute()
    {
        return ModalDialog::Execute();
    }
    
    IMPL_LINK_NOARG(SdHelloDialog, OkHdl)
    {
        return 0;
    }
    
    IMPL_LINK_NOARG(SdHelloDialog, CancelHdl)
    {
        Close();
        return 0;
    }
    
    IMPL_LINK_NOARG(SdHelloDialog, HelloHdl)
    {
        InfoBox aInfo(this, OUString("Hello, World!"));
        aInfo.Execute();
        return 0;
    }
    
    } // end of namespace sd
    
    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

    app.hrc

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

    sdraw.sdi

    diff --git a/sd/sdi/sdraw.sdi b/sd/sdi/sdraw.sdi
    index 83b4d04..f8ce2a5 100644
    --- a/sd/sdi/sdraw.sdi
    +++ b/sd/sdi/sdraw.sdi
    @@ -7251,3 +7251,27 @@ SfxVoidItem PhotoAlbumDialog SID_PHOTOALBUM
         ToolBoxConfig = TRUE,
         GroupId = GID_OPTIONS;
     ]
    +
    +SfxVoidItem HelloDialog SID_HELLODIALOG
    +()
    +[
    +    /* flags: */
    +    AutoUpdate = FALSE,
    +    Cachable = Cachable,
    +    FastCall = TRUE,
    +    HasCoreId = FALSE,
    +    HasDialog = TRUE,
    +    ReadOnlyDoc = FALSE,
    +    Toggle = FALSE,
    +    Container = FALSE,
    +    RecordAbsolute = FALSE,
    +    RecordPerItem;
    +    Synchron;
    +
    +    /* config: */
    +    AccelConfig = FALSE,
    +    MenuConfig = TRUE,
    +    StatusBarConfig = FALSE,
    +    ToolBoxConfig = TRUE,
    +    GroupId = GID_OPTIONS;
    +]

    DrawImpressCommands.xcu

    diff --git a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
    index d135cd13..e1a7ba1 100644
    --- a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
    +++ b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
    @@ -61,6 +61,14 @@
               <value>1</value>
             </prop>
           </node>
    +     <node oor:name=".uno:HelloDialog" oor:op="replace">
    +        <prop oor:name="Label" oor:type="xs:string">
    +          <value xml:lang="en-US">Open Hello Dialog</value>
    +        </prop>
    +        <prop oor:name="Properties" oor:type="xs:int">
    +          <value>1</value>
    +        </prop>
    +      </node>
           <node oor:name=".uno:Dia" oor:op="replace">
             <prop oor:name="Label" oor:type="xs:string">
               <value xml:lang="en-US">SlideTransition</value>

    drviewsh.sdi

    diff --git a/sd/sdi/drviewsh.sdi b/sd/sdi/drviewsh.sdi
    index 5f58cb8..1268425 100644
    --- a/sd/sdi/drviewsh.sdi
    +++ b/sd/sdi/drviewsh.sdi
    @@ -218,6 +218,11 @@ interface ImpressEditView : DrawView
             ExecMethod = FuTemporary ;
             StateMethod = GetMenuState ;
         ]
    +    SID_HELLODIALOG
    +    [
    +        ExecMethod = FuTemporary ;
    +        StateMethod = GetMenuState ;
    +    ]
     }
     
     shell DrawViewShell

    Library_sdui.mk

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

    UIConfig_simpress.mk

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

    menubar.xml

    diff --git a/sd/uiconfig/simpress/menubar/menubar.xml b/sd/uiconfig/simpress/menubar/menubar.xml
    index 73fa4fa..468269c 100644
    --- a/sd/uiconfig/simpress/menubar/menubar.xml
    +++ b/sd/uiconfig/simpress/menubar/menubar.xml
    @@ -216,6 +216,8 @@
                 <menu:menuitem menu:id=".uno:InsertObjectFloatingFrame"/>
                 <menu:menuseparator/>
                 <menu:menuitem menu:id=".uno:ImportFromFile"/>
    +            <menu:menuseparator/>
    +            <menu:menuitem menu:id=".uno:HelloDialog"/>
             </menu:menupopup>
         </menu:menu>
         <menu:menu menu:id=".uno:FormatMenu">

    sdabstdlg.hxx

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

    sdcommands.h

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

    sddlgfact.hxx

    diff --git a/sd/source/ui/dlg/sddlgfact.hxx b/sd/source/ui/dlg/sddlgfact.hxx
    index bd44d54..272ff47 100644
    --- a/sd/source/ui/dlg/sddlgfact.hxx
    +++ b/sd/source/ui/dlg/sddlgfact.hxx
    @@ -294,6 +294,8 @@ public:
         virtual CreateTabPage               GetSdOptionsMiscTabPageCreatorFunc();
         virtual CreateTabPage               GetSdOptionsSnapTabPageCreatorFunc();
     
    +    virtual VclAbstractDialog* CreateSdHelloDialog( weld::Window* pWindow);
    +
     };
     
     #endif

    sddlgfact.cxx

    diff --git a/sd/source/ui/dlg/sddlgfact.cxx b/sd/source/ui/dlg/sddlgfact.cxx
    index 0103a32..d5f9f0c 100644
    --- a/sd/source/ui/dlg/sddlgfact.cxx
    +++ b/sd/source/ui/dlg/sddlgfact.cxx
    @@ -47,6 +47,7 @@
     #include "masterlayoutdlg.hxx"
     #include "headerfooterdlg.hxx"
     #include "PhotoAlbumDialog.hxx"
    +#include "HelloDialog.hxx"
     
     IMPL_ABSTDLG_BASE(SdVclAbstractDialog_Impl);
     IMPL_ABSTDLG_BASE(AbstractCopyDlg_Impl);
    @@ -579,4 +580,9 @@ VclAbstractDialog * SdAbstractDialogFactory_Impl::CreateSdPhotoAlbumDialog( ::Wi
          return new SdVclAbstractDialog_Impl( new ::sd::SdPhotoAlbumDialog( pWindow, pDoc ) );
     }
     
    +VclAbstractDialog * SdAbstractDialogFactory_Impl::CreateSdHelloDialog( weld::Window* pWindow)
    +{
    +     return new SdVclAbstractDialog_Impl( new ::sd::SdHelloDialog( pWindow ) );
    +}
    +
     /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

    drviews2.cxx

    diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx
    index caadb1e..06f79b5 100644
    --- a/sd/source/ui/view/drviews2.cxx
    +++ b/sd/source/ui/view/drviews2.cxx
    @@ -2860,9 +2873,90 @@ void DrawViewShell::FuTemporary(SfxRequest& rReq)
             }
             break;
     
    +       case SID_HELLODIALOG:
    +       {
    +            SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create();
    +            VclAbstractDialog* pDlg = pFact ? pFact->CreateSdHelloDialog(GetActiveWindow()) : 0;
    +
    +            if (pDlg)
    +            {
    +                pDlg->Execute();
    +                delete pDlg;
    +            }
    +            Cancel();
    +            rReq.Ignore ();
    +        }
    +        break;
             default:
             {
                 DBG_ASSERT( 0, "Slot without function" );