Development/Create new dialog in Impress

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). 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: /sd/uiconfig/simpress/ui/ ( means your file's name). Open the file /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/ \ (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: 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  command).
 * 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".

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.

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.

HelloDialog.hxx

HelloDialog.cxx app.hrc sdraw.sdi DrawImpressCommands.xcu drviewsh.sdi Library_sdui.mk UIConfig_simpress.mk menubar.xml sdabstdlg.hxx sdcommands.h sddlgfact.hxx sddlgfact.cxx drviews2.cxx