17 Feb 2025

Understanding the existing code to provide better patches

LibreOffice inherits a gigantic code base from its ancestors, StarOffice and OpenOffice. Here I discuss some notes for the newcomers on how to better understand the existing LibreOffice code, and improve the patches.

Studying the Existing Code

As said, LibreOffice is a huge code base, containing ~10 million lines of mostly C++ code. There are different assumptions, conventions and coding styles across ~200 modules that LibreOffice has.

Therefore, it is important to first, study the existing code, through reading and debugging LibreOffice source code, to understand the things that it does, and the way you can implement your ideas, including bug fixes and adding new features.

And although implementing some ideas seem to be straightforward at first sight, it is meaningful to study the details.

Quality Assurance Point of View

First of all, you should understand the thing that you want to implement. No matter if it is a bug, a new feature, or just an EasyHack, you should understand what is requested, what works and what does not work. This requires careful reading of the Bugzilla pages.

User Point of View

Then, you should try to run LibreOffice to understand the exact place in the application where you want to change. LibreOffice user interface has thousands of dialog boxes, so you need to make sure that you understand the thing that you want to do.

Developer Point of View

And at last, you get into implementing something in the code. Here are some questions that you can ask yourself about the details, when reading the existing code:

  • Why this statement is here, in the first place? (detail-oriented view)
    • You can use git blame to see the last author of a specific line
    • You can use git log to study the details by knowing the commit hash
    • What can this part of code actually does?
    • Can I see its effect?
git log

git log

Or, you may be interested in the code behavior in the big picture:

  • What does the code do as a whole? (holistic view)
  • There are many other statements, functions and other constructs in the code. What do they do?
  • What is the overall goal of the code?
  • Can I test that in action?

You can do some small changes, before even getting into implementing your idea:

  • What happens if I remove it? (small changes)
  • Does the removal prevent the code from working?
  • Is it incomplete, or does it actually do something useful, which
  • will be absent if I remove it?

Then, you can work on the actual implementation. Ask yourself:

  • How can I implement the idea in its simplest form? (straightforward change)
  • Does it have side effects?
  • How can I make sure every thing else works as before?
  • How can I write a test for it?

After understanding some of the basic details about the way things work, you may go into improving your implementation.

  • How can I make it better? (sophisticated change)
  • Can I make the code more robust where it is brittle?
  • Can I complete the code where it is incomplete?

Final Notes

These were the questions to give you some ideas of some of the underlying complexities in the code. You can start from small changes to become familiar with these complexities, and then grow to do more complex stuff in the code.

We have various different EasyHacks in LibreOffice, with different difficulty levels. If you are interested in coding, you can always find something that fits you, and grow gradually.

You can read more in these links:

30 Jan 2025

Custom message boxes using VCL Weld

When you want to interact with users, sometimes simple dialog boxes are sufficient: a simple yes or no, or some info box. But in other cases, you may need more complex message boxes. Here I discuss how to use VCL Weld to create a custom one.

Simple Message Box

You can create a simple message box, using predefined templates like Info box using a code snippet like this:

std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, VclButtonsType::YesNo, u"Are you sure?"_ustr));
xInfoBox->run();

And, this is the result, which is very simple, without any title bar:

Yes / No message box

Yes / No message box

There are other predefined types, which can be used in different scenarios:

enum class VclMessageType
{
    Info,
    Warning,
    Question,
    Error,
    Other
};

But, if you want custom message boxes, you should be using weld mechanism, with its CreateBuilder function.

Custom Message Boxes

Below is the code from the source code sfx2/source/doc/QuerySaveDocument.cxx, which is inside sfx2 (framework) module. This dialog box is accessible across different modules, including Writer, Calc and Draw/Impress.

Let’s look into the code:

short ExecuteQuerySaveDocument(weld::Widget* _pParent, std::u16string_view _rTitle)
{
    ...
    std::unique_ptr<weld::Builder> xBuilder(
        Application::CreateBuilder(_pParent, u"sfx/ui/querysavedialog.ui"_ustr));
    std::unique_ptr<weld::MessageDialog> xQBox(
        xBuilder->weld_message_dialog(u"QuerySaveDialog"_ustr));
    xQBox->set_primary_text(xQBox->get_primary_text().replaceFirst("$(DOC)", _rTitle));
    return xQBox->run();
}

The code is using a UI file, named sfx/ui/querysavedialog.ui to create a message dialog, and then change the title of it.

QuerySaveDialog

QuerySaveDialog

If you look into the include file, include/vcl/weld.hxx inside Builder class, you may see functions like weld_… that are suitable to find various different UI elements from the UI, by mentioning the element ID. For example, to find a label with the ID equal to lable_id, you do this:

std::unique_ptr<weld::Label> m_pTextLabel label = m_xBuilder->weld_label(u"label_id"_ustr)

Result

This is the result, when you try to close an unsaved document.

QuerySaveDialog running

QuerySaveDialog running

Alternative Ways

