SuperbChemistry 3.1
[superbchemistry/superbchemistry.git] / extension / SuperbChemistry / Main.xba
CommitLineData
071359bb
MM
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
2eec932f
MM
3<script:module xmlns:script="http://openoffice.org/2000/script" script:name="Main" script:language="StarBasic">Option Explicit
4
5Function HaveSelection(doc As Object)
6 Dim sel as Object
7 sel = doc.CurrentController.Selection
8 If sel.Count &gt; 1 Then
9 HaveSelection = True
10 Exit Function
11 End If
12 Dim s0
13 s0 = sel.getByIndex(0)
14 If s0.Text.compareRegionStarts(s0.Start, s0.End) = 0 Then
15 HaveSelection = False
16 Else
17 HaveSelection = True
18 End If
19End Function
20
21&apos; Regular expression replace in the document, creating superscripts if
22&apos; superb &gt; 0 or subscripts if superb &lt; 0.
23Sub SuperbReplace(doc As Object, searchRegex As String, replacePattern As String, superb As Integer)
24
25 Dim rd As Object
26 rd = doc.createReplaceDescriptor()
27
28 rd.SearchCaseSensitive = true
29 rd.SearchRegularExpression = true
30 rd.setSearchString(searchRegex)
31 rd.setReplaceString(replacePattern)
32
33 If superb &lt;&gt; 0 Then
34 Dim replaceAttrs(1) As New com.sun.star.beans.PropertyValue
35 replaceAttrs(0).Name = &quot;CharEscapement&quot;
36 If superb &gt; 0 Then
37 replaceAttrs(0).Value = 33
38 Else
39 &apos; The default escapement for subscripts is -33, which looked bad to me in
40 &apos; chemical formulas. This looks better. Modify to your taste.
41 replaceAttrs(0).Value = -9
42 End If
43 replaceAttrs(1).Name = &quot;CharEscapementHeight&quot;
44 replaceAttrs(1).Value = 58
45 rd.setReplaceAttributes(replaceAttrs)
46 End If
47
48 doc.replaceAll(rd)
49
50End Sub
51
52Sub ReplaceInSelection(doc As Object, searchRegex As String, replacePattern As String)
53
54 Dim frame As Object, dispatcher As Object
55 frame = doc.CurrentController.Frame
56 dispatcher = createUnoService(&quot;com.sun.star.frame.DispatchHelper&quot;)
57
58 Dim args(6) As New com.sun.star.beans.PropertyValue
59 args(0).Name = &quot;SearchItem.AlgorithmType&quot;
60 args(0).Value = 1
61 args(1).Name = &quot;SearchItem.SearchFlags&quot;
62 args(1).Value = &amp;H1800 &apos; Search in selection
63 args(2).Name = &quot;SearchItem.SearchString&quot;
64 args(2).Value = searchRegex
65 args(3).Name = &quot;SearchItem.ReplaceString&quot;
66 args(3).Value = replacePattern
67 args(4).Name = &quot;SearchItem.Command&quot;
68 args(4).Value = 3
69 args(5).Name = &quot;SearchItem.AlgorithmType2&quot;
70 args(5).Value = 2
71 args(6).Name = &quot;Quiet&quot;
72 args(6).Value = true
73
74 dispatcher.executeDispatch(frame, &quot;.uno:ExecuteSearch&quot;, &quot;&quot;, 0, args())
75
76End Sub
77
78Global formatSelectionWarningShown As Boolean
79&apos; I haven&apos;t found a way to initialize the variable, but it looks like its
80&apos; default value is treated as false in an if statement.
81&apos;formatSelectionWarningShown = False
82
83Sub FormatSelectionOrDocumentDebug()
84 &apos; Replacing with an empty replacement string triggers a bug in LibreOffice
85 &apos; (https://bugs.documentfoundation.org/show_bug.cgi?id=136577), so we must
86 &apos; avoid it. Fortunately, avoiding it is pretty straightforward.
87
db7363ac
MM
88 &apos; Step 1: Insert @m@ between an item and a number or charge that may be part of
89 &apos; a chemical formula (subject to later checks).
2eec932f
MM
90 If HaveSelection(ThisComponent) Then
91 &apos; doc.replaceAll is not capable of searching a selection, while the
92 &apos; dispatch-based replace API uses the current format options in the
93 &apos; &quot;Find &amp; Replace&quot; dialog and does not let us change them, which means that
94 &apos; (1) existing settings can break things and (2) we cannot make superscripts
95 &apos; and subscripts. We do the best we can by using one dispatch-based replace
96 &apos; to tag all the sequences we want to operate on (which loses the selection in
97 &apos; OpenOffice anyway) and then proceed with replaceAll calls on the whole
98 &apos; document, which will only operate on the already tagged sequences.
99 If Not formatSelectionWarningShown Then
100 MsgBox &quot;Due to limitations in the OpenOffice/LibreOffice API, the &quot; &amp; _
101 &quot;&quot;&quot;Format selection&quot;&quot; command may not process some chemical formulas &quot; &amp; _
102 &quot;in the selection or may apply incorrect formatting if any format &quot; &amp; _
103 &quot;options are active in the &quot;&quot;Find &amp; Replace&quot;&quot; dialog box. If this &quot; &amp; _
104 &quot;happens, just undo the command (if any changes were made), clear the &quot; &amp; _
105 &quot;format options in the &quot;&quot;Find &amp; Replace&quot;&quot; dialog box (focus the &quot; &amp; _
106 &quot;&quot;&quot;Find&quot;&quot; field, click &quot;&quot;No Format&quot;&quot;, and repeat for the &quot;&quot;Replace&quot;&quot; &quot; &amp; _
107 &quot;field), and run &quot;&quot;Format selection&quot;&quot; again.&quot; &amp; Chr$(13) &amp; Chr$(13) &amp; _
108 &quot;This message is always shown on the first &quot;&quot;Format selection&quot;&quot; &quot; &amp; _
109 &quot;command in each OpenOffice/LibreOffice session because &quot; &amp; _
110 &quot;SuperbChemistry has no way to detect whether format options are &quot; &amp; _
111 &quot;active in &quot;&quot;Find &amp; Replace&quot;&quot;.&quot;, _
112 0, &quot;SuperbChemistry &quot;&quot;Format selection&quot;&quot; notice&quot;
113 formatSelectionWarningShown = True
114 End If
115 ReplaceInSelection(ThisComponent, &quot;(?&lt;=[A-Z][a-z]?|[\])}])[-+−0-9]+&quot;, &quot;@m@&amp;&quot;)
116 Else
117 SuperbReplace(ThisComponent, &quot;(?&lt;=[A-Z][a-z]?|[\])}])[-+−0-9]+&quot;, &quot;@m@&amp;&quot;, 0)
118 End If
119
db7363ac
MM
120 &apos; Step 2: Insert @c@ after a charge symbol, if it&apos;s followed by one of the
121 &apos; allowed characters for the second kind of &quot;recognized sequence&quot; described in
122 &apos; the readme.
2eec932f
MM
123 SuperbReplace(ThisComponent, &quot;(?&lt;=@m@)([0-9]*[-+−])(?=[ \t\])}.,:;?!&apos;&quot;&quot;]|$)&quot;, &quot;&amp;@c@&quot;, 0)
124
db7363ac 125 &apos; Step 3: Real minus signs in charges.
2eec932f
MM
126 SuperbReplace(ThisComponent, &quot;-@c@&quot;, &quot;−@c@&quot;, 0)
127
db7363ac
MM
128 &apos; Step 4: Some groups grab a single following digit as a quantity rather than a
129 &apos; charge amount. Insert @sq@ marker to prevent the charge from grabbing the
130 &apos; digit.
2eec932f
MM
131 SuperbReplace(ThisComponent, &quot;(?&lt;=(H|O|F|Cl|Br|I|[\])}])@m@)[0-9]&quot;, &quot;&amp;@sq@&quot;, 0)
132
db7363ac
MM
133 &apos; Step 5: Each charge grabs at most one digit and moves the @c@ in front to
134 &apos; prevent the quantity from grabbing the digit.
2eec932f
MM
135 SuperbReplace(ThisComponent, &quot;([0-9]?[−+])@c@&quot;, &quot;@c@$1&quot;, 1)
136
db7363ac
MM
137 &apos; Step 6: Remove any @sq@ markers so items can grab all the digits that follow
138 &apos; for the quantity.
2eec932f
MM
139 SuperbReplace(ThisComponent, &quot;(.)@sq@&quot;, &quot;$1&quot;, 0)
140
141 &apos; At this point, we have only @m@ and @c@ markers left.
142
db7363ac
MM
143 &apos; Step 7: Format quantities: as many digits as we can still grab. The digits
144 &apos; must be followed by one of the allowed characters for the first kind of
145 &apos; &quot;recognized sequence&quot; described by a readme or by @, which we assume is part
146 &apos; of a @c@ tag we added in step 2. The allowed characters A-Z\[({ represent
147 &apos; the beginning of another item; the other allowed characters are the same as
148 &apos; in step 2.
149 SuperbReplace(ThisComponent, &quot;(?&lt;=@m@)[0-9]+(?=[@A-Z\[({ \t\])}.,:;?!&apos;&quot;&quot;]|$)&quot;, &quot;&amp;&quot;, -1)
2eec932f 150
db7363ac 151 &apos; Step 8: Clean up @c@ markers. We know there is a charge sign after each.
2eec932f
MM
152 SuperbReplace(ThisComponent, &quot;@c@(.)&quot;, &quot;$1&quot;, 0)
153
db7363ac 154 &apos; Step 9: Clean up @m@ markers. We know there is some character before each.
2eec932f
MM
155 SuperbReplace(ThisComponent, &quot;(.)@m@&quot;, &quot;$1&quot;, 0)
156
157End Sub
158
159Dim madeChanges As Boolean
160
161Sub UndoListener_undoActionAdded()
162End Sub
163Sub UndoListener_actionUndone()
164End Sub
165Sub UndoListener_actionRedone()
166End Sub
167Sub UndoListener_allActionsCleared()
168End Sub
169Sub UndoListener_redoActionsCleared()
170End Sub
171Sub UndoListener_resetAll()
172End Sub
173Sub UndoListener_enteredContext()
174End Sub
175Sub UndoListener_enteredHiddenContext()
176End Sub
177Sub UndoListener_leftContext()
178 madeChanges = True
179End Sub
180Sub UndoListener_leftHiddenContext()
181End Sub
182Sub UndoListener_cancelledContext()
183End Sub
184
185Sub FormatSelectionOrDocument()
186
187 Dim undoActionName As String
188 If HaveSelection(ThisComponent) Then
189 undoActionName = &quot;SuperbChemistry: Format selection&quot;
190 Else
191 undoActionName = &quot;SuperbChemistry: Format document&quot;
192 End If
193 ThisComponent.UndoManager.enterUndoContext(undoActionName)
194
195 On Error Goto ErrorHandler
196 FormatSelectionOrDocumentDebug
197 On Error Goto 0
198
199 ThisComponent.UndoManager.leaveUndoContext()
200 Exit Sub
201
202ErrorHandler:
203
204 &apos; If our undo context is nonempty, we want to undo the generated action.
205 &apos; If not, we do not want to undo as that would undo the user&apos;s previous
206 &apos; action. If we just check whether the title of the last undoable action
207 &apos; is &quot;SuperbChemistry: Format (selection|document)&quot;, that might be wrong if
208 &apos; the user ran FormatDocument twice in a row: probably unlikely, but the
209 &apos; completely correct check is not that hard.
210 Dim listener As Object
211 listener = CreateUnoListener(&quot;UndoListener_&quot;, &quot;com.sun.star.document.XUndoManagerListener&quot;)
212 madeChanges = False
213 ThisComponent.UndoManager.addUndoManagerListener(listener)
214 ThisComponent.UndoManager.leaveUndoContext()
215 ThisComponent.UndoManager.removeUndoManagerListener(listener)
216 If madeChanges Then
217 ThisComponent.UndoManager.undo()
218 ThisComponent.UndoManager.clearRedo()
219 End If
220
221 MsgBox &quot;SuperbChemistry encountered an unexpected error:&quot; &amp; Chr$(13) &amp; Chr$(13) &amp; _
222 &quot;Code &quot; &amp; Err &amp; &quot;: &quot; &amp; Error$ &amp; Chr$(13) &amp; Chr$(13) &amp; _
223 &quot;Any changes made so far have been undone.&quot; &amp; Chr$(13) &amp; Chr$(13) &amp; _
224 &quot;SuperbChemistry needed to catch the error in order to leave your undo &quot; &amp; _
225 &quot;history in a consistent state. If the problem is reproducible and you &quot; &amp; _
05bf78df 226 &quot;want to see the precise error location, run the FormatSelectionOrDocumentDebug &quot; &amp; _
2eec932f
MM
227 &quot;macro, but be advised that it may generate multiple entries in the undo &quot; &amp; _
228 &quot;history and will not undo them on error.&quot;, _
229 0, &quot;SuperbChemistry internal error&quot;
230 On Error Goto 0
231End Sub
071359bb
MM
232
233</script:module>