Easyhacking: How to make a feature optional

Working on the user experience often means making a feature optional. Some users may need a feature while others may find it distracting. An example is the tooltip shown on tracked changes.

Overlapping tooltips as reported in tdf#114523

Figure 1: Overlapping tooltips as reported in tdf#114523.

While this tooltip is useful for the review, it just overlaps the text when editing, as long the information is not shown in the document margin (tdf#34355) or if you don’t use the Track Changes deck in the sidebar for some reason. So let’s try to make an easy hack out of this.

On the search for the code pointer

Modifying any feature about Track Changes sounds like a tough nut. Luckily, there are fixed strings in the tooltip, Inserted or Deleted or Formatted, that we can use in a search (learn more in Easyhacking: How to set up your environment > Codepointer). These terms appear too many times in the source code, of course, but since the text is localized we can restrict the search to strings.hrc, and looking through the five results there is only one without additional text: STR_REDLINE_FORMAT. Taking this variable into a new search we find edtwin2.cxx where lcl_GetRedlineHelp() composes the tooltip. And this helper function is called at SwEditWin::RequestHelp() when the focused content matches IsAttrAtPos::Redline. To verify, you simply comment out sText=lcl_GetRedlineHelp(...) – and that tooltips on tracked changes are gone.

Changing the UI

We start by adding the checkbox to the user interface. Some options to tweak the visualization are there under Tools > Options > Writer > View, which is a good place for our option. Searching for “Helplines _While Moving” reveals the right .ui file: sw/uiconfig/swriter/ui/viewoptionspage.ui.

Adding a checkbox in Glade.

Figure 2: Adding a checkbox in Glade.

We extend the number of rows of the GtkGrid grid3 to 6 and drop a GtkCheckButton onto the free space. Now we change the ID to “changestooltip”, in order to easily identify the object in the code, and set its label to “_Tooltips on tracked changes (the underscore is the mnemonic) and add “viewoptionspage|changes- tooltip at “Context for translation” in the Edit Text dialog. That’s all.

Access to the control

Searching for viewoptionspage.ui returns the file where these options are read: sw/source/ui/config/optpage.cxx. And we just have to follow how other controls are accessed.

First, we add the variable to the header file sw/source/uibase/inc/optpage.hxx

class SwContentOptPage : public SfxTabPage
{...
   VclPtr<CheckBox>  m_pShowInlineTooltips;

and assign this variable to the control in the constructor at the C++ source

SwContentOptPage::SwContentOptPage( vcl::Window* pParent,<br>
                                    const SfxItemSet& rCoreSet ) :
    SfxTabPage(pParent, "ViewOptionsPage",
               "modules/swriter/ui/viewoptionspage.ui", &rCoreSet)
{ ...
   get (m_pShowInlineTooltips,"changestooltip");

And never forget to clean-up what you assign

void SwContentOptPage::dispose()
{...
   m_pShowInlineTooltips.clear();

Making the property public

Looking through the other methods of the SwContentOptPage class, it becomes clear that all settings are part of SwElemItem. So we first add our variable at Reset() and FillItemSet(), like the other options have done.

At the class SwElemItem in we introduce a new variable — bShowInlineTooltips —and follow how bNotes is handled including

void SwElemItem::FillViewOptions( SwViewOption& rVOpt) const
{
   ...
   rVOpt.SetShowInlineTooltips( bShowInlineTooltips );
}

In the referenced class SwViewOption, we add two functions similar to SetPostIts()

bool IsShowInlineTooltips() const
    { return bool(m_nCoreOptions & ViewOptFlags1::ShowInlineTooltips); }
void SetShowInlineTooltips( bool b )
    { b ? (m_nCoreOptions |= ViewOptFlags1::ShowInlineTooltips ) :
      ( m_nCoreOptions &= ~ViewOptFlags1::ShowInlineTooltips); }

and extend the enum of ViewOptFlags1 by ShowInlineTooltips.

ShowInlineTooltips = 0x10000000,

The template below sums up all enums and needs to get adjusted too, from 0x67dfcdfe to 0x77dfcdfe.

The newly introduced property SwViewOption::IsShowInlineTooltips can now be used at the actual function in sw/source/uibase/docvw/edtwin2.cxx

case IsAttrAtPos::Redline:
{
   const bool bShowInlineTooltips = rSh.GetViewOptions()->IsShowInlineTooltips();
   if ( bShowTrackChanges )
     sText = lcl_GetRedlineHelp(*aContentAtPos.aFnd.pRedl, bBalloon);
}

Storing the setting

And it’s still not enough. While these modifications may work, the user setting wouldn’t be stored. Searching for IsPostIts returns sw/source/uibase/config/usrpref.cxx where we have to add read/write functionality for the new option.

Sequence<OUString> SwContentViewConfig::GetPropertyNames()
{  ...
   static const char* aPropNames[] =
   { ...
     "Display/ShowInlineTooltips"            //19

void SwContentViewConfig::ImplCommit()
{  ...
   case 19: bVal = rParent.IsShowInlineTooltips(); break;

void SwContentViewConfig::Load()
{ ...
  case 19: rParent.SetShowInlineTooltips(bSet); break;

Conclusion

On the one hand, it’s surprisingly complex how a simple option is implemented. And there is always room for beautification, for example, by providing a UNO command that makes customization possible (implemented and available with release 6.1). But otherwise it’s also not a big deal to copycat what the experts have implemented in the past, and the second time you will quickly make the patch that makes thousands of people happy.

So what are you waiting for? Start hacking now!

Comments
  1. 6 years ago