Line data Source code
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 : #include <accessibility/standard/vclxaccessiblebox.hxx>
21 : #include <accessibility/standard/vclxaccessibletextfield.hxx>
22 : #include <accessibility/standard/vclxaccessibleedit.hxx>
23 : #include <accessibility/standard/vclxaccessiblelist.hxx>
24 : #include <accessibility/helper/listboxhelper.hxx>
25 :
26 : #include <unotools/accessiblestatesethelper.hxx>
27 : #include <com/sun/star/accessibility/AccessibleStateType.hpp>
28 : #include <com/sun/star/accessibility/AccessibleEventId.hpp>
29 : #include <com/sun/star/accessibility/AccessibleRole.hpp>
30 : #include <vcl/svapp.hxx>
31 : #include <vcl/combobox.hxx>
32 : #include <vcl/lstbox.hxx>
33 : #include <accessibility/helper/accresmgr.hxx>
34 : #include <accessibility/helper/accessiblestrings.hrc>
35 :
36 : using namespace ::com::sun::star;
37 : using namespace ::com::sun::star::uno;
38 : using namespace ::com::sun::star::lang;
39 : using namespace ::com::sun::star::beans;
40 : using namespace ::com::sun::star::accessibility;
41 :
42 10 : VCLXAccessibleBox::VCLXAccessibleBox (VCLXWindow* pVCLWindow, BoxType aType, bool bIsDropDownBox)
43 : : VCLXAccessibleComponent (pVCLWindow),
44 : m_aBoxType (aType),
45 : m_bIsDropDownBox (bIsDropDownBox),
46 10 : m_nIndexInParent (DEFAULT_INDEX_IN_PARENT)
47 : {
48 : // Set up the flags that indicate which children this object has.
49 10 : m_bHasListChild = true;
50 :
51 : // A text field is not present for non drop down list boxes.
52 10 : if ((m_aBoxType==LISTBOX) && ! m_bIsDropDownBox)
53 0 : m_bHasTextChild = false;
54 : else
55 10 : m_bHasTextChild = true;
56 10 : }
57 :
58 0 : VCLXAccessibleBox::~VCLXAccessibleBox()
59 : {
60 0 : }
61 :
62 162 : void VCLXAccessibleBox::ProcessWindowChildEvent( const VclWindowEvent& rVclWindowEvent )
63 : {
64 324 : uno::Any aOldValue, aNewValue;
65 324 : uno::Reference<XAccessible> xAcc;
66 :
67 162 : switch ( rVclWindowEvent.GetId() )
68 : {
69 : case VCLEVENT_WINDOW_SHOW:
70 : case VCLEVENT_WINDOW_HIDE:
71 : {
72 48 : vcl::Window* pChildWindow = static_cast<vcl::Window *>(rVclWindowEvent.GetData());
73 : // Just compare to the combo box text field. All other children
74 : // are identical to this object in which case this object will
75 : // be removed in a short time.
76 48 : if (m_aBoxType==COMBOBOX)
77 : {
78 28 : VclPtr< ComboBox > pComboBox = GetAs< ComboBox >();
79 28 : if ( ( pComboBox != nullptr ) && ( pChildWindow != NULL ) )
80 17 : if (pChildWindow == pComboBox->GetSubEdit())
81 : {
82 5 : if (rVclWindowEvent.GetId() == VCLEVENT_WINDOW_SHOW)
83 : {
84 : // Instantiate text field.
85 0 : getAccessibleChild (0);
86 0 : aNewValue <<= m_xText;
87 : }
88 : else
89 : {
90 : // Release text field.
91 5 : aOldValue <<= m_xText;
92 5 : m_xText = NULL;
93 : }
94 : // Tell the listeners about the new/removed child.
95 : NotifyAccessibleEvent (
96 : AccessibleEventId::CHILD,
97 5 : aOldValue, aNewValue);
98 28 : }
99 :
100 : }
101 : }
102 48 : break;
103 :
104 : default:
105 114 : VCLXAccessibleComponent::ProcessWindowChildEvent (rVclWindowEvent);
106 162 : }
107 162 : }
108 :
109 45 : void VCLXAccessibleBox::ProcessWindowEvent (const VclWindowEvent& rVclWindowEvent)
110 : {
111 45 : switch ( rVclWindowEvent.GetId() )
112 : {
113 : case VCLEVENT_DROPDOWN_SELECT:
114 : case VCLEVENT_LISTBOX_SELECT:
115 : case VCLEVENT_LISTBOX_FOCUSITEMCHANGED:
116 : {
117 : // Forward the call to the list child.
118 0 : VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
119 0 : if ( pList == NULL )
120 : {
121 0 : getAccessibleChild ( m_bHasTextChild ? 1 : 0 );
122 0 : pList = static_cast<VCLXAccessibleList*>(m_xList.get());
123 : }
124 0 : if ( pList != NULL )
125 : {
126 0 : pList->ProcessWindowEvent (rVclWindowEvent, m_bIsDropDownBox);
127 : #if defined WNT
128 : if (m_bIsDropDownBox)
129 : {
130 : NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any());
131 : }
132 : #endif
133 : }
134 0 : break;
135 : }
136 : case VCLEVENT_DROPDOWN_OPEN:
137 : {
138 2 : VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
139 2 : if ( pList == NULL )
140 : {
141 1 : getAccessibleChild ( m_bHasTextChild ? 1 : 0 );
142 1 : pList = static_cast<VCLXAccessibleList*>(m_xList.get());
143 : }
144 2 : if ( pList != NULL )
145 : {
146 2 : pList->ProcessWindowEvent (rVclWindowEvent);
147 2 : pList->HandleDropOpen();
148 : }
149 2 : break;
150 : }
151 : case VCLEVENT_DROPDOWN_CLOSE:
152 : {
153 1 : VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
154 1 : if ( pList == NULL )
155 : {
156 0 : getAccessibleChild ( m_bHasTextChild ? 1 : 0 );
157 0 : pList = static_cast<VCLXAccessibleList*>(m_xList.get());
158 : }
159 1 : if ( pList != NULL )
160 : {
161 1 : pList->ProcessWindowEvent (rVclWindowEvent);
162 : }
163 1 : vcl::Window* pWindow = GetWindow();
164 1 : if( pWindow && (pWindow->HasFocus() || pWindow->HasChildPathFocus()) )
165 : {
166 2 : Any aOldValue, aNewValue;
167 1 : aNewValue <<= AccessibleStateType::FOCUSED;
168 2 : NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
169 : }
170 1 : break;
171 : }
172 : case VCLEVENT_COMBOBOX_SELECT:
173 : {
174 0 : VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
175 0 : if (pList != NULL && m_xText.is())
176 : {
177 0 : Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
178 0 : if ( xText.is() )
179 : {
180 0 : OUString sText = xText->getSelectedText();
181 0 : if ( sText.isEmpty() )
182 0 : sText = xText->getText();
183 0 : pList->UpdateSelection_Acc(sText, m_bIsDropDownBox);
184 : #if defined WNT
185 : if (m_bIsDropDownBox || ( !m_bIsDropDownBox && m_aBoxType==COMBOBOX))
186 : NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any());
187 : #endif
188 0 : }
189 : }
190 0 : break;
191 : }
192 : //case VCLEVENT_DROPDOWN_OPEN:
193 : //case VCLEVENT_DROPDOWN_CLOSE:
194 : case VCLEVENT_LISTBOX_DOUBLECLICK:
195 : case VCLEVENT_LISTBOX_SCROLLED:
196 : //case VCLEVENT_LISTBOX_SELECT:
197 : case VCLEVENT_LISTBOX_ITEMADDED:
198 : case VCLEVENT_LISTBOX_ITEMREMOVED:
199 : case VCLEVENT_COMBOBOX_ITEMADDED:
200 : case VCLEVENT_COMBOBOX_ITEMREMOVED:
201 : case VCLEVENT_COMBOBOX_SCROLLED:
202 : {
203 : // Forward the call to the list child.
204 0 : VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
205 0 : if ( pList == NULL )
206 : {
207 0 : getAccessibleChild ( m_bHasTextChild ? 1 : 0 );
208 0 : pList = static_cast<VCLXAccessibleList*>(m_xList.get());
209 : }
210 0 : if ( pList != NULL )
211 0 : pList->ProcessWindowEvent (rVclWindowEvent);
212 0 : break;
213 : }
214 :
215 : //case VCLEVENT_COMBOBOX_SELECT:
216 : case VCLEVENT_COMBOBOX_DESELECT:
217 : {
218 : // Selection is handled by VCLXAccessibleList which operates on
219 : // the same VCL object as this box does. In case of the
220 : // combobox, however, we have to help by providing the list with
221 : // the text of the currently selected item.
222 0 : VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
223 0 : if (pList != NULL && m_xText.is())
224 : {
225 0 : Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
226 0 : if ( xText.is() )
227 : {
228 0 : OUString sText = xText->getSelectedText();
229 0 : if ( sText.isEmpty() )
230 0 : sText = xText->getText();
231 0 : pList->UpdateSelection (sText);
232 0 : }
233 : }
234 0 : break;
235 : }
236 :
237 : case VCLEVENT_EDIT_MODIFY:
238 : case VCLEVENT_EDIT_SELECTIONCHANGED:
239 : // case VCLEVENT_EDIT_CARETCHANGED:
240 : // Modify/Selection events are handled by the combo box instead of
241 : // directly by the edit field (Why?). Therefore, delegate this
242 : // call to the edit field.
243 1 : if (m_aBoxType==COMBOBOX)
244 : {
245 1 : if (m_xText.is())
246 : {
247 0 : Reference<XAccessibleContext> xContext = m_xText->getAccessibleContext();
248 0 : VCLXAccessibleEdit* pEdit = static_cast<VCLXAccessibleEdit*>(xContext.get());
249 0 : if (pEdit != NULL)
250 0 : pEdit->ProcessWindowEvent (rVclWindowEvent);
251 : }
252 : }
253 1 : break;
254 : default:
255 41 : VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent );
256 : }
257 45 : }
258 :
259 1293 : IMPLEMENT_FORWARD_XINTERFACE2(VCLXAccessibleBox, VCLXAccessibleComponent, VCLXAccessibleBox_BASE)
260 0 : IMPLEMENT_FORWARD_XTYPEPROVIDER2(VCLXAccessibleBox, VCLXAccessibleComponent, VCLXAccessibleBox_BASE)
261 :
262 : //===== XAccessible =========================================================
263 :
264 8 : Reference< XAccessibleContext > SAL_CALL VCLXAccessibleBox::getAccessibleContext( )
265 : throw (RuntimeException, std::exception)
266 : {
267 8 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
268 :
269 8 : return this;
270 : }
271 :
272 : //===== XAccessibleContext ==================================================
273 :
274 67 : sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleChildCount()
275 : throw (RuntimeException, std::exception)
276 : {
277 67 : SolarMutexGuard aSolarGuard;
278 134 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
279 :
280 : // Usually a box has a text field and a list of items as its children.
281 : // Non drop down list boxes have no text field. Additionally check
282 : // whether the object is valid.
283 67 : sal_Int32 nCount = 0;
284 67 : if (IsValid())
285 67 : nCount += (m_bHasTextChild?1:0) + (m_bHasListChild?1:0);
286 : else
287 : {
288 : // Object not valid anymore. Release references to children.
289 0 : m_bHasTextChild = false;
290 0 : m_xText = NULL;
291 0 : m_bHasListChild = false;
292 0 : m_xList = NULL;
293 : }
294 :
295 134 : return nCount;
296 : }
297 :
298 31 : Reference<XAccessible> SAL_CALL VCLXAccessibleBox::getAccessibleChild (sal_Int32 i)
299 : throw (IndexOutOfBoundsException, RuntimeException, std::exception)
300 : {
301 31 : SolarMutexGuard aSolarGuard;
302 62 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
303 :
304 31 : if (i<0 || i>=getAccessibleChildCount())
305 0 : throw IndexOutOfBoundsException();
306 :
307 31 : Reference< XAccessible > xChild;
308 31 : if (IsValid())
309 : {
310 31 : if (i==1 || ! m_bHasTextChild)
311 : {
312 : // List.
313 15 : if ( ! m_xList.is())
314 : {
315 10 : VCLXAccessibleList* pList = new VCLXAccessibleList ( GetVCLXWindow(),
316 10 : (m_aBoxType == LISTBOX ? VCLXAccessibleList::LISTBOX : VCLXAccessibleList::COMBOBOX),
317 20 : this);
318 10 : pList->SetIndexInParent (i);
319 10 : m_xList = pList;
320 : }
321 15 : xChild = m_xList;
322 : }
323 : else
324 : {
325 : // Text Field.
326 16 : if ( ! m_xText.is())
327 : {
328 10 : if (m_aBoxType==COMBOBOX)
329 : {
330 5 : VclPtr< ComboBox > pComboBox = GetAs< ComboBox >();
331 5 : if (pComboBox!=nullptr && pComboBox->GetSubEdit()!=NULL)
332 : //Set the edit's acc name the same as parent
333 : {
334 5 : pComboBox->GetSubEdit()->SetAccessibleName(getAccessibleName());
335 5 : m_xText = pComboBox->GetSubEdit()->GetAccessible();
336 5 : }
337 : }
338 5 : else if (m_bIsDropDownBox)
339 5 : m_xText = new VCLXAccessibleTextField (GetVCLXWindow(),this);
340 : }
341 16 : xChild = m_xText;
342 : }
343 : }
344 :
345 62 : return xChild;
346 : }
347 :
348 83 : sal_Int16 SAL_CALL VCLXAccessibleBox::getAccessibleRole() throw (RuntimeException, std::exception)
349 : {
350 83 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
351 :
352 : // Return the role <const>COMBO_BOX</const> for both VCL combo boxes and
353 : // VCL list boxes in DropDown-Mode else <const>PANEL</const>.
354 : // This way the Java bridge has not to handle both independently.
355 : //return m_bIsDropDownBox ? AccessibleRole::COMBO_BOX : AccessibleRole::PANEL;
356 83 : if (m_bIsDropDownBox || (!m_bIsDropDownBox && m_aBoxType == COMBOBOX ))
357 83 : return AccessibleRole::COMBO_BOX;
358 : else
359 0 : return AccessibleRole::PANEL;
360 : }
361 :
362 1 : sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleIndexInParent()
363 : throw (::com::sun::star::uno::RuntimeException, std::exception)
364 : {
365 1 : if (m_nIndexInParent != DEFAULT_INDEX_IN_PARENT)
366 0 : return m_nIndexInParent;
367 : else
368 1 : return VCLXAccessibleComponent::getAccessibleIndexInParent();
369 : }
370 :
371 : //===== XAccessibleAction ===================================================
372 :
373 22 : sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleActionCount()
374 : throw (RuntimeException, std::exception)
375 : {
376 22 : ::osl::Guard< ::osl::Mutex> aGuard (GetMutex());
377 :
378 : // There is one action for drop down boxes (toggle popup) and none for
379 : // the other boxes.
380 22 : return m_bIsDropDownBox ? 1 : 0;
381 : }
382 :
383 8 : sal_Bool SAL_CALL VCLXAccessibleBox::doAccessibleAction (sal_Int32 nIndex)
384 : throw (IndexOutOfBoundsException, RuntimeException, std::exception)
385 : {
386 8 : bool bNotify = false;
387 :
388 : {
389 8 : SolarMutexGuard aSolarGuard;
390 16 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
391 :
392 8 : if (nIndex<0 || nIndex>=getAccessibleActionCount())
393 : throw ::com::sun::star::lang::IndexOutOfBoundsException(
394 : ("VCLXAccessibleBox::doAccessibleAction: index "
395 4 : + OUString::number(nIndex) + " not among 0.."
396 8 : + OUString::number(getAccessibleActionCount())),
397 6 : static_cast<OWeakObject*>(this));
398 :
399 6 : if (m_aBoxType == COMBOBOX)
400 : {
401 3 : VclPtr< ComboBox > pComboBox = GetAs< ComboBox >();
402 3 : if (pComboBox != nullptr)
403 : {
404 3 : pComboBox->ToggleDropDown();
405 3 : bNotify = true;
406 3 : }
407 : }
408 3 : else if (m_aBoxType == LISTBOX)
409 : {
410 3 : VclPtr< ListBox > pListBox = GetAs< ListBox >();
411 3 : if (pListBox != nullptr)
412 : {
413 3 : pListBox->ToggleDropDown();
414 3 : bNotify = true;
415 3 : }
416 8 : }
417 : }
418 :
419 6 : if (bNotify)
420 6 : NotifyAccessibleEvent (AccessibleEventId::ACTION_CHANGED, Any(), Any());
421 :
422 6 : return bNotify;
423 : }
424 :
425 6 : OUString SAL_CALL VCLXAccessibleBox::getAccessibleActionDescription (sal_Int32 nIndex)
426 : throw (IndexOutOfBoundsException, RuntimeException, std::exception)
427 : {
428 6 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
429 6 : if (nIndex<0 || nIndex>=getAccessibleActionCount())
430 2 : throw ::com::sun::star::lang::IndexOutOfBoundsException();
431 :
432 4 : if (m_bIsDropDownBox)
433 4 : return TK_RES_STRING(RID_STR_ACC_ACTION_TOGGLEPOPUP);
434 :
435 2 : return OUString();
436 : }
437 :
438 4 : Reference< XAccessibleKeyBinding > VCLXAccessibleBox::getAccessibleActionKeyBinding( sal_Int32 nIndex )
439 : throw (IndexOutOfBoundsException, RuntimeException, std::exception)
440 : {
441 4 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
442 :
443 4 : Reference< XAccessibleKeyBinding > xRet;
444 :
445 4 : if (nIndex<0 || nIndex>=getAccessibleActionCount())
446 2 : throw ::com::sun::star::lang::IndexOutOfBoundsException();
447 :
448 : // ... which key?
449 4 : return xRet;
450 : }
451 :
452 : //===== XComponent ==========================================================
453 :
454 10 : void SAL_CALL VCLXAccessibleBox::disposing()
455 : {
456 10 : VCLXAccessibleComponent::disposing();
457 10 : }
458 :
459 : // ===== XAccessibleValue ===============================================
460 0 : Any VCLXAccessibleBox::getCurrentValue( )
461 : throw( RuntimeException, std::exception )
462 : {
463 0 : SolarMutexGuard aSolarGuard;
464 0 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
465 :
466 0 : Any aAny;
467 0 : if( m_xList.is() && m_xText.is())
468 : {
469 : // VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
470 0 : Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
471 0 : if ( xText.is() )
472 : {
473 0 : OUString sText = xText->getText();
474 0 : aAny <<= sText;
475 0 : }
476 : }
477 0 : if (m_aBoxType == LISTBOX && m_bIsDropDownBox && m_xList.is() )
478 : {
479 :
480 0 : VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
481 0 : if(pList->IsInDropDown())
482 : {
483 0 : if(pList->getSelectedAccessibleChildCount()>0)
484 : {
485 0 : Reference<XAccessibleContext> xName (pList->getSelectedAccessibleChild((sal_Int32)(0)), UNO_QUERY);
486 0 : if(xName.is())
487 : {
488 0 : aAny <<= xName->getAccessibleName();
489 0 : }
490 : }
491 : }
492 : }
493 :
494 0 : return aAny;
495 : }
496 :
497 0 : sal_Bool VCLXAccessibleBox::setCurrentValue( const Any& aNumber )
498 : throw( RuntimeException, std::exception )
499 : {
500 0 : SolarMutexGuard aSolarGuard;
501 0 : ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
502 :
503 0 : OUString fValue;
504 0 : bool bValid = (aNumber >>= fValue);
505 : if( bValid )
506 : {
507 :
508 : }
509 0 : return bValid;
510 :
511 : }
512 :
513 0 : Any VCLXAccessibleBox::getMaximumValue( )
514 : throw( RuntimeException, std::exception )
515 : {
516 0 : Any aAny;
517 0 : return aAny;
518 : }
519 :
520 0 : Any VCLXAccessibleBox::getMinimumValue( )
521 : throw( RuntimeException, std::exception )
522 : {
523 0 : Any aAny;
524 0 : return aAny;
525 : }
526 :
527 : // Set the INDETERMINATE state when there is no selected item for combobox
528 21 : void VCLXAccessibleBox::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )
529 : {
530 21 : VCLXAccessibleComponent::FillAccessibleStateSet(rStateSet);
531 21 : if (m_aBoxType == COMBOBOX )
532 : {
533 13 : OUString sText;
534 13 : sal_Int32 nEntryCount = 0;
535 26 : VclPtr< ComboBox > pComboBox = GetAs< ComboBox >();
536 13 : if (pComboBox != nullptr)
537 : {
538 13 : Edit* pSubEdit = pComboBox->GetSubEdit();
539 13 : if ( pSubEdit)
540 13 : sText = pSubEdit->GetText();
541 13 : nEntryCount = pComboBox->GetEntryCount();
542 : }
543 13 : if ( sText.isEmpty() && nEntryCount > 0 )
544 13 : rStateSet.AddState(AccessibleStateType::INDETERMINATE);
545 : }
546 8 : else if (m_aBoxType == LISTBOX && m_bIsDropDownBox)
547 : {
548 8 : sal_Int32 nSelectedEntryCount = 0;
549 8 : VclPtr< ListBox > pListBox = GetAs< ListBox >();
550 8 : if (pListBox != nullptr && pListBox->GetEntryCount() > 0)
551 : {
552 8 : nSelectedEntryCount = pListBox->GetSelectEntryCount();
553 8 : if ( nSelectedEntryCount == 0)
554 0 : rStateSet.AddState(AccessibleStateType::INDETERMINATE);
555 8 : }
556 : }
557 57 : }
558 :
559 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|