This is not the only way you can create nice dialog boxes using VCL weld mechanism. There are some predefined message boxes that look nice which use weld mechanism, and are available for use via relevant C++ classes.

An interesting one here, is the QueryDialog, which is created by a factory method design pattern.

It uses a predefined dialog, using cui/uiconfig/ui/querydialog.ui as the UI file, and it contains a nice stock image! You can test it easily, by modifying a LibreOffice example, minweld.

IMPL_LINK_NOARG(TipOfTheDayDialog, OnNextClick, weld::Button&, void)
{
    VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
    auto pDlg = pFact->CreateQueryDialog(getDialog(), u"Tips"_ustr, u"Tip of the day"_ustr, u"Are you sure you want to see the next tip of the day?"_ustr, false);
    sal_Int32 nResult = pDlg->Execute();
    pDlg->disposeOnce();

    if(nResult == RET_YES)
    {
        ++m_nCounter;
        m_pTextLabel->set_label(u"Here you will see tip of the day #"_ustr
+ OUString::number(m_nCounter) + ".");
    }
}

Assuming that you have a working build of LibreOffice, you can simply run the minweld workbench by invoking:

./bin/run minweld

The result looks much more interesting:

QueryDialog

QueryDialog

Final Words

The possibilities are endless! It only depends on your ideas and understanding of the user’s needs and requirements. It would be good if you look into what design team does to understand the design process:

16 Jan 2025

Outlook for the new year 2025

Happy new year 2025! I wish a great year for you, and the global LibreOffice community. Now that we are now in 2025, I briefly discuss the year 2024 and outlook for 2024 in the development blog.

LibreOffice Conference 2024, Luxembourg

LibreOffice Conference 2024, Luxembourg

At The Document Foundation (TDF), our aim is to improve LibreOffice, the leading free/open source office suite that has millions of users around the world. Our work is community-driven, and the software needs your contribution to become better, and work in a way that you like.

My goal here, is to help people understand LibreOffice code easier, and eventually participate in LibreOffice core development to make LibreOffice better for everyone. In 2024, I wrote 22 posts around LibreOffice development in the dev blog (4 of them are unpublished drafts).

Outlook For the New Year

Focus of the development blog for 2025 in this blog will be:

  • Introducing new EasyHacks
  • Describing user interface creation with VCL
  • Explaining LibreOffice architecture
  • Explaining Python interaction with LibreOffice

I have written about some of these topics in 2024. Therefore, this year I will try to expand the previous writings and provide new articles about them. For example, creating user interfaces using VCL with the help of glade interface designer will be one of important things to discuss.

You can give feedback by writing a comment here, or sending me an email to hossein AT libreoffice DOT org.

We provide mentoring support to those who want to start LibreOffice development. You are welcome to contact me if you need help to build LibreOffice and do some EasyHacks via the above email address. Also, you can always refer to our Getting Involved Wiki page:

Let’s hope a great year for LibreOffice (and the world) in 2025.

22 Nov 2024

VCL weld: create LibreOffice GUI from design files

LibreOffice uses VCL (Visual Class Library) as its internal widget toolkit to create the graphical user interface (GUI) of LibreOffice. Here I discuss how to use UI files designed with Glade interface designer to create LibreOffice user interfaces with a framework called weld, which is part of LibreOffice core source code.

Creating a Minimal VCL Weld Application

In my previous blog post, you can find out about the structure of a minimal VCL application. Please refer to the below blog post to see how a Window is created in VCL, and how it can be used as a test workbench called minvcl. You can run it with ./bin/run minvcl after you build LibreOffice.

VCL application in its minimal form

Here I discuss how to go further, and create user interface with Glade interface designer, and do most of the things without writing code.

VCL Weld Mechanism

In order to simplify user interface creation in LibreOffice, experienced LibreOffice developer, Caolán, has introduced a mechanism to load UI files created with Glade interface designer, and use them as if they are UI files for each and every GUI framework that LibreOffice supports: from GTK itself to Qt, Windows, macOS and even the so-called gen backend that only requires the X11 library on Linux.

To illustrate how the VCL weld mechanism works, I have added a minimal example, minweld, as a test workbench. The structure of the code is very similar to the previous example, minvcl, but there are some changes in the code. In the new code, UI is created from a .ui file that is designed visually with Glade interface designer. The .ui file is an XML file which contains placement of widgets that should be displayed on the screen.

The complete code for minweld is available in the LibreOffice core source code repository, which can also be viewed online:

Glade UI File

In minweld, I have used an existing Glade UI file, tipofthedaydialog.ui. This is the user interface for displaying a tip of the day in LibreOffice at startup. Heiko, the TDF design mentor, has discussed this dialog box in detail before:

Easyhacking: How to create a new “Tip-Of-The-Day” dialog

But, you can assume that it is a simple .ui file, that one can create with Glade. Here, we use it to create our own user interface in C++. You may use any other .ui file that you have created with almost the same code.

Tip of the day displayed at LibreOffice startup

