Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/sc/source/ui/inc/AccessibleCsvControl.hxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#pragma once
21
22
#include <memory>
23
#include <com/sun/star/accessibility/AccessibleRole.hpp>
24
#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
25
#include <com/sun/star/accessibility/XAccessibleText.hpp>
26
#include <com/sun/star/accessibility/XAccessibleTable.hpp>
27
#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
28
#include <tools/gen.hxx>
29
#include <rtl/ref.hxx>
30
#include <rtl/ustrbuf.hxx>
31
#include <comphelper/OAccessible.hxx>
32
#include <cppuhelper/implbase.hxx>
33
#include <editeng/AccessibleStaticTextBase.hxx>
34
#include <comphelper/uno3.hxx>
35
#include <map>
36
37
class ScCsvControl;
38
39
/** Accessible base class used for CSV controls. */
40
class ScAccessibleCsvControl : public comphelper::OAccessible
41
{
42
private:
43
    ScCsvControl*               mpControl;          /// Pointer to the VCL control.
44
45
public:
46
    explicit ScAccessibleCsvControl(ScCsvControl& rControl);
47
    virtual ~ScAccessibleCsvControl() override;
48
49
    virtual void SAL_CALL disposing() override;
50
51
    virtual void SAL_CALL grabFocus(  ) override;
52
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
53
54
    // events -----------------------------------------------------------------
55
public:
56
    /** Sends a GetFocus or LoseFocus event to all listeners. */
57
    virtual void SendFocusEvent( bool bFocused );
58
    /** Sends a caret changed event to all listeners. */
59
    virtual void SendCaretEvent();
60
    /** Sends a visible area changed event to all listeners. */
61
    void SendVisibleEvent();
62
    /** Sends a selection changed event to all listeners. */
63
    void SendSelectionEvent();
64
    /** Sends a table model changed event for changed cell contents to all listeners. */
65
    virtual void SendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows );
66
    /** Sends a table model changed event for an inserted column to all listeners. */
67
    virtual void SendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn );
68
    /** Sends a table model changed event for a removed column to all listeners. */
69
    virtual void SendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn );
70
71
    // helpers ----------------------------------------------------------------
72
protected:
73
    virtual css::awt::Rectangle implGetBounds() override;
74
75
    /** Returns the VCL control. Assumes a living object. */
76
    ScCsvControl& implGetControl() const;
77
78
    /** Creates a StateSet and fills it with DEFUNC, OPAQUE, ENABLED, SHOWING and VISIBLE. */
79
    sal_Int64 implCreateStateSet();
80
};
81
82
class ScCsvRuler;
83
84
/** Accessible class representing the CSV ruler control. */
85
class ScAccessibleCsvRuler : public cppu::ImplInheritanceHelper<ScAccessibleCsvControl,
86
                                                                css::accessibility::XAccessibleText>
87
{
88
private:
89
    OUStringBuffer       maBuffer;   /// Contains the text representation of the ruler.
90
91
public:
92
    explicit                    ScAccessibleCsvRuler( ScCsvRuler& rRuler );
93
    virtual                     ~ScAccessibleCsvRuler() override;
94
95
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent(  ) override;
96
97
    virtual OUString SAL_CALL getAccessibleDescription(  ) override;
98
    virtual OUString SAL_CALL getAccessibleName(  ) override;
99
100
    virtual sal_Int32 SAL_CALL getForeground(  ) override;
101
102
    virtual sal_Int32 SAL_CALL getBackground(  ) override;
103
104
    // XAccessibleContext -----------------------------------------------------
105
106
    /** Returns the child count (the ruler does not have children). */
107
    virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
108
109
    /** Throws an exception (the ruler does not have children). */
110
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 nIndex ) override;
111
112
0
    virtual sal_Int16 SAL_CALL getAccessibleRole(  ) override { return css::accessibility::AccessibleRole::TEXT; }
113
114
    /** Returns the relation to the grid control. */
115
    virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
116
117
    /** Returns the current set of states. */
118
    virtual sal_Int64  SAL_CALL getAccessibleStateSet() override;
119
120
    // XAccessibleText --------------------------------------------------------
121
122
    /** Return the position of the caret. */
123
    virtual sal_Int32 SAL_CALL getCaretPosition() override;
124
125
    /** Sets the position of the caret. */
126
    virtual sal_Bool SAL_CALL setCaretPosition( sal_Int32 nIndex ) override;
127
128
    /** Returns the specified character. */
129
    virtual sal_Unicode SAL_CALL getCharacter( sal_Int32 nIndex ) override;
130
131
    /** Returns the attributes of the specified character. */
132
    virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
133
134
    /** Returns the screen coordinates of the specified character. */
135
    virtual css::awt::Rectangle SAL_CALL getCharacterBounds( sal_Int32 nIndex ) override;
136
137
    /** Returns the count of characters. */
138
    virtual sal_Int32 SAL_CALL getCharacterCount() override;
139
140
    /** Returns the character index at the specified coordinate (object's coordinate system). */
141
    virtual sal_Int32 SAL_CALL getIndexAtPoint( const css::awt::Point& rPoint ) override;
142
143
    /** Returns the selected text (ruler returns empty string). */
144
    virtual OUString SAL_CALL getSelectedText() override;
145
146
    /** Returns the start index of the selection (ruler returns -1). */
147
    virtual sal_Int32 SAL_CALL getSelectionStart() override;
148
149
    /** Returns the end index of the selection (ruler returns -1). */
150
    virtual sal_Int32 SAL_CALL getSelectionEnd() override;
151
152
    /** Selects a part of the text (ruler does nothing). */
153
    virtual sal_Bool SAL_CALL setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
154
155
    /** Returns the entire text. */
156
    virtual OUString SAL_CALL getText() override;
157
158
    /** Returns the specified range [Start,End) of the text. */
159
    virtual OUString SAL_CALL getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
160
161
    /** Returns the specified text portion. */
162
    virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
163
    virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
164
    virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
165
166
    /** Copies the specified text range into the clipboard (ruler does nothing). */
167
    virtual sal_Bool SAL_CALL copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
168
169
    virtual sal_Bool SAL_CALL scrollSubstringTo( sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override;
170
171
    // events -----------------------------------------------------------------
172
public:
173
    /** Sends a caret changed event to all listeners. */
174
    virtual void SendCaretEvent() override;
175
176
    // helpers ----------------------------------------------------------------
177
private:
178
179
    /** @throws css::lang::IndexOutOfBoundsException if the specified character position is invalid (outside 0..len-1). */
180
    void ensureValidIndex( sal_Int32 nIndex ) const;
181
    /** @throws css::lang::IndexOutOfBoundsException if the specified character position is invalid (outside 0..len). */
182
    void ensureValidIndexWithEnd( sal_Int32 nIndex ) const;
183
    /** @throws css::lang::IndexOutOfBoundsException if the specified character range [Start,End) is invalid.
184
        @descr  If Start>End, swaps Start and End before checking. */
185
    void ensureValidRange( sal_Int32& rnStartIndex, sal_Int32& rnEndIndex ) const;
186
187
    /** Returns the VCL ruler control. Assumes a living object. */
188
    ScCsvRuler& implGetRuler() const;
189
190
    /** Builds the entire string buffer.
191
192
        @throws css::uno::RuntimeException
193
    */
194
    void constructStringBuffer();
195
    /** Returns the character count of the text. */
196
    sal_Int32 implGetTextLength() const;
197
198
    /** Returns true, if the character at the specified index has a split. */
199
    bool implHasSplit( sal_Int32 nApiPos );
200
201
    /** Returns the first character index with equal formatting as at nApiPos. */
202
    sal_Int32 implGetFirstEqualFormatted( sal_Int32 nApiPos );
203
    /** Returns the last character index with equal formatting as at nApiPos. */
204
    sal_Int32 implGetLastEqualFormatted( sal_Int32 nApiPos );
205
};
206
207
class ScCsvGrid;
208
class ScAccessibleCsvCell;
209
210
/** Accessible class representing the CSV grid control. */
211
class ScAccessibleCsvGrid
212
    : public cppu::ImplInheritanceHelper<ScAccessibleCsvControl,
213
                                         css::accessibility::XAccessibleTable,
214
                                         css::accessibility::XAccessibleSelection>
215
{
216
protected:
217
    typedef std::map< sal_Int64, rtl::Reference<ScAccessibleCsvCell> > XAccessibleSet;
218
219
private:
220
    XAccessibleSet maAccessibleChildren;
221
222
public:
223
    explicit                    ScAccessibleCsvGrid( ScCsvGrid& rGrid );
224
    virtual                     ~ScAccessibleCsvGrid() override;
225
    virtual void SAL_CALL       disposing() override;
226
227
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent(  ) override;
228
229
    /** Returns the cell at the specified point. */
230
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& rPoint ) override;
231
232
    virtual OUString SAL_CALL getAccessibleDescription(  ) override;
233
    virtual OUString SAL_CALL getAccessibleName(  ) override;
234
235
    virtual sal_Int32 SAL_CALL getForeground(  ) override;
236
237
    virtual sal_Int32 SAL_CALL getBackground(  ) override;
238
239
0
    virtual sal_Int16 SAL_CALL getAccessibleRole(  ) override { return css::accessibility::AccessibleRole::TABLE; }
240
241
    // XAccessibleContext -----------------------------------------------------
242
243
    /** Returns the child count (count of cells in the table). */
244
    virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
245
246
    /** Returns the specified child cell. */
247
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 nIndex ) override;
248
249
    /** Returns the relation to the ruler control. */
250
    virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
251
252
    /** Returns the current set of states. */
253
    virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
254
255
    // XAccessibleTable -------------------------------------------------------
256
257
    /** Returns the number of rows in the table. */
258
    virtual sal_Int32 SAL_CALL getAccessibleRowCount() override;
259
260
    /** Returns the number of columns in the table. */
261
    virtual sal_Int32 SAL_CALL getAccessibleColumnCount() override;
262
263
    /** Returns the description of the specified row in the table. */
264
    virtual OUString SAL_CALL getAccessibleRowDescription( sal_Int32 nRow ) override;
265
266
    /** Returns the description text of the specified column in the table. */
267
    virtual OUString SAL_CALL getAccessibleColumnDescription( sal_Int32 nColumn ) override;
268
269
    /** Returns the number of rows occupied at a specified row and column.
270
        @descr  Returns always 1 (Merged cells not supported). */
271
    virtual sal_Int32 SAL_CALL getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
272
273
    /** Returns the number of rows occupied at a specified row and column.
274
        @descr  Returns always 1 (Merged cells not supported). */
275
    virtual sal_Int32 SAL_CALL getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
276
277
    /** Returns the row headers as an AccessibleTable. */
278
    virtual css::uno::Reference< css::accessibility::XAccessibleTable > SAL_CALL getAccessibleRowHeaders() override;
279
280
    /** Returns the column headers as an AccessibleTable. */
281
    virtual css::uno::Reference< css::accessibility::XAccessibleTable > SAL_CALL getAccessibleColumnHeaders() override;
282
283
    /** Returns the selected rows as a sequence. */
284
    virtual css::uno::Sequence< sal_Int32 > SAL_CALL getSelectedAccessibleRows() override;
285
286
    /** Returns the selected columns as a sequence. */
287
    virtual css::uno::Sequence< sal_Int32 > SAL_CALL getSelectedAccessibleColumns() override;
288
289
    /** Returns true, if the specified row is selected. */
290
    virtual sal_Bool SAL_CALL isAccessibleRowSelected( sal_Int32 nRow ) override;
291
292
    /** Returns true, if the specified column is selected. */
293
    virtual sal_Bool SAL_CALL isAccessibleColumnSelected( sal_Int32 nColumn ) override;
294
295
    /** Returns the accessible cell object at the specified position. */
296
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
297
298
    /** Returns the caption object of the table. */
299
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleCaption() override;
300
301
    /** Returns the summary description object of the table. */
302
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleSummary() override;
303
304
    /** Returns true, if the cell at a specified position is selected. */
305
    virtual sal_Bool SAL_CALL isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn ) override;
306
307
    /** Returns the child index of the cell at the specified position. */
308
    virtual sal_Int64 SAL_CALL getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn ) override;
309
310
    /** Returns the row index of the specified child. */
311
    virtual sal_Int32 SAL_CALL getAccessibleRow( sal_Int64 nChildIndex ) override;
312
313
    /** Returns the column index of the specified child. */
314
    virtual sal_Int32 SAL_CALL getAccessibleColumn( sal_Int64 nChildIndex ) override;
315
316
    // XAccessibleSelection ---------------------------------------------------
317
318
    /** Selects the specified child (selects the entire column or the entire table). */
319
    virtual void SAL_CALL selectAccessibleChild( sal_Int64 nChildIndex ) override;
320
321
    /** Returns true, if the specified child is selected. */
322
    virtual sal_Bool SAL_CALL isAccessibleChildSelected( sal_Int64 nChildIndex ) override;
323
324
    /** Deselects all cells. */
325
    virtual void SAL_CALL clearAccessibleSelection() override;
326
327
    /** Selects all cells. */
328
    virtual void SAL_CALL selectAllAccessibleChildren() override;
329
330
    /** Returns the count of selected children. */
331
    virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount() override;
332
333
    /** Returns the child with the specified index in all selected children. */
334
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
335
336
    /** Deselects the child with the specified index in all selected children. */
337
    virtual void SAL_CALL deselectAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
338
339
    // events -----------------------------------------------------------------
340
public:
341
    /** Sends a GetFocus or LoseFocus event to all listeners. */
342
    virtual void SendFocusEvent( bool bFocused ) override;
343
    /** Sends a table model changed event for changed cell contents to all listeners. */
344
    virtual void SendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows ) override;
345
    /** Sends a table model changed event for an inserted column to all listeners. */
346
    virtual void SendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn ) override;
347
    /** Sends a table model changed event for a removed column to all listeners. */
348
    virtual void SendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn ) override;
349
350
    // helpers ----------------------------------------------------------------
351
private:
352
353
    /** @throws css::lang::IndexOutOfBoundsException if nIndex is not a valid child index. */
354
    void ensureValidIndex( sal_Int64 nIndex ) const;
355
    /** @Throws css::lang::IndexOutOfBoundsException if the specified position is invalid. */
356
    void ensureValidPosition( sal_Int32 nRow, sal_Int32 nColumn ) const;
357
358
    /** Returns the VCL grid control. Assumes a living object. */
359
    ScCsvGrid& implGetGrid() const;
360
361
    /** Returns true, if the specified column (including header) is selected. */
362
    bool implIsColumnSelected( sal_Int32 nColumn ) const;
363
    /** Selects the specified column (including header). */
364
    void implSelectColumn( sal_Int32 nColumn, bool bSelect );
365
366
    /** Returns the count of visible rows in the table (including header). */
367
    sal_Int32 implGetRowCount() const;
368
    /** Returns the total column count in the table (including header). */
369
    sal_Int32 implGetColumnCount() const;
370
    /** Returns the count of selected columns in the table. */
371
    sal_Int32 implGetSelColumnCount() const;
372
    /** Returns the total cell count in the table (including header). */
373
0
    sal_Int64 implGetCellCount() const { return static_cast<sal_Int64>(implGetRowCount()) * static_cast<sal_Int64>(implGetColumnCount()); }
374
375
    /** Returns the row index from cell index (including header). */
376
0
    sal_Int32 implGetRow( sal_Int64 nIndex ) const { return nIndex / implGetColumnCount(); }
377
    /** Returns the column index from cell index (including header). */
378
0
    sal_Int32 implGetColumn( sal_Int64 nIndex ) const { return nIndex % implGetColumnCount(); }
379
    /** Returns the absolute column index of the nSelColumn-th selected column. */
380
    sal_Int32 implGetSelColumn( sal_Int32 nSelColumn ) const;
381
    /** Returns the child index from cell position (including header). */
382
0
    sal_Int64 implGetIndex( sal_Int32 nRow, sal_Int32 nColumn ) const { return static_cast<sal_Int64>(nRow) * static_cast<sal_Int64>(implGetColumnCount()) + nColumn; }
383
384
    /** Returns the contents of the specified cell (including header). Indexes must be valid. */
385
    OUString implGetCellText( sal_Int32 nRow, sal_Int32 nColumn ) const;
386
    /** Creates a new accessible object of the specified cell. Indexes must be valid. */
387
    rtl::Reference<ScAccessibleCsvCell> implCreateCellObj(sal_Int32 nRow, sal_Int32 nColumn);
388
389
    css::uno::Reference<css::accessibility::XAccessible> getAccessibleCell(sal_Int32 nRow, sal_Int32 nColumn);
390
};
391
392
/** Accessible class representing a cell of the CSV grid control. */
393
class ScAccessibleCsvCell : public ScAccessibleCsvControl,
394
                            public ::accessibility::AccessibleStaticTextBase
395
{
396
protected:
397
    typedef ::std::unique_ptr< SvxEditSource >      SvxEditSourcePtr;
398
399
private:
400
    OUString                    maCellText; /// The text contents of this cell.
401
    sal_Int32                   mnLine;     /// The grid line index (core index).
402
    sal_uInt32                  mnColumn;   /// The grid column index (core index).
403
    sal_Int32                   mnIndex;    /// The index of the cell in the table.
404
405
public:
406
    explicit                    ScAccessibleCsvCell(
407
                                    ScCsvGrid& rGrid,
408
                                    OUString aCellText,
409
                                    sal_Int32 nRow, sal_Int32 nColumn);
410
    virtual                     ~ScAccessibleCsvCell() override;
411
412
    virtual void SAL_CALL       disposing() override;
413
414
    // XAccessibleComponent ---------------------------------------------------
415
416
    /** Sets the focus to the column of this cell. */
417
    virtual void SAL_CALL grabFocus() override;
418
419
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent(  ) override;
420
421
    virtual OUString SAL_CALL getAccessibleDescription(  ) override;
422
    virtual OUString SAL_CALL getAccessibleName(  ) override;
423
0
    virtual sal_Int16 SAL_CALL getAccessibleRole(  ) override { return css::accessibility::AccessibleRole::TEXT; }
424
425
    virtual sal_Int32 SAL_CALL getForeground(  ) override;
426
427
    virtual sal_Int32 SAL_CALL getBackground(  ) override;
428
429
    // XAccessibleContext -----------------------------------------------------
430
431
    /** Returns the child count. */
432
    virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
433
434
    /** Returns the specified child. */
435
    virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 nIndex ) override;
436
437
    /** Returns the index of this cell in the table. */
438
    virtual sal_Int64 SAL_CALL getAccessibleIndexInParent() override;
439
440
    /** Returns the relation to the ruler control. */
441
    virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
442
443
    /** Returns the current set of states. */
444
    virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
445
446
    // XInterface -------------------------------------------------------------
447
448
    DECLARE_XINTERFACE()
449
450
    // XTypeProvider ----------------------------------------------------------
451
452
    DECLARE_XTYPEPROVIDER()
453
454
private:
455
    /** Returns the VCL grid control. Assumes a living object. */
456
    ScCsvGrid& implGetGrid() const;
457
    /** Returns the pixel position of the cell (rel. to parent), regardless of visibility. */
458
    Point implGetRealPos() const;
459
    /** Returns the width of the character count */
460
    sal_uInt32 implCalcPixelWidth(sal_uInt32 nChars) const;
461
    /** Returns the pixel size of the cell, regardless of visibility. */
462
    Size implGetRealSize() const;
463
    /** Returns the bounding box of the cell relative in the table. */
464
    virtual css::awt::Rectangle implGetBounds() override;
465
466
    /** Creates the edit source the text helper needs. */
467
    ::std::unique_ptr< SvxEditSource > implCreateEditSource();
468
};
469
470
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */