Showing posts with label To Develop UNO Extensions (LibreOffice Extensions or Apache OpenOffice Extensions). Show all posts
Showing posts with label To Develop UNO Extensions (LibreOffice Extensions or Apache OpenOffice Extensions). Show all posts

2018-01-07

38: We Begin to List Dispatch Command Specifications

<The previous article in this series | The table of contents of this series |

Main Body START

Here Is a Link to a List (Under Construction) of Dispatch Command Specifications That Can Be Used for Invoking Dispatch Commands

About: UNO (Universal Network Objects)

About: LibreOffice

About: Apache OpenOffice

About: Java Programming Language

About: Java Programming Language

About: LibreOffice Basic Programming Language

Necessity for a List of Dispatch Command Specifications That Can Be Used for Invoking Dispatch Commands

Here are -Hypothesizer and -Rebutter sitting in front of a computer screen in a room on a brook among some mountains on the Bias planet.

-Hypothesizer

As we investigate how to trace the movement of the current cell, a haunting suspicion is "Aren't there some dispatch commands we can use for this purpose?" However, as we can't find any satisfactory list of dispatch command specifications, that suspicion is left haunting us.

-Rebutter

Satisfactory?

-Hypothesizer

Well, there is a list of dispatch commands, but that isn't satisfactory . . . for our purposes.

-Rebutter

Let me see that list?

-Hypothesizer

Here it is.

-Hypothesizer shows the document in a web browser. -Rebutter scans the document.

-Rebutter

'Commands that are accessible through the GUI'?

-Hypothesizer

That seems to imply that commands that can't be accessed through the GUI aren't included, which is a reason of its being unsatisfactory . . . for our purposes: whether a command can be accessed through the GUI or not isn't any our concern.

-Rebutter

The column titles of the list are 'Commands', 'GUI Elements', 'Internal definitions of commands ID', 'Short Description', and 'ID' . . .

Why aren't the first characters of 'definitions' and 'commands' capitalized?

-Hypothesizer

How should I know?

-Rebutter

Does 'Commands' mean URLs?

-Hypothesizer

Actually, the 'Command' prefixed by '.uno:' is the URL.

-Rebutter

There is a dispatch command, 'StatusDocPos', whose 'GUI Elements' is 'S'. What does that mean?

-Hypothesizer

'S' seems to mean the status bar.

-Rebutter

I know that, but how can we invoke the 'StatusDocPos' command from the status bar?

-Hypothesizer

Well, I guess, we can't invoke that command from the status bar, but the result of an internal invocation of the command is displayed on the status bar.

-Rebutter

I wouldn't say that such a command is "accessible through the GUI", but that results of such a command is accessible.

-Hypothesizer

Whatever.

-Rebutter

And how are 'Internal definitions of commands ID' and 'ID' different?

-Hypothesizer

I guess, what they mean is this, for example.

@C++ Source Code
#define SID_AVMEDIA_PLAYER 6694

-Rebutter

So, does 'ID' also mean 'commands ID'?

-Hypothesizer

That's my guess.

-Rebutter

I wondered, "ID? ID of what?"

-Hypothesizer

I guess, the writer thought, "As I said it 'commands ID' once, I will be allowed to leave the qualification, 'commands', out hereafter."

-Rebutter

I thought, "As one is called 'commands ID' and the other is called just 'ID', the two must be different things."

-Hypothesizer

Well, I understand that misunderstandings happen that way.

-Rebutter

And 'Short Description'? Aren't these short descriptions too short?

-Hypothesizer

Well . . .

-Rebutter

For example, 'Absolute Record'? What is an absolute record? What does the command do with an absolute record?

-Hypothesizer

Well, that list is really an internal document, which isn't supposed to be understood by general users.

-Rebutter

Not knowing what are absolute records may be just our insufficiency of knowledge, but I wonder, how do internal people know what does the command do with an absolute record . . .

-Hypothesizer

Probably, that's obvious for internal people who share a certain mindset.

Anyway, that list is written in a secret language for internal people, which is another reason why that list is unsatisfactory . . . for our purposes, and as there is no explanation of command arguments, we can't call commands based on that list, which is the biggest reason why that list is unsatisfactory . . . for our purposes.

-Rebutter

And isn't that list old?

-Hypothesizer

Judging from the title of the document, it's old, which is another reason why that list is unsatisfactory . . . for our purposes.

-Rebutter

Does the nonexistence of any list that is indispensable for calling dispatch commands imply that we aren't supposed to call dispatch commands?

-Hypothesizer

I wonder. We might be supposed to know specifications of dispatch commands by reading source files.

Anyway, As we try to use some functionality of LibreOffice from our programs, we have to know specifications of dispatch commands: as we discussed before, we can access only part of the functionality of LibreOffice in the way we have mostly relied on (getting references to UNO objects and calling methods of those UNO objects). The dispatch mechanism is the other way of accessing the functionality of LibreOffice.

-Rebutter

We can use the 'macro recording' feature to know some information of some dispatch commands, can't we?

-Hypothesizer

Some . . . yes. However, that feature isn't particularly perfect.

-Rebutter

How 'not particularly perfect' is it?

-Hypothesizer

It does not record every possible command, not record every possible command argument, or not explain command arguments.

-Rebutter

Ah, the last part is undeniable.

-Hypothesizer

We want to know what each command argument means and what are options for each command argument, but we can't do that by a single macro recording.

-Rebutter

Can we do that by multiple recordings?

-Hypothesizer

In some cases, we would be able to do that by recording all of the possible argument patterns, but maybe, the GUI doesn't represent all the possibilities. And if we would be able to do that, that would be a tedious task.

-Rebutter

And you say, not every command or command argument is recordable?

-Hypothesizer