Tip of the day displayed at LibreOffice startup

This UI file is found in cui/uiconfig/ui/tipofthedaydialog.ui, and minweld loads it. This is how it looks when you open it in Glade interface designer:

tipofthedaydialog.ui in Glade user interface designer

tipofthedaydialog.ui in Glade user interface designer

Let’s look into the specifics of minweld.cxx.

Header Includes

Headers are almost the same, but here we use vcl/weld.hxx instead of vcl/wrkwin.hxx. Therefore, you can see this line in the code:

#include <vcl/weld.hxx>

Then we have the C++ code for the application. The TipOfTheDayDialog class is defined with:

class TipOfTheDayDialog : public weld::GenericDialogController
{
public:
    TipOfTheDayDialog(weld::Window* pParent = nullptr);
    DECL_LINK(OnNextClick, weld::Button&, void);

private:
    std::unique_ptr<weld::Label> m_pTextLabel;
    std::unique_ptr<weld::Button> m_pNextButton;
    sal_Int32 m_nCounter = 0;
};
...
}

As you can see, TipOfTheDayDialog inherits from weld::GenericDialogController, and not Application class as before. Also, TipOfTheDayDialog constructor receives a parent of type weld::Window*, which is nullptr now. The reason is that there is no parent window in this example. Using weld:: prefix is also done for other types of widgets that we use in LibreOffice. For example, we use weld::Button to denote a push button in LibreOffice, or in any application that is created with the vcl::weld mechanism.

Class Constructor

This is the code for the TipOfTheDayDialog constructor. Here, we initialize two member variables, m_pTextLabel and m_pNextButton which point to a label and a button, respectively. We will interact with these two in our code. There are string literals like lbText and btnNext , which are the IDs of those widgets in Glade. The IDs should be unique for linking to specific variables in the code.

TipOfTheDayDialog::TipOfTheDayDialog(weld::Window* pParent)
: weld::GenericDialogController(pParent, u"cui/ui/tipofthedaydialog.ui"_ustr,
u"TipOfTheDayDialog"_ustr)
, m_pTextLabel(m_xBuilder->weld_label(u"lbText"_ustr))
, m_pNextButton(m_xBuilder->weld_button(u"btnNext"_ustr))
{
    m_pNextButton->connect_clicked(LINK(this, TipOfTheDayDialog, OnNextClick));
}

One last step is linking the events with functions in the code. You may do that with the LINK macro. In the last line, connect_clicked activates OnNextClick from the class TipOfTheDayDialog, whenever m_pNextButton is clicked.

Event Handler

This is the implementation of the event handler. It should be started with IMPL_LINK macro, in the form of IMPL_LINK_NOARG(Class, Member, ArgType, RetType). The code is straightforward: It increases a counter which is initially zero, and displays it alongside a text:

IMPL_LINK_NOARG(TipOfTheDayDialog, OnNextClick, weld::Button&, void)
{
    ++m_nCounter;
    m_pTextLabel->set_label(u"Here you will see tip of the day #"_ustr
+ OUString::number(m_nCounter) + ".");
}

With a call to set_label function, m_pTextLabel is updated every time that you click on “Next Tip” button.

Running the Example

You may run the example after you have built LibreOffice from sources. Then, you may simply invoke:

./bin/run minweld

The result is a little bit different from the tipoftheday dialog in LibreOffice, as it does not use a picture. But, it has a nice feature: if you click on “Next Tip”, it will show a text and a counter that goes up whenever you click on it again.

Final Notes

You may look into the original “tip of the day” dialog box in cui/source/dialogs/tipofthedaydlg.cxx, which is more complex than the one that we created here, as it reads some data from the configuration and uses images. But, the idea is the same. Inherit a class from GenericDialogController, define and link variables to the widgets with their IDs, add event handlers. Now, the application with VCL graphical user interface is ready to use!

This is somehow similar to the way one creates dialog boxes with Qt and other widget toolkits. On the other hand, the VCL weld mechanism is different in the way that it uses such a toolkit to create UI on the fly. Therefore, if you choose a desired VCL UI plugin, then it will use that specific library for creating user interface. For example, you can run minweld example with Qt this way:

export SAL_USE_VCLPLUGIN=qt5

./bin/run minweld
The minweld example in Qt (light theme)

The minweld example in Qt (light theme)

You may also run it with GTK3 UI, this way:

export SAL_USE_VCLPLUGIN=gtk3

export GTK_THEME=Adwaita:light # For light/dark theme

./bin/run minweld
The minweld example in GTK3 (light theme)

The minweld example in GTK3 (light theme)

I hope that this explanation was helpful for you to understand the basics of GUI design and implementation in LibreOffice. You can try doing small improvements in LibreOffice GUI by looking into the EasyHacks that with the tag “Design“:

TDF Wiki: EasyHacks categorized by “Design” as the required skill

We welcome your code submissions to improve LibreOffice. If you would like to start contributing to LibreOffice, please take a look at our video tutorial:

