1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 #include <svx/sdr/properties/textproperties.hxx>
27 #include <svl/itemset.hxx>
28 #include <svl/style.hxx>
29 #include <svl/itemiter.hxx>
30 #include <svl/smplhint.hxx>
31 #include <svx/svddef.hxx>
32 #include <svx/svdotext.hxx>
33 #include <svx/svdoutl.hxx>
34 #include <editeng/writingmodeitem.hxx>
35 #include <svx/svdmodel.hxx>
36 #include <editeng/outlobj.hxx>
37 #include <svx/xflclit.hxx>
38 #include <editeng/adjitem.hxx>
39 #include <svx/svdetc.hxx>
40 #include <editeng/editeng.hxx>
41 #include <editeng/flditem.hxx>
42 #include <svx/xlnwtit.hxx>
43 #include <svx/svdpool.hxx>
44 
45 //////////////////////////////////////////////////////////////////////////////
46 
47 namespace sdr
48 {
49 	namespace properties
50 	{
51 		SfxItemSet& TextProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
52 		{
53 			return *(new SfxItemSet(rPool,
54 
55 				// range from SdrAttrObj
56 				SDRATTR_START, SDRATTR_SHADOW_LAST,
57 				SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
58 				SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
59 
60 				// range from SdrTextObj
61 				EE_ITEMS_START, EE_ITEMS_END,
62 
63 				// end
64 				0, 0));
65 		}
66 
67 		TextProperties::TextProperties(SdrObject& rObj)
68 		:	AttributeProperties(rObj),
69             maVersion(0)
70 		{
71 		}
72 
73 		TextProperties::TextProperties(const TextProperties& rProps, SdrObject& rObj)
74 		:	AttributeProperties(rProps, rObj),
75             maVersion(rProps.getVersion())
76 		{
77 		}
78 
79 		TextProperties::~TextProperties()
80 		{
81 		}
82 
83 		BaseProperties& TextProperties::Clone(SdrObject& rObj) const
84 		{
85 			return *(new TextProperties(*this, rObj));
86 		}
87 
88 		void TextProperties::ItemSetChanged(const SfxItemSet& rSet)
89 		{
90 			SdrTextObj& rObj = (SdrTextObj&)GetSdrObject();
91 			sal_Int32 nText = rObj.getTextCount();
92 
93             // #i101556# ItemSet has changed -> new version
94             maVersion++;
95 
96             while( --nText >= 0 )
97 			{
98 				SdrText* pText = rObj.getText( nText );
99 
100 				OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : 0;
101 
102 				if(pParaObj)
103 				{
104 					const bool bTextEdit = rObj.IsTextEditActive() && (rObj.getActiveText() == pText);
105 
106 					// handle outliner attributes
107 					GetObjectItemSet();
108 					Outliner* pOutliner = rObj.GetTextEditOutliner();
109 
110 					if(!bTextEdit)
111 					{
112 						pOutliner = &rObj.ImpGetDrawOutliner();
113 						pOutliner->SetText(*pParaObj);
114 					}
115 
116 					sal_uInt32 nParaCount(pOutliner->GetParagraphCount());
117 
118 					for(sal_uInt16 nPara = 0; nPara < nParaCount; nPara++)
119 					{
120 						SfxItemSet aSet(pOutliner->GetParaAttribs(nPara));
121 						aSet.Put(rSet);
122 						pOutliner->SetParaAttribs(nPara, aSet);
123 					}
124 
125 					if(!bTextEdit)
126 					{
127 						if(nParaCount)
128 						{
129 							// force ItemSet
130 							GetObjectItemSet();
131 
132 							SfxItemSet aNewSet(pOutliner->GetParaAttribs(0L));
133 							mpItemSet->Put(aNewSet);
134 						}
135 
136 						OutlinerParaObject* pTemp = pOutliner->CreateParaObject(0, (sal_uInt16)nParaCount);
137 						pOutliner->Clear();
138 
139 						rObj.NbcSetOutlinerParaObjectForText(pTemp,pText);
140 					}
141 				}
142 			}
143 
144 			// Extra-Repaint for radical layout changes (#43139#)
145 			if(SFX_ITEM_SET == rSet.GetItemState(SDRATTR_TEXT_CONTOURFRAME))
146 			{
147 				// Here only repaint wanted
148 				rObj.ActionChanged();
149 				//rObj.BroadcastObjectChange();
150 			}
151 
152 			// call parent
153 			AttributeProperties::ItemSetChanged(rSet);
154 		}
155 
156 		void TextProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
157 		{
158 			SdrTextObj& rObj = (SdrTextObj&)GetSdrObject();
159 
160 			// #i25616#
161 			sal_Int32 nOldLineWidth(0L);
162 
163 			if(XATTR_LINEWIDTH == nWhich && rObj.DoesSupportTextIndentingOnLineWidthChange())
164 			{
165 				nOldLineWidth = ((const XLineWidthItem&)GetItem(XATTR_LINEWIDTH)).GetValue();
166 			}
167 
168 			if(pNewItem && (SDRATTR_TEXTDIRECTION == nWhich))
169 			{
170 				sal_Bool bVertical(com::sun::star::text::WritingMode_TB_RL == ((SvxWritingModeItem*)pNewItem)->GetValue());
171 				rObj.SetVerticalWriting(bVertical);
172 			}
173 
174 			// #95501# reset to default
175 			if(!pNewItem && !nWhich && rObj.HasText() )
176 			{
177 				SdrOutliner& rOutliner = rObj.ImpGetDrawOutliner();
178 
179 				sal_Int32 nCount = rObj.getTextCount();
180 				while( nCount-- )
181 				{
182 					SdrText* pText = rObj.getText( nCount );
183 					OutlinerParaObject* pParaObj = pText->GetOutlinerParaObject();
184 					if( pParaObj )
185 					{
186 						rOutliner.SetText(*pParaObj);
187 						sal_uInt32 nParaCount(rOutliner.GetParagraphCount());
188 
189 						if(nParaCount)
190 						{
191 							ESelection aSelection( 0, 0, EE_PARA_ALL, EE_PARA_ALL);
192 							rOutliner.RemoveAttribs(aSelection, sal_True, 0);
193 
194 							OutlinerParaObject* pTemp = rOutliner.CreateParaObject(0, (sal_uInt16)nParaCount);
195 							rOutliner.Clear();
196 
197 							rObj.NbcSetOutlinerParaObjectForText( pTemp, pText );
198 						}
199 					}
200 				}
201 			}
202 
203 			// call parent
204 			AttributeProperties::ItemChange( nWhich, pNewItem );
205 
206 			// #i25616#
207 			if(XATTR_LINEWIDTH == nWhich && rObj.DoesSupportTextIndentingOnLineWidthChange())
208 			{
209 				const sal_Int32 nNewLineWidth(((const XLineWidthItem&)GetItem(XATTR_LINEWIDTH)).GetValue());
210 				const sal_Int32 nDifference((nNewLineWidth - nOldLineWidth) / 2);
211 
212 				if(nDifference)
213 				{
214 					const sal_Bool bLineVisible(XLINE_NONE != ((const XLineStyleItem&)(GetItem(XATTR_LINESTYLE))).GetValue());
215 
216 					if(bLineVisible)
217 					{
218 						const sal_Int32 nLeftDist(((const SdrTextLeftDistItem&)GetItem(SDRATTR_TEXT_LEFTDIST)).GetValue());
219 						const sal_Int32 nRightDist(((const SdrTextRightDistItem&)GetItem(SDRATTR_TEXT_RIGHTDIST)).GetValue());
220 						const sal_Int32 nUpperDist(((const SdrTextUpperDistItem&)GetItem(SDRATTR_TEXT_UPPERDIST)).GetValue());
221 						const sal_Int32 nLowerDist(((const SdrTextLowerDistItem&)GetItem(SDRATTR_TEXT_LOWERDIST)).GetValue());
222 
223 						SetObjectItemDirect(SdrTextLeftDistItem(nLeftDist + nDifference));
224 						SetObjectItemDirect(SdrTextRightDistItem(nRightDist + nDifference));
225 						SetObjectItemDirect(SdrTextUpperDistItem(nUpperDist + nDifference));
226 						SetObjectItemDirect(SdrTextLowerDistItem(nLowerDist + nDifference));
227 					}
228 				}
229 			}
230 		}
231 
232 		void TextProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, sal_Bool bDontRemoveHardAttr)
233 		{
234 			SdrTextObj& rObj = (SdrTextObj&)GetSdrObject();
235 
236 			// call parent
237 			AttributeProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
238 
239             // #i101556# StyleSheet has changed -> new version
240             maVersion++;
241 
242             if( rObj.GetModel() /*&& !rObj.IsTextEditActive()*/ && !rObj.IsLinkedText() )
243 			{
244 				SdrOutliner& rOutliner = rObj.ImpGetDrawOutliner();
245 
246 				sal_Int32 nText = rObj.getTextCount();
247 
248 				while( --nText >= 0 )
249 				{
250 					SdrText* pText = rObj.getText( nText );
251 
252 					OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : 0;
253 					if( !pParaObj )
254 						continue;
255 
256 					// apply StyleSheet to all paragraphs
257 					rOutliner.SetText(*pParaObj);
258 					sal_uInt32 nParaCount(rOutliner.GetParagraphCount());
259 
260 					if(nParaCount)
261 					{
262 						for(sal_uInt16 nPara = 0; nPara < nParaCount; nPara++)
263 						{
264 							SfxItemSet* pTempSet = 0L;
265 
266 							// since setting the stylesheet removes all para attributes
267 							if(bDontRemoveHardAttr)
268 							{
269 								// we need to remember them if we want to keep them
270 								pTempSet = new SfxItemSet(rOutliner.GetParaAttribs(nPara));
271 							}
272 
273 							if(GetStyleSheet())
274 							{
275 								if((OBJ_OUTLINETEXT == rObj.GetTextKind()) && (SdrInventor == rObj.GetObjInventor()))
276 								{
277 									String aNewStyleSheetName(GetStyleSheet()->GetName());
278 									aNewStyleSheetName.Erase(aNewStyleSheetName.Len() - 1, 1);
279 									sal_Int16 nDepth = rOutliner.GetDepth((sal_uInt16)nPara);
280 									aNewStyleSheetName += String::CreateFromInt32( nDepth <= 0 ? 1 : nDepth + 1);
281 
282 									SdrModel* pModel = rObj.GetModel();
283 									SfxStyleSheetBasePool* pStylePool = (pModel != NULL) ? pModel->GetStyleSheetPool() : 0L;
284 									SfxStyleSheet* pNewStyle = (SfxStyleSheet*)pStylePool->Find(aNewStyleSheetName, GetStyleSheet()->GetFamily());
285 									DBG_ASSERT( pNewStyle, "AutoStyleSheetName - Style not found!" );
286 
287 									if(pNewStyle)
288 									{
289 										rOutliner.SetStyleSheet(nPara, pNewStyle);
290 									}
291 								}
292 								else
293 								{
294 									rOutliner.SetStyleSheet(nPara, GetStyleSheet());
295 								}
296 							}
297 							else
298 							{
299 								// remove StyleSheet
300 								rOutliner.SetStyleSheet(nPara, 0L);
301 							}
302 
303 							if(bDontRemoveHardAttr)
304 							{
305 								if(pTempSet)
306 								{
307 									// restore para attributes
308 									rOutliner.SetParaAttribs(nPara, *pTempSet);
309 								}
310 							}
311 							else
312 							{
313 								if(pNewStyleSheet)
314 								{
315 									// remove all hard paragraph attributes
316 									// which occur in StyleSheet, take care of
317 									// parents (!)
318 									SfxItemIter aIter(pNewStyleSheet->GetItemSet());
319 									const SfxPoolItem* pItem = aIter.FirstItem();
320 
321 									while(pItem)
322 									{
323 										if(!IsInvalidItem(pItem))
324 										{
325 											sal_uInt16 nW(pItem->Which());
326 
327 											if(nW >= EE_ITEMS_START && nW <= EE_ITEMS_END)
328 											{
329 												rOutliner.QuickRemoveCharAttribs((sal_uInt16)nPara, nW);
330 											}
331 										}
332 										pItem = aIter.NextItem();
333 									}
334 								}
335 							}
336 
337 							if(pTempSet)
338 							{
339 								delete pTempSet;
340 							}
341 						}
342 
343 						OutlinerParaObject* pTemp = rOutliner.CreateParaObject(0, (sal_uInt16)nParaCount);
344 						rOutliner.Clear();
345 						rObj.NbcSetOutlinerParaObjectForText(pTemp, pText);
346 					}
347 				}
348 			}
349 
350 			if(rObj.IsTextFrame())
351 			{
352 				rObj.NbcAdjustTextFrameWidthAndHeight();
353 			}
354 		}
355 
356 		void TextProperties::ForceDefaultAttributes()
357 		{
358 			SdrTextObj& rObj = (SdrTextObj&)GetSdrObject();
359 
360 			if( rObj.GetObjInventor() == SdrInventor )
361 			{
362 				const sal_uInt16 nSdrObjKind = rObj.GetObjIdentifier();
363 
364 				if( nSdrObjKind == OBJ_TITLETEXT || nSdrObjKind == OBJ_OUTLINETEXT )
365 					return; // no defaults for presentation objects
366 			}
367 
368 			bool bTextFrame(rObj.IsTextFrame());
369 
370 			// force ItemSet
371 			GetObjectItemSet();
372 
373 			if(bTextFrame)
374 			{
375 				mpItemSet->Put(XLineStyleItem(XLINE_NONE));
376 				mpItemSet->Put(XFillColorItem(String(), Color(COL_WHITE)));
377 				mpItemSet->Put(XFillStyleItem(XFILL_NONE));
378 			}
379 			else
380 			{
381                 mpItemSet->Put(SvxAdjustItem(SVX_ADJUST_CENTER, EE_PARA_JUST));
382 				mpItemSet->Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER));
383 				mpItemSet->Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER));
384 			}
385 		}
386 
387 		void TextProperties::ForceStyleToHardAttributes()
388 		{
389 			// #i61284# call parent first to get the hard ObjectItemSet
390 			AttributeProperties::ForceStyleToHardAttributes();
391 
392 			// #i61284# push hard ObjectItemSet to OutlinerParaObject attributes
393 			// using existing functionality
394 			GetObjectItemSet(); // force ItemSet
395 			ItemSetChanged(*mpItemSet);
396 
397 			// now the standard TextProperties stuff
398 			SdrTextObj& rObj = (SdrTextObj&)GetSdrObject();
399 
400 			if(rObj.GetModel()
401 				&& !rObj.IsTextEditActive()
402 				&& !rObj.IsLinkedText())
403 			{
404 				Outliner* pOutliner = SdrMakeOutliner(OUTLINERMODE_OUTLINEOBJECT, rObj.GetModel());
405 				sal_Int32 nText = rObj.getTextCount();
406 
407 				while( --nText >= 0 )
408 				{
409 					SdrText* pText = rObj.getText( nText );
410 
411 					OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : 0;
412 					if( !pParaObj )
413 						continue;
414 
415 					pOutliner->SetText(*pParaObj);
416 
417 					sal_uInt32 nParaCount(pOutliner->GetParagraphCount());
418 
419 					if(nParaCount)
420 					{
421 						sal_Bool bBurnIn(sal_False);
422 
423 						for(sal_uInt16 nPara = 0; nPara < nParaCount; nPara++)
424 						{
425 							SfxStyleSheet* pSheet = pOutliner->GetStyleSheet(nPara);
426 
427 							if(pSheet)
428 							{
429 								SfxItemSet aParaSet(pOutliner->GetParaAttribs(nPara));
430 								SfxItemSet aSet(*aParaSet.GetPool());
431 								aSet.Put(pSheet->GetItemSet());
432 
433 								/** the next code handles a special case for paragraphs that contain a
434 									url field. The color for URL fields is either the system color for
435 									urls or the char color attribute that formats the portion in which the
436 									url field is contained.
437 									When we set a char color attribute to the paragraphs item set from the
438 									styles item set, we would have this char color attribute as an attribute
439 									that is spanned over the complete paragraph after xml import due to some
440 									problems in the xml import (using a XCursor on import so it does not know
441 									the paragraphs and can't set char attributes to paragraphs ).
442 
443 									To avoid this, as soon as we try to set a char color attribute from the style
444 									we
445 									1. check if we have at least one url field in this paragraph
446 									2. if we found at least one url field, we span the char color attribute over
447 									all portions that are not url fields and remove the char color attribute
448 									from the paragraphs item set
449 								*/
450 
451 								sal_Bool bHasURL(sal_False);
452 
453 								if(aSet.GetItemState(EE_CHAR_COLOR) == SFX_ITEM_SET)
454 								{
455 									EditEngine* pEditEngine = const_cast<EditEngine*>(&(pOutliner->GetEditEngine()));
456 									EECharAttribArray aAttribs;
457 									pEditEngine->GetCharAttribs((sal_uInt16)nPara, aAttribs);
458 									sal_uInt16 nAttrib;
459 
460 									for(nAttrib = 0; nAttrib < aAttribs.Count(); nAttrib++)
461 									{
462 										struct EECharAttrib aAttrib(aAttribs.GetObject(nAttrib));
463 
464 										if(EE_FEATURE_FIELD == aAttrib.pAttr->Which())
465 										{
466 											if(aAttrib.pAttr)
467 											{
468 												SvxFieldItem* pFieldItem = (SvxFieldItem*)aAttrib.pAttr;
469 
470 												if(pFieldItem)
471 												{
472 													const SvxFieldData* pData = pFieldItem->GetField();
473 
474 													if(pData && pData->ISA(SvxURLField))
475 													{
476 														bHasURL = sal_True;
477 														break;
478 													}
479 												}
480 											}
481 										}
482 									}
483 
484 									if(bHasURL)
485 									{
486 										SfxItemSet aColorSet(*aSet.GetPool(), EE_CHAR_COLOR, EE_CHAR_COLOR );
487 										aColorSet.Put(aSet, sal_False);
488 
489 										ESelection aSel((sal_uInt16)nPara, 0);
490 
491 										for(nAttrib = 0; nAttrib < aAttribs.Count(); nAttrib++)
492 										{
493 											struct EECharAttrib aAttrib(aAttribs.GetObject(nAttrib));
494 
495 											if(EE_FEATURE_FIELD == aAttrib.pAttr->Which())
496 											{
497 												aSel.nEndPos = aAttrib.nStart;
498 
499 												if(aSel.nStartPos != aSel.nEndPos)
500 												{
501 													pEditEngine->QuickSetAttribs(aColorSet, aSel);
502 												}
503 
504 												aSel.nStartPos = aAttrib.nEnd;
505 											}
506 										}
507 
508 										aSel.nEndPos = pEditEngine->GetTextLen((sal_uInt16)nPara);
509 
510 										if(aSel.nStartPos != aSel.nEndPos)
511 										{
512 											pEditEngine->QuickSetAttribs( aColorSet, aSel );
513 										}
514 									}
515 
516 								}
517 
518 								aSet.Put(aParaSet, sal_False);
519 
520 								if(bHasURL)
521 								{
522 									aSet.ClearItem(EE_CHAR_COLOR);
523 								}
524 
525 								pOutliner->SetParaAttribs(nPara, aSet);
526 								bBurnIn = sal_True; // #i51163# Flag was set wrong
527 							}
528 						}
529 
530 						if(bBurnIn)
531 						{
532 							OutlinerParaObject* pTemp = pOutliner->CreateParaObject(0, (sal_uInt16)nParaCount);
533 							rObj.NbcSetOutlinerParaObjectForText(pTemp,pText);
534 						}
535 					}
536 
537 					pOutliner->Clear();
538 				}
539 				delete pOutliner;
540 			}
541 		}
542 
543 		void TextProperties::SetObjectItemNoBroadcast(const SfxPoolItem& rItem)
544 		{
545             GetObjectItemSet();
546 			mpItemSet->Put(rItem);
547 		}
548 
549 
550 		void TextProperties::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
551 		{
552 			// call parent
553 			AttributeProperties::Notify(rBC, rHint);
554 
555 			SdrTextObj& rObj = (SdrTextObj&)GetSdrObject();
556 			if(rObj.HasText())
557 			{
558 				if(HAS_BASE(SfxStyleSheet, &rBC))
559 				{
560 					SfxSimpleHint* pSimple = PTR_CAST(SfxSimpleHint, &rHint);
561 					sal_uInt32 nId(pSimple ? pSimple->GetId() : 0L);
562 
563 					if(SFX_HINT_DATACHANGED == nId)
564 					{
565 						rObj.SetPortionInfoChecked(sal_False);
566 
567 						sal_Int32 nText = rObj.getTextCount();
568 						while( --nText > 0 )
569 						{
570 							OutlinerParaObject* pParaObj = rObj.getText(nText )->GetOutlinerParaObject();
571 							if( pParaObj )
572 								pParaObj->ClearPortionInfo();
573 						}
574 						rObj.SetTextSizeDirty();
575 
576 						if(rObj.IsTextFrame() && rObj.NbcAdjustTextFrameWidthAndHeight())
577 						{
578 							// here only repaint wanted
579 							rObj.ActionChanged();
580 							//rObj.BroadcastObjectChange();
581 						}
582 
583                         // #i101556# content of StyleSheet has changed -> new version
584                         maVersion++;
585 					}
586 
587 					if(SFX_HINT_DYING == nId)
588 					{
589 						rObj.SetPortionInfoChecked(sal_False);
590 						sal_Int32 nText = rObj.getTextCount();
591 						while( --nText > 0 )
592 						{
593 							OutlinerParaObject* pParaObj = rObj.getText(nText )->GetOutlinerParaObject();
594 							if( pParaObj )
595 								pParaObj->ClearPortionInfo();
596 						}
597 					}
598 				}
599 				else if(HAS_BASE(SfxStyleSheetBasePool, &rBC))
600 				{
601 					SfxStyleSheetHintExtended* pExtendedHint = PTR_CAST(SfxStyleSheetHintExtended, &rHint);
602 
603 					if(pExtendedHint
604 						&& SFX_STYLESHEET_MODIFIED == pExtendedHint->GetHint())
605 					{
606 						String aOldName(pExtendedHint->GetOldName());
607 						String aNewName(pExtendedHint->GetStyleSheet()->GetName());
608 						SfxStyleFamily eFamily = pExtendedHint->GetStyleSheet()->GetFamily();
609 
610 						if(!aOldName.Equals(aNewName))
611 						{
612 							sal_Int32 nText = rObj.getTextCount();
613 							while( --nText > 0 )
614 							{
615 								OutlinerParaObject* pParaObj = rObj.getText(nText )->GetOutlinerParaObject();
616 								if( pParaObj )
617 									pParaObj->ChangeStyleSheetName(eFamily, aOldName, aNewName);
618 							}
619 						}
620 					}
621 				}
622 			}
623 		}
624 
625         // #i101556# Handout version information
626         sal_uInt32 TextProperties::getVersion() const
627         {
628             return maVersion;
629         }
630 	} // end of namespace properties
631 } // end of namespace sdr
632 
633 //////////////////////////////////////////////////////////////////////////////
634 // eof
635