Yes, I say so, based on some tries. It seems that arguments that can't be prepared easily or commands that require such arguments won't be recorded.

-Rebutter

Ah, an argument may not be a string or a number, but a UNO object that can't be gotten easily.

-Hypothesizer

An example is the certificate argument of the command that exports the specified document as a PDF file.

We Begin to List Dispatch Command Specifications

The same with the previous scene.

-Hypothesizer

So, we need a list of dispatch command specifications, and we will have to list them ourselves.

-Rebutter

You say "we will have to", but there are very many commands, according to that list. Are we going to list all the commands?

-Hypothesizer scans that list.

-Hypothesizer

. . . As we are dealing with only Calc right now, first we will concentrate on commands that can be invoked on Calc.

-Rebutter

All commands for Calc?

-Hypothesizer scans that list, again.

-Hypothesizer

. . . Honestly speaking, there are many commands that we will never be interested in. In fact, we aren't interested in commands that do what we can do by handling UNO objects.

-Rebutter

For example?

-Hypothesizer

For example, . . . 'Add'.

-Rebutter

What a vague name is that? Add what?

-Hypothesizer

The 'Short Description' says, "Append Sheet", which I understand as "This command appends a spread sheet."

-Rebutter

So, we aren't interested in that command because we can append spread sheets by handling UNO objects?

-Hypothesizer

Exactly. We might list also such commands in the future, but their priorities are low.

So, we don't declare that we are going to list all the commands, but will pick up commands that interest us, one by one.

-Rebutter

Will we pick them up based on the very 'Short Description's of that list?

-Hypothesizer

Actually, we won't rely much on that list. That list is old and only includes commands that are accessible through the GUI. We will have to pick up URLs from a source file. As we have to wildly infer what those commands do, some commands picked up that way may turn out to be useless for our purposes.

-Rebutter

What will be our first command?

-Hypothesizer

Let's begin with a command we already know: '.uno:GoToCell'.

-Rebutter

Where will be our list?

-Hypothesizer

Here. From each command in the list, we can go to the command specifications page, which explains command arguments and the return.

Main Body END

References

  • Apache OpenOffice Wiki. (2010/12/13). Framework. Retrieved from https://wiki.openoffice.org/wiki/Framework

<The previous article in this series | The table of contents of this series |

2017-12-31

37: How to Listen to Spread Sheet Mouse Click Events and Keystroke Events

<The previous article in this series | The table of contents of this series | The next article in this series>

Main Body START

How to Implement and Register Listeners of Spread Sheet Mouse Click Events and Keystroke Events in LibreOffice or Apache OpenOffice Is Explained

About: UNO (Universal Network Objects)

About: LibreOffice

About: Apache OpenOffice

About: Java Programming Language

Necessity for Listening to Spread Sheet Mouse Click Events and Keystroke Events

Here are -Hypothesizer and -Rebutter sitting in front of a computer screen in a room on a brook among some mountains on the Bias planet.

-Hypothesizer

We need to listen to spread sheet mouse click events and keystroke events, because we are using spread sheets as GUI components of our applications. That is, we aren't just handling spread sheet documents as files.

-Rebutter

We won't necessarily need to listen to those events because we are using spread sheets as GUI components . . .

-Hypothesizer

Well, we may not necessarily need to do so, but without doing so, the user interfaces of some of our applications can't avoid being very frustrating.

-Rebutter

Some? What some?

-Hypothesizer

I think, a user interface is frustrating when it requires the user to do what, the user feels, shouldn't be required. I mean, by requiring to push a button after the user clicks a cell, the application can know the occurrence of the click, but the user would feel, "Why should I be required to push the damn button just for notifying the damn application my click? I already clearly showed my intention by clicking the damn cell!"

-Rebutter

Is your user ill-bred, using a word like 'damn' three times in just two sentences?

-Hypothesizer

Actually, the user is nobody but me.

-Rebutter

So, the answer is yes. Anyway, the button or the cell isn't a one to be damned, for they aren't ones to be blamed.

-Hypothesizer

. . . I will retain 'damn' only for the application: the application is damn.

Anyway, in order to push the button, the user has to move the mouse cursor from the cell to the button, often very far, and often he or she has to move the mouse cursor from the button to another cell near the previously clicked cell. And such operations are often repetitive.

-Rebutter

In the first place, why are we using spread sheets as GUI components?

-Hypothesizer

Because the spread sheet user interface of LibreOffice is handy: we can set functions into cells, which are automatically refreshed as other cells are changed, can set fonts, colors, etc. into cells or characters, can do spell checks, can merge cells, can easily input serial data, can create charts, . . .. Creating our own user interface that parallels the spread sheet user interface of LibreOffice wouldn't be insignificant task. So, we want to take advantage of the spread sheet user interface of LibreOffice, and incorporate our logics into the user interface.

-Rebutter

In fact, don't we want to listen to events of the current cell movement, not mouse click events or keystroke events themselves?

-Hypothesizer

Yes. Our present concern is to listen to events of the current cell movement, but regrettably, we can't find out how to directly do so. We want to listen to mouse click events and keystroke events as a way to infer the current cell movement.

How to Implement and Register Listeners of Spread Sheet Mouse Click Events and Keystroke Events

The same with the previous scene.

-Hypothesizer

As for mouse click events, the controller implements an interface, 'com.sun.star.sheet.XEnhancedMouseClickBroadcaster', that accepts listeners that implement 'com.sun.star.awt.XEnhancedMouseClickHandler'; as for keystroke events, the controller implements an interface, 'com.sun.star.awt.XUserInputInterception', that accepts listeners that implement 'com.sun.star.awt.XKeyHandler'.

This time, we will make the 'thebiasplanet.unoextensiontests.controllers.TestController' class of the 'testUnoExtensionToDisclose' project the listener of both kinds of events, like this.

-Hypothesizer edits the 'thebiasplanet.unoextensiontests.controllers.TestController' class (he used their utility classes, 'thebiasplanet.unoutilities.documentshandling.spreadsheetsdocumentshandling.UnoSpreadSheet', 'thebiasplanet.unoutilities.documentshandling.spreadsheetsdocumentshandling.UnoSpreadSheetsDocument', and 'thebiasplanet.coreutilities.messaging.Publisher'; how to get the reference to the controller is described here; the method, 'appendToFile', appends the specified string into the specified file). -Hypothesizer also added a button, 'Set the Events Listeners', into the control panel.

@Java Source Code
package thebiasplanet.unoextensiontests.controllers;

~~~ Import directives irrelevant here are omitted. ~~~
import java.net.URL;
import com.sun.star.awt.EnhancedMouseEvent;
import com.sun.star.awt.KeyEvent;
import com.sun.star.awt.XEnhancedMouseClickHandler;
import com.sun.star.awt.XKeyHandler;
import com.sun.star.awt.XUserInputInterception;
import com.sun.star.beans.XPropertySet;
import com.sun.star.lang.EventObject;
import com.sun.star.sheet.XEnhancedMouseClickBroadcaster;
import com.sun.star.sheet.XSpreadsheetView;
import com.sun.star.table.XCell;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import thebiasplanet.coreutilities.constantsgroups.*;
import thebiasplanet.coreutilities.messaging.Publisher;
import thebiasplanet.unoutilities.documentshandling.spreadsheetsdocumentshandling.UnoSpreadSheet;
import thebiasplanet.unoutilities.documentshandling.spreadsheetsdocumentshandling.UnoSpreadSheetsDocument;

public class TestController implements XEnhancedMouseClickHandler, XKeyHandler {
 private XComponentContext i_componentContextInXComponentContext;
 private String i_openedDocumentDirectoryPath;
 
 public TestController (XComponentContext a_componentContextInXComponentContext) {
  i_componentContextInXComponentContext = a_componentContextInXComponentContext;
 }
 
 ~~~ Parts irrelevant here are omitted. ~~~
 
 public void setEventsListeners () throws Exception {
  UnoSpreadSheet l_currentSpreadSheet = UnoSpreadSheet.getCurrentSpreadSheet (i_componentContextInXComponentContext);
  if (l_currentSpreadSheet != null) {
   UnoSpreadSheetsDocument l_currentSpreadSheetsDocument = l_currentSpreadSheet.getSpreadSheetsDocument ();
   XSpreadsheetView l_controllerInXSpreadsheetView = l_currentSpreadSheetsDocument.getControllerInXSpreadsheetView ();
   XEnhancedMouseClickBroadcaster l_controllerInXEnhancedMouseClickBroadcaster = (XEnhancedMouseClickBroadcaster) UnoRuntime.queryInterface (XEnhancedMouseClickBroadcaster.class, l_controllerInXSpreadsheetView);
   l_controllerInXEnhancedMouseClickBroadcaster.addEnhancedMouseClickHandler (this);
   XUserInputInterception l_controllerInXUserInputInterception = (XUserInputInterception) UnoRuntime.queryInterface (XUserInputInterception.class, l_controllerInXSpreadsheetView);
   l_controllerInXUserInputInterception.addKeyHandler (this);
   String l_openedDocumentFilePath = (new URL (l_currentSpreadSheetsDocument.getLocationUrl ())).getFile ();
   i_openedDocumentDirectoryPath = l_openedDocumentFilePath.substring (0, l_openedDocumentFilePath.lastIndexOf ("/"));
  }
 }
 
 private String getCellAbsoluteName (XCell a_cellInXCell) {
  XPropertySet l_cellInXPropertySet = (XPropertySet) UnoRuntime.queryInterface (XPropertySet.class, a_cellInXCell);
  try {
   return (String) l_cellInXPropertySet.getPropertyValue ("AbsoluteName");
  }
  catch (Exception l_exception) {
   // This will never happen.
   return null;
  }
 }
 
 @Override
 public void disposing (EventObject a_event) {
 }
 
 @Override
 public boolean mousePressed (EnhancedMouseEvent a_enhancedMouseEvent) {
  XCell l_mousePressedCellInXCell = (XCell) UnoRuntime.queryInterface (XCell.class, a_enhancedMouseEvent.Target);
  if (l_mousePressedCellInXCell != null) {
   Publisher.appendToFile ( String.format ("%s/%s", i_openedDocumentDirectoryPath, "UnoMouseEvents.txt"), String.format ("MousePressed: on %s, buttons = %d, modifiers = %d, count = %d", getCellAbsoluteName (l_mousePressedCellInXCell), a_enhancedMouseEvent.Buttons, a_enhancedMouseEvent.Modifiers, a_enhancedMouseEvent.ClickCount));
  }
  return true;
 }
 
 @Override
 public boolean  mouseReleased (EnhancedMouseEvent a_enhancedMouseEvent) {
  XCell l_mouseReleasedCellInXCell = (XCell) UnoRuntime.queryInterface (XCell.class, a_enhancedMouseEvent.Target);
  if (l_mouseReleasedCellInXCell != null) {
   Publisher.appendToFile ( String.format ("%s/%s", i_openedDocumentDirectoryPath, "UnoMouseEvents.txt"), String.format ("MouseReleased: on %s, buttons = %d, modifiers = %d, count = %d", getCellAbsoluteName (l_mouseReleasedCellInXCell), a_enhancedMouseEvent.Buttons, a_enhancedMouseEvent.Modifiers, a_enhancedMouseEvent.ClickCount));
  }
  return true;
 }
 
 @Override
 public boolean keyPressed (KeyEvent a_keyEvent) {
  Publisher.appendToFile (String.format ("%s/%s", i_openedDocumentDirectoryPath, "UnoKeyEvents.txt"), String.format ("KeyPressed: code = %d, modifiers = %d", a_keyEvent.KeyCode, a_keyEvent.Modifiers));
  return false;
 }
 
 @Override
 public boolean keyReleased (KeyEvent a_keyEvent) {
  Publisher.appendToFile (String.format ("%s/%s", i_openedDocumentDirectoryPath, "UnoKeyEvents.txt"), String.format ("KeyReleased: code = %d, modifiers = %d", a_keyEvent.KeyCode, a_keyEvent.Modifiers));
  return false;
 }
}

-Hypothesizer

As you can see, from the event arguments of the 'mousePressed' method and the 'mouseReleased' method, we can get the cell on which the mouse event happened.

-Rebutter

What if the mouse click happened not on a cell?

-Hypothesizer

. . . What do you mean? Do you mean that the mouse click happened precisely on the border between two cells?

-Rebutter

I don't assume that a mouse click can happen precisely on a border, but I know that a row label box (I don't know the official name of it. I mean a box that shows the row number, by clicking on which we can select the whole row), a column label box (please guess what it is from the explanation of 'row label box'), or the 'select-all-the-cells-box' (what is the official name of it? I guess what I mean will be understood) can be clicked.

-Hypothesizer

Well, . . . let's find out.

-Hypothesizer does some experiments.

-Hypothesizer

. . . Really?

-Hypothesizer

There doesn't seem to be any reason to doubt its being real.

-Hypothesizer

Our listener doesn't hear anything when a row label box, a column label box, or the 'select-all-the-cells-box' is clicked. . . .Such a behavior seems unreasonable, for many people who want to hear cell clicks will also want to hear row label box clicks.

-Rebutter

Is that a problem?

-Hypothesizer

That's a huge, or, I would say, critical problem! As mouse events on row label boxs move the current cell, we have to detect such mouse events in order to infer the movement of the current cell.

-Rebutter

Isn't there a way to infer the movement of the current cell without detecting such mouse events?

-Hypothesizer

There isn't any. I thought of Inferring it from the change of the selection, but that's impossible. For example, there are these two cases in which the changes of the selection are the same but the movements of the current cell are different.

In the first case, we press (and not release) the mouse button on the 9th row label box, drag the mouse cursor to the 7th row, drag back the mouse cursor to the 8th row, and release the button. The changes of the selection are 'A9:AMJ9' -> 'A7:AMJ9' -> 'A8:AMJ9'; the last current cell is at 'A8'.

-Rebutter

At 'A8'? That behavior feels inconsistent with the behavior of when the mouse cursor is dragged over cells: in the latter, the current cell doesn't move as the mouse cursor is dragged.

-Hypothesizer

Certainly, but that's the way it really is.

In the second case, we press (and not release) the mouse button on the 9th row label box, drag the mouse cursor to the 7th row, release the button, and click with the control key pressed on the 7th row label box. The changes of the selection are 'A9:AMJ9' -> 'A7:AMJ9' -> 'A8:AMJ9', the same with the first case; the last current cell is at 'A7', different from the first case.

-Rebutter

Well, when 'select-all-the-cells-box' is clicked, where does the current cell go?

-Hypothesizer

In fact, it doesn't go anywhere.

-Rebutter

That behavior also feels inconsistent with the behavior of when a row or column label box is clicked. Why doesn't the current cell move in the former, while it does in the latter?

-Hypothesizer

I don't know, but that's the way it is.

-Rebutter

Well, isn't there another type of mouse events listener we can use?

-Hypothesizer

Hmm, the interface, 'com.sun.star.awt.XUserInputInterception', also accepts mouse clicks listeners, and the interface, 'XWindow', implemented by the spread sheet window also accepts mouse clicks listeners. . . . I wonder whether those listeners can hear what we want to hear.

-Hypothesizer does some experiments with those listeners, taking some time.

-Hypothesizer

. . . You know what, those listeners can't hear what we want to hear, and especially, the latter doesn't hear anything.

-Rebutter

Why?

-Hypothesizer

I don't know. Probably, events are intercepted and consumed by another listener.

-Rebutter

Well, we don't have to blindly obey the way it is, at least when the way it is is inconsistent or unreasonable, do we?

-Hypothesizer

. . . Being said "blindly obey", as far as the current cell moves, we will have to infer the movement as it does . . .

-Rebutter

I thought we could move the current cell from our programs.

-Hypothesizer

Ah, . . . so, we would control the current cell movement, not just infer it?

-Rebutter

If possible.

-Hypothesizer

In fact, we don't know how to move only the current cell without any side effect: the existing selection will be nullified by our moving the current cell.

-Rebutter

Can't we reselect the previous selection area from our programs?

-Hypothesizer

Well, . . . we could, but that seems too much: it feels like we are sinking into a bottomless swamp, just for rather infrequent cases . . .

-Rebutter

Well, giving up on some infrequent cases can be a viable solution, although it may be somewhat ugly.

-Hypothesizer

I will consider what to do . . .

-Rebutter

As for keystroke events, how can we know the cell that received the keystroke?

-Hypothesizer

As far as I look at the event argument, we seem to not be able to know that.

-Rebutter

Hmm, . . . is that OK?

-Hypothesizer

Well, I thought that if we successfully managed to infer the position of the current cell, that cell would be the cell that received the keystroke. However, that precondition seems to have to be conditional.

-Rebutter

What are those return values of the listener methods?

-Hypothesizer

Actually, there is no explanation for the return value of the 'mousePressed' method or the 'mouseReleased' method in the API document, but the return value of 'false' seems to mean 'not forwarding the event'.

-Rebutter

No explanation at all?

-Hypothesizer

The API document says that the return type is boolean, and that's the whole of the explanation, unless some kind of secret ink is used.

-Rebutter

What exactly will happen if the method returns 'false'?

-Hypothesizer

The current cell won't move to the clicked cell, if, of course, we don't move it from our method. So, the user will feel as though his or her click was ignored.

-Rebutter

Why do we return 'false' for the 'keyPressed' method and the 'keyReleased' method?

-Hypothesizer

Because for those methods, returning 'true' means 'not forwarding the event'.

-Rebutter

So, is the meaning of the return of the 'mousePressed' method opposite to that of the 'keyPressed' method?

-Hypothesizer

I guessed so.

-Rebutter

Are those events fired before the selection change events are fired?

-Hypothesizer

Yes. As we can know from the fact that we can block the movement of the current cell by the return values, at the time when those methods are called, the selection isn't changed yet.

Our test code is included in this zip file, and how to use the zip file is described in this article. Opening the 'TestSpreadSheetsDocumentForExecutingTests.ods' file, clicking the 'Show Test Control Panel' button, and clicking the 'Set the Events Listeners' button will begin listening to events. Events detected will be recorded into the files, 'UnoMouseEvents.txt' or 'UnoKeyEvents.txt', in the 'execution' directory of the 'testUnoExtensionToDisclose' project.

Main Body END

References

  • Apache OpenOffice Wiki. (2014/01/02). Apache OpenOffice Developer's Guide. Retrieved from https://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide

<The previous article in this series | The table of contents of this series | The next article in this series>

2017-12-24

36: How to Navigate Through the Spread Sheet That Has Merged Cells

<The previous article in this series | The table of contents of this series | The next article in this series>

Main Body START

How to Know That the Specified Spread Sheet Cell Is Part of a Group of Merged Cells and Know the Cells Range of the Group in LibreOffice or Apache OpenOffice Is Explained

About: UNO (Universal Network Objects)

About: LibreOffice

About: Apache OpenOffice

About: Java Programming Language

Necessity for Knowing That the Specified Spread Sheet Cell Is Part of a Group of Merged Cells and Knowing the Cells Range of the Group

Here are -Hypothesizer and -Rebutter sitting in front of a computer screen in a room on a brook among some mountains on the Bias planet.

-Hypothesizer

When we try to navigate through a spread sheet, . . .

-Rebutter

Wait. I thought that "How to Listen to Spread Sheet Cells Selection Events and (Possibly) Trace the Current Cell Movement, Part Two" was coming.

-Hypothesizer

Ah, you remember.

-Rebutter

Of course, I remember.

-Hypothesizer

Well, I didn't forget, or dissemble to have forgotten that. I was working on it and faced some additional issues, which we have to solve first.

-Rebutter

So, we are addressing one of those issues in this article?

-Hypothesizer

Yes. Be assured that the "Part Two" will come, eventually.

-Rebutter

I am not in any position to be assured of something baselessly. In fact, you may be hit by a car in three days . . .

-Hypothesizer

. . . Yes, I may. So, I just say here, it is sincerely meant to come.

-Rebutter

Well, . . . OK.

-Hypothesizer

In fact, the issue of this article arose from the preceding article, "How to Listen to Spread Sheet Cells Selection Events and (Possibly) Trace the Current Cell Movement, Part One": in order to infer the movement of the current cell , we have to navigate through the spread sheet, but if the spread sheet has merged cells, it isn't as easy as just incrementing or decrementing the row index or the column index of the current cell.

-Rebutter

When the current cell is 'B8', and 'B8:C9' (meaning that the top leftmost cell is 'B8' and the bottom rightmost cell is 'C9', forming a 2x2 rectangle) cells are merged, the rightward movement of the current cell is to a 'D' column cell.

-Hypothesizer

In order to correctly infer the movement, we have to know that a specific cell, which is 'B8' in this case, is part of a group of merged cells, and know the cells range of the group.

-Rebutter

That issue is relevant more generally than only in an odd enterprise of inferring the movement of the current cell: we can't correctly find neighboring cells without that knowledge.

-Hypothesizer

We usually want to get the 'D8' cell when we want the right cell of the 'B8' cell.

How to Handle a Group of Merged Cells

The same with the previous scene.

-Rebutter

Before we delve into the issue, I wonder how we should handle a group of merged cells.

-Hypothesizer

Well, what is unclear?

-Rebutter

Does the top leftmost cell solely represent the group of merged cells?

-Hypothesizer

That depends on what 'represent' means.

-Rebutter

Supposing that we know that a cells range is a group of merged cells, can we handle the group solely by handling the top leftmost cell? For example, if we set the background color of the top leftmost cell, will the whole background color be set for the cells range of the group?

-Hypothesizer

I thought so, . . . but didn't confirm it. Not being so would mean that each cell in the group can have its own background color, which may be a good thing or a bad thing. . . . Well, we have to do an experiment to find that out.

-Hypothesizer writes a test code in the 'testUnoExtensionToDisclose' project (he added a button, 'Handle Merged Cells', into the control panel), opens the spread sheet document file, 'TestSpreadSheetsDocumentForExecutingTests.ods', and executes the test.

-Hypothesizer

Hmm, this is odd. Only the background color of the top row of the group has changed.

-Rebutter

Huh? Let me see your test code.

-Hypothesizer shows the code in a text editor over the spread sheet window ('UnoSpreadSheet' and 'Publisher' are their utility classes; how to get the current spread sheet is described here).

@Java Source Code
   UnoSpreadSheet l_currentSpreadSheet = UnoSpreadSheet.getCurrentSpreadSheet (i_componentContextInXComponentContext);
   XSpreadsheet l_currentSpreadSheetInXSpreadsheet = l_currentSpreadSheet.getSpreadSheetInXSpreadsheet ();
   XCell l_topLeftmostSpreadSheetCellInXCell = l_currentSpreadSheetInXSpreadsheet.getCellByPosition (1, 7);
   l_topLeftmostSpreadSheetCellInXCell.setValue (1);
   
   // Handling the top leftmost spread sheet cell. BEGINNING
   XPropertySet l_topLeftmostSpreadSheetCellInXPropertySet = (XPropertySet) UnoRuntime.queryInterface (XPropertySet.class, l_topLeftmostSpreadSheetCellInXCell);
   l_topLeftmostSpreadSheetCellInXPropertySet.setPropertyValue ("CellBackColor", Integer.valueOf ( (new Color (0, 255, 0)).getRGB ()));
   l_topLeftmostSpreadSheetCellInXPropertySet.setPropertyValue ("IsCellBackgroundTransparent", Boolean.valueOf (false));
   Publisher.show ("The top leftmost spread sheet cell has been handled.");
   // Handling the top leftmost spread sheet cell. END

-Rebutter

Nothing looks wrong with the test code.

-Hypothesizer hides the text editor, exposing the spread sheet again.

-Hypothesizer

Oh. The background color has spread over the whole of the group. . . . It seems that repainting the area has made the display right.

-Rebutter

That behavior may depend on the environment. I mean, in another environment, the display may immediately become right.

-Hypothesizer

Basically, the top leftmost cell seems to solely represents the group of merged cells, but relying on that may not be desirable.

-Rebutter

What if we handle one of the other cells in the group of merged cells?

-Hypothesizer

Well, you know, we have to do another experiment to find that out. I will set the background color of the bottom rightmost cell of the group.

-Hypothesizer adds a logic into the test code, and executes the test.

@Java Source Code
   // Handling the bottom rightmost spread sheet cell. BEGINNING
   XCell l_bottomRightmostSpreadSheetCellInXCell = l_currentSpreadSheetInXSpreadsheet.getCellByPosition (2, 8);
   l_bottomRightmostSpreadSheetCellInXCell.setValue (4);
   XPropertySet l_bottomRightmostSpreadSheetCellInXPropertySet = (XPropertySet) UnoRuntime.queryInterface (XPropertySet.class, l_bottomRightmostSpreadSheetCellInXCell);
   l_bottomRightmostSpreadSheetCellInXPropertySet.setPropertyValue ("CellBackColor", Integer.valueOf ( (new Color (0, 0, 255)).getRGB ()));
   l_bottomRightmostSpreadSheetCellInXPropertySet.setPropertyValue ("IsCellBackgroundTransparent", Boolean.valueOf (false));
   Publisher.show ("The bottom rightmost spread sheet cell has been handled.");
   // Handling the bottom rightmost spread sheet cell. END

-Hypothesizer

Ah, handling of the other cells seems to be invisible, or is it totally vanished? . . . What if we unmerge the group from this state?

-Hypothesizer unmerges the group on the spread sheet.

-Hypothesizer

Ah, the color has appeared. So, the setting is there, but was invisible while the cell was included in the group.

-Rebutter

When the group of merged cells is selected, what do we get as the selection? The top leftmost cell, the cells range of the group, or something else?

-Hypothesizer

Actually, we get the top leftmost cell.

-Rebutter

In the example above, when we move the current cell from 'A9' to the merged cells group by pushing the right arrow key, is something different about the selection from when we move the current cell from 'A8'?

-Hypothesizer

No, as far as I know. The selection is the top leftmost cell, 'B8', in either case.

-Rebutter

Hmm, I asked that because pushing the left arrow key returns the current cell back to 'A9', not to 'A8'. How can LibreOffice do that?

-Hypothesizer tries some such operations.

-Hypothesizer

Ah, . . . certainly, when I move the current cell from 'A9', it goes back to 'A9'; if from 'A8', back to 'A8'. How is it done? I don't know . . . The selections themselves don't look different; information held somewhere else, for example the user operations history, seems to to be used . . .

-Rebutter

Anyway, remember that behavior when you infer the current cell movement.

-Hypothesizer

I will . . .

How to Know That the Specified Spread Sheet Cell Is Part of a Group of Merged Cells and Know the Cells Range

The same with the previous scene.

-Hypothesizer

First, I expected that every merged cell would have a property that showed the cells range of the group of merged cells, but I couldn't find such a property. In fact, no cell seems to have such a property.

-Rebutter

Hmm.

-Hypothesizer

And the cell UNO component doesn't seem to implement any UNO interface that is useful for our purpose.

-Rebutter

Well . . .

-Rebutter goes over the list of UNO interfaces implemented by the spread sheet cell component.

-Rebutter

Can't we use 'com.sun.star.util.XMergeable'?

-Hypothesizer

That UNO interface seems to be something used for cells ranges: there is a method, 'getIsMerged', but that doesn't seem to be useful for a single cell to know the cells range to which the single cell belongs. You know, the spread sheet cell component implements the UNO interface just because a cell is a kind of cells range.

-Rebutter

So, in order to use the UNO interface for our purpose, first, we have to wildly guess the cells area and then, call the method, 'getIsMerged'?

-Hypothesizer

That's my understanding. You know, such a guess will be too wild, for how many possibilities are there of the cells range?

-Rebutter

Many.

-Hypothesizer

It turned out that there is a thing called 'spread sheet cell cursor', which is described in a reference document.

-Hypothesizer shows the reference document in a browser, and -Rebutter scans it.

-Rebutter

. . . I don't understand this very well. When we create a 'spread sheet cell cursor' specifying a cells range into the 'createCursorByRange' method, is the cells range what the cursor points to, or is it the space in which the cursor, which points to a single cell at a time, moves?

-Hypothesizer

My guess is the former. What are described there are how to change the geometry of the cells range, not how to move a cursor through the cells range. So, the cursor seems to trace cells ranges, not cells.

-Rebutter

So, it is really a cells range cursor, not a cell cursor . . .

-Hypothesizer

Anyway, what we want to do is limited: we create a cursor of a single cell, and fit the cursor to the cells range of the group of merged cells, like this.

@Java Source Code
   // Handling merged spread sheet cells range. BEGINNING
   XSheetCellCursor l_spreadSheetCellsRangeCursor = l_currentSpreadSheetInXSpreadsheet.createCursorByRange ( (XSheetCellRange) UnoRuntime.queryInterface (XSheetCellRange.class, l_topLeftmostSpreadSheetCellInXCell));
   l_spreadSheetCellsRangeCursor.collapseToMergedArea ();
   XPropertySet l_mergedSpreadSheetCellsRangeInXPropertySet = (XPropertySet) UnoRuntime.queryInterface (XPropertySet.class, l_spreadSheetCellsRangeCursor);
   l_mergedSpreadSheetCellsRangeInXPropertySet.setPropertyValue ("CellBackColor", Integer.valueOf ( (new Color (0, 255, 0)).getRGB ()));
   l_mergedSpreadSheetCellsRangeInXPropertySet.setPropertyValue ("IsCellBackgroundTransparent", Boolean.valueOf (false));
   Publisher.show ("The merged spread sheet cells range has been handled.");
   // Handling merged spread sheet cells range. END

-Rebutter

Ah, the method, 'collapseToMergedArea', expands the cursor to the cells range . . .

-Hypothesizer

As the cursor UNO object is a kind of cells range, now, we have what we want.

-Rebutter

Can we use that 'gotoOffset' method to know a neighboring cell, with merged cells automatically considered?

-Hypothesizer

Unfortunately, no. If we offset the merged cells range, 'B8:C9', by one row, the result range will not be 'B10' or perhaps 'B10:C11' as we expect it to be, but 'B9:C10'.

-Rebutter

Hmm.

-Hypothesizer

Our test code is included in this zip file, and how to use the zip file is described in this article.

Main Body END

References

  • Apache OpenOffice Wiki. (2014/01/02). Apache OpenOffice Developer's Guide. Retrieved from https://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide

<The previous article in this series | The table of contents of this series | The next article in this series>

2017-12-17

35: How to Listen to Spread Sheet Cells Selection Events and (Possibly) Trace the Current Cell Movement, Part One

<The previous article in this series | The table of contents of this series | The next article in this series>

Main Body START

How to Listen to Spread Sheet Cells Selection Events and (Possibly) Trace the Current Cell Movement in LibreOffice or Apache OpenOffice Is Explained

About: UNO (Universal Network Objects)

About: LibreOffice

About: Apache OpenOffice

About: Java Programming Language

Necessity for Listening to Cells Selection Events and Clarification Between Selected Cells and the Current Cell

Here are -Hypothesizer and -Rebutter sitting in front of a computer screen in a room on a brook among some mountains on the Bias planet.

-Hypothesizer

When we have created a spread sheet cell editor, we encountered an issue of how to detect changes of cell selection on spread sheets.

-Rebutter

Ah, we wanted our spread sheet cell editor to be able to automatically attach to the current cell as we select a cell on a spread sheet.

-Hypothesizer

From the viewpoint of functionality, we can do without reacting to such events, just requiring users to push a button in order to reattach the cell editor to the currently selected cell, but from the viewpoint of user experience, such a requirement can be frustrating.

-Rebutter

There are some use cases in which we don't want the cell editor to be linked to the current cell, but when the user is in the mode, "the cell editor should be linked to the current cell", . . .

-Hypothesizer

. . . that requirement is frustrating. I mean, in the user's head, they are already linked, but the program refuses to understand that expectation. The user thinks, "Why isn't my intention understood?" He or she feels alienated.

-Rebutter

I don't know whether someone has a sense of alienation just using a piece of software . . .

-Hypothesizer

At least, I feel alienated.

-Rebutter

OK. Talk only of your feelings. Don't assume you know what others feel.

-Hypothesizer

I feel being abused.

-Rebutter

'Abused' will be an overstatement . . .

-Hypothesizer

That's my feeling.

-Rebutter

Well, I can't deny what you feel, but I can say, "You are cranky".

-Hypothesizer

. . . I know.

-Rebutter

By the way, we have been using two terms, 'the current cell' and 'the selected cell', interchangeably, but in fact, multiple cells can be selected at the same time, right?

-Hypothesizer

Ah, . . . that hits us close to home . . . us who are proud of accurate expressions . . .

-Rebutter

Don't be proud of that, or of anything. Just be a student of accurate expressions, or of something.

-Hypothesizer

. . . Yes. All we have to do and can do is to do our best, admit mistakes, and improve . . . So, let's clarify things here.

First, I admit here that I was thinking only of single cell selections. Certainly, I knew that we could select multiple cells, but that wasn't in the scope of our discussions. When we use only simple single cell selections, the selected cell is the current cell. However, in general, there isn't such thing as 'the selected cell', but are multiple selected cells.

-Rebutter

So, what is 'the current cell'?

-Hypothesizer

I don't know whether 'the current cell' is a term universally agreed upon, but I am using the term as the cell that has the focus of key inputs.

-Rebutter

So, even if there are multiple selected cells, there is only one current cell?

-Hypothesizer

That's my understanding.

-Rebutter

Is it also your understanding that the current cell may not be one of the selected cells?

-Hypothesizer

Well, . . . as I far as I understand, it doesn't seem to be my understanding.

-Rebutter

Open a spread sheet document.

-Hypothesizer opens a spread sheet document.

-Rebutter

Select 3x3 rectangle of cells.

-Hypothesizer

Why 3x3?

-Rebutter

. . . It doesn't particularly have to be 3x3; I just thought, without the size being specified, someone who got the direction would be at a loss . . .

-Hypothesizer

OK. 3x3.

-Hypothesizer selects a 3x3 rectangle of cells.

-Rebutter

The top leftmost cell is the current cell, right?

-Hypothesizer

Right. That cell is enclosed in the black borders.

-Rebutter

Now, click the center cell with the control key pushed.

-Hypothesizer

Ah, . . .

How to Listen to Cells Selection Events

The same with the previous scene.

-Hypothesizer

In fact, registering a listener of spread sheet cells selection events, itself, is easy.

-Rebutter

'Itself'?

-Hypothesizer

Well, there is a problem, which I will talk about eventually.

The controller has the UNO interface, 'XSelectionSupplier', to which we can register an events listener of 'XSelectionChangeListener'.

-Rebutter

Does 'the controller' mean that controller we used before?

-Hypothesizer

Yes, I mean that controller.

'XSelectionChangeListener' has a method, 'selectionChanged', which is called when the cells selection has changed.

-Rebutter

The selection information is passed as the argument of the method, I presume?

-Hypothesizer

Actually, it isn't directly passed, but the event source is passed.

-Rebutter

What is the event source, exactly?

-Hypothesizer

The controller itself.

-Rebutter

Well, so we are supposed to get the selection information from the controller?

-Hypothesizer

That's my guess.

-Rebutter

Well, it's fine because we can get the selection information from the controller.

-Hypothesizer

A problem is, the listener method is called four times when we click a cell on a spread sheet.

-Rebutter

. . . Why?

-Hypothesizer

I don't know. I also noted that when we move the selection by pushing an arrow key, the listener method is called only once; when the selection change is caused by an activation of a spread sheet, the listener method is called twice.

-Rebutter

. . . Why?

-Hypothesizer

I don't know.

Anyway, as we don't want to unnecessarily react multiple times, we will have to have a way to ignore unnecessary events.

-Rebutter

I guess so.

-Hypothesizer

My plan is to save the last selection information, and react only when the selection information is changed compared with the last selection information.

-Rebutter

Hmm, in what form will you save the last selection information?

-Hypothesizer

The selection can be a single cell, a single cells range, or a collection of cells ranges, and we will identify a cell by its row and column indexes, a cells range by its top row, leftmost column, bottom row, and rightmost column indexes, and a collection of cells ranges by the identities of all of its element cells ranges.

-Rebutter

I guess, a selection can be split into multiple cells ranges because the selection may not consist of a consecutive area?

-Hypothesizer

It's not just a matter of consecutiveness. As a cells range is really a cells rectangle, if a selection isn't a rectangle, it will be split into rectangles.

-Rebutter

Does the splitting of a selection into cells ranges depend only on the shape of the selection?

-Hypothesizer

Ah, you seem to be asking whether it depends on the formation process of the selection . . .

-Rebutter

I seem to be asking that. For example, if a selection was first consisted of two rectangles, and is made a big rectangle by adding some cells, will the selection become a single cells range?

-Hypothesizer

Probably . . .. I don't know for sure, but that won't matter in this context.

How to Trace the Current Cell Movement (Not Found, Actually)

The same with the previous scene.

-Hypothesizer

Tracing the current cell movement is a problem . . .

-Rebutter

How is it a problem?

-Hypothesizer

I can't find any listener of such events.

-Rebutter

So, . . . did you give up?

-Hypothesizer

I didn't particularly give up; I was persuing a possibility . . .

-Rebutter

Which is?

-Hypothesizer

I thought that the current cell might have a property value or something that distinguishes itself as the current cell . . .

-Rebutter

The existence of such a property value or something aside, isn't it too resource intensive to find a single cell among so many cells?

-Hypothesizer

Ah, in that respect, it seems unpromising, or I would say, unrealistic.

-Rebutter

You will have to search the spread sheet or the controller for such a property value.

-Hypothesizer

Actually, I did, and couldn't find one. I also thought that there might be another UNO object accessible to us that provides a way to such a value, but I couldn't find one. You know, the reference documents aren't so intensive . . .

-Rebutter

Well, when a cell is clicked, does the cell always become the current cell?

-Hypothesizer

As far as I know, yes.

-Rebutter

Can we detect such clicks?

-Hypothesizer

Yes. So, limited to that particular case, we can detect the movement of the current cell. However, as you of course know, the current cell can also be moved by key strokes.

-Rebutter

Can we also detect key strokes?

-Hypothesizer

Yes.

-Rebutter

Are there other sources that can move the current cell? Psychokinesis? A strong belief or a will to power?

-Hypothesizer

As far as I am concerned, clicks and key strokes are the only sources.

-Rebutter

Hmm, . . .

-Hypothesizer

Are you suggesting that we could infer the current cell movement from clicks and key strokes, not detecting the current cell movement?

-Rebutter

I'm not suggesting anything.

By the way, what if we keep pushing the return key for a while? Can we trust that the number of key strokes we receive is the number of key strokes the LibreOffice program processes?

-Hypothesizer

Well, . . . I don't know. I don't see a better way than we just try . . .. What are you suggesting?

-Rebutter

None at all.

-Hypothesizer

. . .

Main Body END

<The previous article in this series | The table of contents of this series | The next article in this series>