Getting Started (Video Tutorial)

14 Nov 2024

Notebookbar part 1: custom widgets for the tabbed interface

Notebookbar, or tabbed interface is an attempt to modernize LibreOffice user interface. In these series, I try to explain the implementation in LibreOffice code. In the first part, I discuss custom Glade widgets that are building blocks of Notebookbar user interface.

Building LibreOffice From Sources

If you haven’t built LibreOffice from sources before, you can refer to can refer to this tutorial:

Getting Started (Video Tutorial)

The next sections assume that you have a working build environment.

Custom Widgets in Glade Catalogs

Notebookbar implementation consists of .ui files, configuration files and C++ implementation. Let’s look into the user interface files.

First time that you clone LibreOffice source code, and try to open a Notebookbar UI file like this, you may see error:

$ glade ./sc/uiconfig/scalc/ui/notebookbar.ui

You may see an error, which indicates that a required catalog related to LibreOffice is not available.

Glade error

Glade error

To fix this issue, you have to know that Notebookbar uses custom widgets that with the Glade interface designer. These custom widgets are available from a Glade catalog with the name of LibreOffice.

Inside sc/uiconfig/scalc/ui/notebookbar.ui, you may see these two lines:

<requires lib="gtk+" version="3.20"/>
<requires lib="LibreOffice" version="1.0"/>

Glade catalogs are xml files with the keyword glade-catalog inside them, so we can search for this keyword:

$ git grep -l glade-catalog
extras/source/glade/libreoffice-catalog.xml.in
extras/source/glade/makewidgetgroup.xslt

The .in files is an input file in which the build process creates the final xml file out of it. Searching for glade-catalog inside the build folder results:

$ grep -lr glade-catalog
...
instdir/share/glade/libreoffice-catalog.xml

As you can see, the result goes inside the folder instdir/share/glade/, so to be able to use the catalog, you should add this folder to the glade catalog search path. One of the easiest ways to do this, is to add it via Glade interface itself. Use ☰ (hamburger menu), go to “Glade Preferences”, and add instdir/share/glade/ to the “Extra Catalog & Template paths”. Then, reload a notebookbar UI file, and the error should go away. This setting is saved inside ~/.config/glade.conf configuration file.

Custom Widgets for the Notebookbar

Inside the Glade custom catalog instdir/share/glade/libreoffice-catalog.xml, you can see 10 custom widgets:

$ grep "glade-widget-class\ " instdir/share/glade/libreoffice-catalog.xml
<glade-widget-class title="Notebookbar ToolBox" name="sfxlo-NotebookbarToolBox" generic-name="Notebookbar ToolBox" parent="GtkToolbar" icon-name="widget-gtk-toolbar">
<glade-widget-class title="Notebook switching tabs depending on context" name="sfxlo-NotebookbarTabControl" generic-name="NotebookbarTabControl" parent="GtkNotebook" icon-name="widget-gtk-notebook"/>
<glade-widget-class title="Horizontal box hiding children depending on its priorities" name="sfxlo-PriorityHBox" generic-name="PriorityHBox" parent="GtkBox" icon-name="widget-gtk-box"/>
<glade-widget-class title="Horizontal box hiding children depending on its priorities" name="sfxlo-PriorityMergedHBox" generic-name="PriorityMergedHBox" parent="GtkBox" icon-name="widget-gtk-box"/>
<glade-widget-class title="Box which can move own content to the popup" name="sfxlo-DropdownBox" generic-name="DropdownBox" parent="GtkBox" icon-name="widget-gtk-box"/>
<glade-widget-class title="Box which can hide own content" name="VclOptionalBox" generic-name="VclOptionalBox" parent="GtkBox" icon-name="widget-gtk-box"/>
<glade-widget-class title="Vertical box hiding children depending on context" name="sfxlo-ContextVBox" generic-name="ContextVBox" parent="GtkBox" icon-name="widget-gtk-box"/>
<glade-widget-class title="Managed Menu Button" name="svtlo-ManagedMenuButton" generic-name="ManagedMenuButton" parent="GtkButton" icon-name="widget-gtk-button"/>
<glade-widget-class title="NotebookBar Toolbar Addons" name="NotebookBarAddonsToolMergePoint" generic-name="ShowText" parent="GtkToolButton" icon-name="widget-gtk-toolbutton"/>
<glade-widget-class title="NotebookBar MenuItem Addons" name="NotebookBarAddonsMenuMergePoint" generic-name="ShowText" parent="GtkMenuItem" icon-name="widget-gtk-menuitem"/>

The previous xml shows the custom widgets that are building blocks of building Notebookbar. Let’s look into each of them, based on their title and names.

Notebookbar widgets

In the next picture, you can see the notebookbar in LibreOffice, and compare it to what is visible in Glade user interface designer. As you can see, not everything is visible in the designer. Specifically, icons and text are not visible in the designer but are visible in the final application.

 

LibreOffice with Notebookbar

Notebookbar in LibreOffice

Main Widget

1. Notebookbar Tab Control: This widget has the name sfxlo-NotebookbarTabControl, and is the primary widget for Notebookbar. It can change the set of visible tabs based on the user context. Its parent class is GtkNotebook and provides context-sensitive tab switching.

Container Widgets

2. NotebookbarToolBox: This widget is named sfxlo-NotebookbarToolBox,  its parent class is GtkToolbar. It can contain toolbar elements.

NotebookbarTabControl

NotebookbarTabControl

3. Priority Horizontal Box: This widget has the name sfxlo-PriorityHBox, and its parent class is GtkBox. It is the horizontal box hiding children depending on its priorities. In this way, lower priority widgets becomes hidden to give the more important widgets room to be displayed on a screen that is not big enough to show all the available elements.

4. Priority Merged Horizontal Box: This widget has the name sfxlo-PriorityMergedHBox, and its parent class is GtkBox. It is the “horizontal box hiding children depending on its priorities”. This widget is also related to the previous one for creating more room for important widgets, but it is used inside the PriorityHBox.

5. Optional Box: This widget has the name VclOptionalBox, and its parent class is GtkBox. This “box which can hide own content”, is a widget that is useful for creating small areas dedicated to a specific purpose. For example, you may see Home-Section-Clipboard, which is used to define an area for clipboard related tasks inside Home tab.

6. Contextual Vertical Box: This widget has the name sfxlo-ContextVBox and is a “vertical box hiding children depending on context” and its parent class is GtkBox. It provides a box that can act based on the context, showing and hiding its children accordingly. You may look into sw/uiconfig/swriter/ui/notebookbar_single.ui, which provides an example use.

Here is the correct control hierarchy, as depicted and described in the TDF Wiki:

Correct Notebookbar controls hierarchy

Correct Notebookbar controls hierarchy

Menu Widgets

7. Dropdown Box: This widget has the name sfxlo-DropdownBox, its parent class is GtkBox and is a “box which can move own content to the popup”. This is also useful where the space for the tabbed interface is not big enough. The menu, is what you can see in “File” and “Help” menu in every notebookbar in LibreOffice tabbed interface. Please note that only 1 GtkBox child should be inside it, so that the popup works properly. In fact, the above diagram shows the correct usage.

8. Managed Menu Button: This widget has the name svtlo-ManagedMenuButton, and its parent class is GtkButton. It is a “Managed Menu Button”. It provides a button that opens a dynamic menu which is populated according to the context.

Custom Widgets for the Extensions

9. NotebookBar MenuItem Addons: This widget has the name NotebookBarAddonsToolMergePoint, and its parent class is GtkToolButton. Specifically, LibreOffice extensions can use it for adding additional tools to the notebookbar.

10. NotebookBar MenuItem Addons: This widget has the name NotebookBarAddonsMenuMergePoint, and its parent class is GtkMenuItem. This is also used for adding extra items into the notebookbar.

Final Notes

You can find useful information about Notebookbar in the design team blog:

And at last, these are some useful Wiki articles around Notebookbar in the TDF Wiki:

24 Oct 2024

Crash fix part 5: crash report tool

In previous blog posts about crashes in LibreOffice, I have discussed how to debug and fix some of crashes. Now I discuss a nice tool to keep track of the crash reports from volunteers: Crash report tool.

Crash Report Statistics

Crash report is available via this LibreOffice website:

You can see that different versions of LibreOffice listed there, and for each and every tracked version, number of crashes during the previous 1, 3, 7, 14 and 28 days can be seen. This is possible using the appropriate buttons on the top.

This data is gathered from those to volunteer to submit reports to make LibreOffice better.

This statistic is very helpful to understand the robustness of the builds in different versions.

Crash Signatures

If you choose a specific version, you may see signatures of the crashes. This is helpful when trying to fix crashes. For example, this is one of the crash signatures found in LibreOffice 24.8.0.3:

This shows that the crash happens in GetCharFormat() function. One may use this information to track and fix the problem.

Looking into one of the crashes, one may see the details of the crash, including the stack trace in the crashing thread, and link to the exact place of the source code that leads to the crash.

As an example, you can see this crash report.

Sometimes, experienced developers may be able to reproduce the bug using crash signatures while knowing some background. Otherwise, in most cases, filing a bug with documents and instructions to reproduce the bug is essential. Adding a link to the crash report can be helpful.

17 Oct 2024

WSL for building LibreOffice

Windows Subsystem for Linux (WSL) is a mechanism to use complete Linux distributions on Windows. Here I discuss how to use it to build LibreOffice for both Linux and Windows binaries.

What is WSL?

WSL is the relatively new mechanism in Windows that lets you use a complete Linux distribution alongside your Windows. Interoperability between WSL and Windows lets you to share files and utilities between Windows and Linux. That is where it becomes helpful for LibreOffice, as LibreOffice make depends heavily on GNU tools, which are available in Linux.

Linux or Windows?

You can use WSL for 2 different scenarios:

1. Building for Linux: this is the full Linux build, in which Linux compilers, libraries and utilities will be used to create a Linux binary. You can then run or package the Linux build. You can find more information here:

Using WSL2 is recommended, as it is supposed to be faster, and also you can simply use the graphical interface of LibreOffice.

LibreOffice build on WSL with Linux binaries, displayed on Windows

LibreOffice build on WSL with Linux binaries, displayed on Windows

When you run the resulting binary, the graphical interface is usable, and it will use GTK fronted by default.

2. Building for Windows: this is the WSL as helper mode, where it uses only a few Linux utilities like pkg-config, make, automake and a few other utilities to configure the project.  Then, GNU Make for Windows will be the tool to build the project. More information is available here:

The results are Windows .exe files, and you can simply run them on Windows as native programs.

Build Options on Windows

You can build LibreOffice on different platforms. On Windows, it is possible to use Cygwin, but using WSL can be faster, and considering some issues with recent Cygwin versions, WSL is an alternative.

One can imagine of other ways to build LibreOffice on Windows, including MinGW. But, at the moment, MinGW, both as a helper to use Visual Studio, and also as an independent distribution to build LibreOffice, is not usable due to various reasons.

And last note: if you do not have prior experience with LibreOffice development but you are interested, you can start from our video tutorial for getting started with LibreOffice development.

3 Oct 2024

Setting dialogs that are available via UNO Commands

LibreOffice options page provides rich set of settings for everyone who wants to tune LibreOffice to match their needs. But, what if you as a developer, need setting dialogs that are needed elsewhere in the LibreOffice application? Here I discuss some of such use cases, which are handled by defining UNO commands.

Options Page

The code for providing “Tools > Options” is not in a single module, but main part resides in cui module, which contains code which is used across different modules. Looking into cui/source/options/ folder from LibreOffice core source code, you can see various different source files related to the options. The biggest file there is cui/source/options/treeopt.cxx, which is the actual implementation of the tree-based dialog that you see when you open Tools > Options dialog. There are other C++ files that handle .ui files related to options. You can find those UI files in cui/uiconfig/ui/ folder with a name like opt*.ui:

$ ls cui/uiconfig/ui/opt*.ui

These files can be edited and they are used as described in the LibreOffice design blog:

UNO Dispatch Commands

Only some of the dialogs can be opened available via UNO dispatch commands. As an example, you may see “.uno:AdditionsDialog” is used both in cui/source/options/optgdlg.cxx for creating a dialog in Tools > Options (when you click for “more icons”), and also in sfx2/source/appl/appserv.cxx.

You can try running this UNO command in LibreOffice BASIC editor with this code snippet:

Sub Main()
    Set oDispatch = CreateUnoService("com.sun.star.frame.DispatchHelper")
    Dim args(0) As New com.sun.star.beans.PropertyValue
    Set oFrame = StarDesktop.Frames.getByIndex(0)
    oDispatch.executeDispatch(oFrame, ".uno:AdditionsDialog", "", 0, args)
End Sub

The above command is defined specifically to help developers use the “Extensions” dialog, anywhere in LibreOffice UI, from top menus to context menus and toolbars and also in code, in a simple way.

"Extensions

There is another dialog titled “Security Options and Warnings”, which is opened through .uno:OptionsSecurityDialog UNO command. In this way, it can be used easily in other modules of LibreOffice.

SecurityOptionsDialog

SecurityOptionsDialog

Implementing UNO Command

Adding a new UNO command was discussed before, in a separate blog post:

Adding a new UNO command

Adding a new UNO command for an options dialog is basically the same. There can be differences regarding the configurations and the data that is passed between the dialog and the caller.

When you create a dialog box directly like the code snippet below, you have access to the member functions defined for that specific dialog:

IMPL_LINK_NOARG( SwGlossaryDlg, PathHdl, weld::Button&, void )
{
    SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
    ScopedVclPtr<AbstractSvxMultiPathDialog> pDlg(pFact->CreateSvxPathSelectDialog(m_xDialog.get()));
    SvtPathOptions aPathOpt;
    const OUString sGlosPath( aPathOpt.GetAutoTextPath() );
    pDlg->SetPath(sGlosPath);
    if(RET_OK == pDlg->Execute())
    {
        const OUString sTmp(pDlg->GetPath());
        if(sTmp != sGlosPath)
        {
            aPathOpt.SetAutoTextPath( sTmp );
            ::GetGlossaries()->UpdateGlosPath( true );
            Init();
        }
    }
}

As you can see, pDlg->GetPath() is accessible here, and you can use it to pass data. But when you are using UNO commands, those functions will not be available directly. Instead, you may pass values that denote the data that will be read from somewhere else, like the configuration.

For example, consider there are multiple paths that you may want to edit using this UNO command with the same dialog. In this case, you can pass a value that shows the associated path that you are changing. It can be passed as an enumeration, and then you set and/or get the value directly from the configuration.

In this way, the callers in the C++ code will have much easier task to do, as it is only calling the UNO command, and the rest is done in the implementation of the UNO command.

For AdditionsDialog, the calling code is as simple as this in cui/source/options/optgdlg.cxx:

IMPL_STATIC_LINK_NOARG(OfaViewTabPage, OnMoreIconsClick, weld::Button&, void)
{
    css::uno::Sequence<css::beans::PropertyValue> aArgs{ comphelper::makePropertyValue(
u"AdditionsTag"_ustr, u"Icons"_ustr) };
    comphelper::dispatchCommand(u".uno:AdditionsDialog"_ustr, aArgs);
}

Passing Parameters to UNO Commands

UNO dispatch commands can take parameters. As an example, take a look at .uno:NewDoc defined in sfx2/sdi/sfx.sdi:

SfxStringItem NewDoc SID_NEWDOC
(SfxStringItem Region SID_TEMPLATE_REGIONNAME,SfxStringItem Name SID_TEMPLATE_NAME)
[
...
]

As you can see, there are two parameters:

SfxStringItem Region SID_TEMPLATE_REGIONNAME SfxStringItem Name SID_TEMPLATE_NAME

The SfxStringItem is the type, Region and Name are the names of the parameters, SID_TEMPLATE_REGIONNAME and SID_TEMPLATE_NAME are the constants used for passing parameters. The parameter type is not limited to numbers and strings, and it can be any defined class.

To set and get data for this parameters, you may use appropriate Set and Put functions. For example, this gets the parameter if set:

const SfxStringItem *pItem = rSet.GetItemIfSet( SID_TEMPLATE_REGIONNAME, false)

Or, this sets the data:

OUString sVal = u"test"_ustr;

rSet.Put( SfxStringItem( SID_TEMPLATE_REGIONNAME, sVal ) );

Final Notes

To get to know better how to implement more complex UNO dispatch commands, you can refer to the implementation of the existing UNO commands to get idea about how they are implemented. You can see a comprehensive list of UNO commands here:

15 Aug 2024

Extending the UNO API

Various functionalities of the LibreOffice are available through its programming interface, the UNO API. Here I discuss how to extend it.

What is UNO API?

Many functionalities of the LibreOffice is available through UNO API. You can write extensions and external programs that use LibreOffice functionality without the need to change the LibreOffice core source code.

Extensions work seamlessly with the software, and external applications can connect to the LibreOffice process and use it. The ability to do that depends on the UNO API.

On the other hand, some functionalities may not be available through this API. For example, newer features of the decent versions of LibreOffice, or functionalities that are not useful and/or important for external applications. Sometimes, you may want to use such functionalities elsewhere. Then you have to modify the LibreOffice core source code, and expose those functionalities through the API make them available to the external applications.

Let’s refer to the LibreOffice Developer’s Guide, which is mostly around the LibreOffice UNO API. There, you can read:

“The goal of UNO (Universal Network Objects) is to provide an environment for network objects across programming language and platform boundaries. UNO objects run and communicate everywhere.”

As UNO objects should be usable across different languages and platforms, they are described in an abstract meta language called UNOIDL (UNO interface definition language). This is similar to the IDL definitions in many other technologies like CORBA.

Example UNO API: FullScreen

The API that I discuss here, provides functionality to control full screen functionality for top level windows. Stephan, experienced LibreOffice developer, added that API in this commit:

commit af5c4092052c98853b88cf886adb11b4a1532fff

Expose WorkWindow fullscreen mode via new XTopWindow3

...deriving from the existing XTopWindow2. (Exposing this functionality via UNO
is useful e.g. for some embedded LOWA example application.)

The changes in this commit are over these files:

offapi/UnoApi_offapi.mk
offapi/com/sun/star/awt/XTopWindow3.idl
toolkit/inc/awt/vclxtopwindow.hxx
toolkit/source/awt/vclxtopwindow.cxx

First one, offapi/UnoApi_offapi.mk is needed to introduce the IDL file, according to its module, in a proper location. XTopWindow3.idl is added in com/sun/star/awt, which corresponds to com.sun.star.awt module. The other two, vclxtopwindow.hxx and vclxtopwindow.cxx are the implementation of the API in C++.

Let’s look into XTopWindow3.idl:

module com { module sun { module star { module awt {

/** extends XTopWindow with additional functionality

@since LibreOffice 25.2
*/
interface XTopWindow3: XTopWindow2 {
/** controls whether the window is currently shown full screen */
    [attribute] boolean FullScreen;
};

}; }; }; };

As you may see, it contains these important information:

1. It is an interface, called XTopWindow3.

2.It has a boolean attribute, FullScreen.

3. This functionality will be available in LibreOffice 25.2 and later.

4. This interface extends XTopWindow interface. You may find the documentation for XTopWindow in api.libreoffice.org.

More information about XTopWindow interface can be found in XWindow section of the LibreOffice Developer’s Guide, chapter 2.

C++ Implementation

C++ implementation basically consists of two functions to set and get the FullScreen property:

sal_Bool VCLXTopWindow::getFullScreen() {
    SolarMutexGuard g;
    if (auto const win = VCLXContainer::GetAsDynamic()) {
        return win->IsFullScreenMode();
    }
    return false;
}

void VCLXTopWindow::setFullScreen(sal_Bool value) {
    SolarMutexGuard g;
    if (auto const win = VCLXContainer::GetAsDynamic()) {
        return win->ShowFullScreenMode(value);
    }
}

Final Words

Some APIs are much more complex. The one that was discussed here was only an example to show the required things to extend UNO API. You can browse the API documentation in api.libreoffice.org for more complex APIs:

29 Jul 2024

Fuzz testing to maintain LibreOffice code quality

Here I discuss what fuzz testing is, and how LibreOffice developers use it incrementally to maintain LibreOffice code quality.

Maintaining Code Quality

LibreOffice developers use various different methods and tools to maintain LibreOffice code quality. These are some of them:

1. Code review: Every patch from contributors should pass code review on Gerrit, and after conforming to coding standards and conventions, it can become part of the LibreOffice source code.

2. Static code checking: “Coverity Scan” continuously scans LibreOffice source code to find the possible defects. An automated script reports these issues to the LibreOffice developers mailing list so that developers can fix them.

3. Continuous Testing: There are various C++ unit test and Python UI tests in LibreOffice core source code to make sure that the functionalities of the software remain working during the later changes. They are also helpful for making sure that the fixed regressions do not happen again. These test run continuously for each and every Gerrit submission on CI machines via Jenkins.

4. Crash testing: A good way to make sure that LibreOffice works fine is to batch open and convert a huge set of documents. This task is done regularly, and if some failure occurs developers are informed to fix the issue.

5. Crash reporting: LibreOffice uses crash testing to find out about the recurrent crashes, and fix them.

6. Tinderbox Platforms: Using dedicated machines with various different architectures, LibreOffice developers make sure that LibreOffice source code builds and runs without problem on different platforms. Here is the description of tinderbox (TB) from TDF Wiki:

Tinderbox is a script to run un-attended build on multiple repos, for multiple branches and for gerrit patch review system.

LibreOffice tinderboxes status

LibreOffice tinderboxes status

You can see the build status here:

https://tinderbox.libreoffice.org/

7. Fuzz testing: LibreOffice software is checked continuously using Fuzz testing. This is essentially giving various automated inputs to the program to find the possible places in the code where problem occurs. Then, developers will become aware of the those problematic places in the code, and can fix them.

Fuzz Testing LibreOffice

Fuzz testing on LibreOffice source code is active since 2017, and since then there has been various bug fixes for the problems that the fuzz tester reported. You can see more than 1500 of such fixes in the git log until now:

$ git shortlog -s -n --grep=ofz#

Issues Found with Fuzz Testing

This tool can find various different problems. These issues are then filed in a section of Chromium bug tracker, and after ~30 days, they are made public. When developers fix bugs of this kind, they refer to the issue number (for example 321) as ofz#321. A comprehensive list of all issues found is visible here:

Fixing the Issues

Let’s look at one of the fixes. You can find commits related to fuzzing with:

$ git log --grep=ofz

This is a recent fix from Caolán, an experienced LibreOffice developer that provided most of the fixes found through oss-fuzz:

commit d30ecb5fb07f005ebd944e864f0a15678289a4ed
    ofz#69809 Integer-overflow

--- a/filter/source/graphicfilter/icgm/cgm.cxx
+++ b/filter/source/graphicfilter/icgm/cgm.cxx
@@ -227,7 +227,7 @@ double CGM::ImplGetFloat( RealPrecision eRealPrecision, sal_uInt32 nRealSize )
         else
         {
             sal_Int32* pLong = static_cast<sal_Int32*>(pPtr);
-            nRetValue = static_cast(abs( pLong[ nSwitch ] ));
+            nRetValue = fabs(static_cast(pLong[nSwitch]));
             nRetValue *= 65536;
             nVal = static_cast( pLong[ nSwitch ^ 1 ] );
             nVal >>= 16;

As you can see, using abs() first, and then casting to double is changed in this commit to cast to double first, and then using fabs(). The reason of this change lies in the data type of some variables.

pLong is an array of sal_Int32, which is 32 bit signed integer. It can take values from -2,147,483,648 to 2,147,483,647. As you can see, the smallest negative 32-bit signed integer can not be stored in the same 32-bit signed variable if abs() is used to remove sign from that.

As the result is stored in nRetValue, a varible of type double, it is possible to first cast the array item to double, and then use floating point version of absolute function, fabs() over it. In this way, “integer overflow” will not happen anymore.

This patch was one of the smallest examples of what a fix can be. There are many bugs that are more complex, and require more careful examination to provide a fix.

Further Reading

You can read more in the TDF Wiki article around fuzz testing in LibreOffice. Also, OSS-Fuzz documentation is a good place to look into: