1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2
3 /* AbiWord
4 * Copyright (C) 1998 AbiSource, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301 USA.
20 */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26
27 #include "ap_Features.h"
28
29 #include "ut_assert.h"
30 #include "ut_string.h"
31 #include "ut_units.h"
32 #include "ut_debugmsg.h"
33
34 #include "xap_App.h"
35 #include "xap_Dialog_Id.h"
36 #include "xad_Document.h"
37 #include "xap_DialogFactory.h"
38 #include "xap_Dlg_MessageBox.h"
39 #include "xap_Prefs.h"
40 #include "fv_View.h"
41 #include "fl_DocLayout.h"
42
43 #include "ap_Dialog_Tab.h"
44 #include "ap_Prefs_SchemeIds.h"
45 #include "ap_TopRuler.h" // for AP_TopRulerInfo
46
47
48
AP_Dialog_Tab(XAP_DialogFactory * pDlgFactory,XAP_Dialog_Id id)49 AP_Dialog_Tab::AP_Dialog_Tab(XAP_DialogFactory * pDlgFactory, XAP_Dialog_Id id)
50 : XAP_Dialog_NonPersistent(pDlgFactory,id, "interface/dialogtabs"),
51 m_answer(a_OK),
52 m_pFrame(0),
53 m_dim(DIM_IN),
54 m_pszTabStops(0),
55 m_pCallbackFn(0),
56 m_closure(0)
57 {
58 m_pszTabStops = new char [1]; m_pszTabStops[0] = 0;
59 }
60
~AP_Dialog_Tab(void)61 AP_Dialog_Tab::~AP_Dialog_Tab(void)
62 {
63 DELETEPV(m_pszTabStops);
64 UT_VECTOR_PURGEALL(fl_TabStop *, m_tabInfo);
65 }
66
setSaveCallback(TabSaveCallBack pCb,void * closure)67 void AP_Dialog_Tab::setSaveCallback (TabSaveCallBack pCb, void * closure)
68 {
69 m_pCallbackFn = pCb;
70 m_closure = closure;
71 }
72
getAnswer(void) const73 AP_Dialog_Tab::tAnswer AP_Dialog_Tab::getAnswer(void) const
74 {
75 return m_answer;
76 }
77
_storeWindowData()78 void AP_Dialog_Tab::_storeWindowData()
79 {
80 UT_return_if_fail (m_pFrame); // needs to be set from runModal for some of the event_'s to work
81
82 FV_View *pView = (FV_View *)m_pFrame->getCurrentView();
83 UT_return_if_fail (pView && m_pCallbackFn);
84
85 (*m_pCallbackFn)(this, pView, m_pszTabStops, _gatherDefaultTabStop(), m_closure);
86 }
87
_populateWindowData(void)88 void AP_Dialog_Tab::_populateWindowData(void)
89 {
90 const gchar * szRulerUnits;
91 if (getApp()->getPrefsValue(AP_PREF_KEY_RulerUnits,&szRulerUnits))
92 m_dim = UT_determineDimension(szRulerUnits);
93 else
94 m_dim = DIM_IN;
95
96 UT_return_if_fail (m_pFrame); // needs to be set from runModal for some of the event_'s to work
97
98 FV_View *pView = (FV_View *)m_pFrame->getCurrentView();
99 UT_return_if_fail (pView);
100
101 // get the info used in the top ruler
102 AP_TopRulerInfo rulerInfo;
103 pView->getTopRulerInfo(&rulerInfo);
104
105 UT_DEBUGMSG(("AP_Dialog_Tab::_populateWindowData\n"));
106 UT_DEBUGMSG(("iTabStops=%d\tDefaultTabInterval=%d\ttabStops=%s\n",
107 rulerInfo.m_iTabStops,
108 rulerInfo.m_iDefaultTabInterval,
109 rulerInfo.m_pszTabStops
110 ));
111
112 // save the tab string
113 m_pszTabStops = new char[strlen(rulerInfo.m_pszTabStops) + 1];
114 strcpy(m_pszTabStops, rulerInfo.m_pszTabStops);
115
116 int iTab;
117 fl_TabStop *pTabInfo;
118 for ( iTab = 0; iTab < rulerInfo.m_iTabStops; iTab++ )
119 {
120
121 // create new tab info
122 pTabInfo = new fl_TabStop();
123 UT_return_if_fail (pTabInfo);
124
125
126 (*rulerInfo.m_pfnEnumTabStops)( rulerInfo.m_pVoidEnumTabStopsData,
127 iTab, pTabInfo);
128
129 m_tabInfo.addItem(pTabInfo);
130 }
131
132 _setTabList(m_tabInfo.getItemCount());
133 _setAlignment(FL_TAB_LEFT);
134
135 const gchar ** propsBlock = NULL;
136 pView->getBlockFormat(&propsBlock);
137
138 _setDefaultTabStop((const gchar *)"0");
139
140 if (propsBlock[0])
141 {
142 const gchar * sz;
143
144 sz = UT_getAttribute("default-tab-interval", propsBlock);
145
146 if(sz)
147 {
148 double inches = UT_convertToInches(sz);
149
150 _setDefaultTabStop((const gchar *)UT_convertInchesToDimensionString(m_dim, inches));
151 }
152
153 }
154
155 // enable/disable controls
156 _initEnableControls();
157 }
158
159
160 // The initialize the controls (i.e., disable controls not coded yet)
_initEnableControls()161 void AP_Dialog_Tab::_initEnableControls()
162 {
163 // alignment
164 _controlEnable( id_ALIGN_BAR, true );
165
166 // buttons
167 // Un-comment this once changes detailed below in something changed are implemented.
168 //_controlEnable( id_BUTTON_SET, false );
169 _controlEnable( id_BUTTON_SET, true );
170 _controlEnable( id_BUTTON_CLEAR, false );
171
172 _controlEnable( id_BUTTON_CLEAR_ALL, m_tabInfo.getItemCount() == 0 ? false : true );
173 }
174
_event_TabChange(void)175 void AP_Dialog_Tab::_event_TabChange(void)
176 {
177 _controlEnable(id_BUTTON_SET, true);
178 }
179
_event_AlignmentChange(void)180 void AP_Dialog_Tab::_event_AlignmentChange(void)
181 {
182 _controlEnable(id_BUTTON_SET, true);
183 }
184
185
_event_TabSelected(UT_sint32 index)186 void AP_Dialog_Tab::_event_TabSelected( UT_sint32 index )
187 {
188 UT_DEBUGMSG(("AP_Dialog_Tab::_event_TabSelected\n"));
189
190 if(index >= 0)
191 {
192 UT_return_if_fail (index < m_tabInfo.getItemCount());
193
194 fl_TabStop *pTabInfo = (fl_TabStop *)m_tabInfo.getNthItem(index);
195
196 // HACK - iType if 1..5 to be LEFT..BAR in the same order. We SHOULD make
197 // an enumerated type in block layout or something, or atleast define the
198 // common set of constants. ap_TopRuler.cpp defines all the constants
199 // again. Here, since enum is rel 0, i'm subtracting one and doing an
200 // ugly type cast
201 _setAlignment( pTabInfo->getType() );
202 _setLeader( pTabInfo->getLeader() );
203
204 _setTabEdit( _getTabDimensionString(index) );
205
206 // something changed...
207 _event_somethingChanged();
208 }
209 }
210
_event_Set(void)211 void AP_Dialog_Tab::_event_Set(void)
212 {
213 UT_String buffer;
214
215 // check the validity of the input
216 bool res = buildTab(buffer);
217 if (!res)
218 {
219 // TODO: add a message box here to inform our user - MARCM
220 return;
221 }
222
223 UT_DEBUGMSG(("DOM: %s\n", buffer.c_str()));
224
225 const char *cbuffer = buffer.c_str();
226 int Dimension_size = 0;
227 while(cbuffer[Dimension_size] != 0)
228 {
229
230 if(cbuffer[Dimension_size] == '/')
231 {
232 Dimension_size--;
233 break;
234 }
235
236 Dimension_size++;
237 }
238 UT_sint32 i;
239 // do we have the tab already.
240
241 for ( i = 0; i < m_tabInfo.getItemCount(); i++ )
242 {
243 fl_TabStop *pTabInfo = (fl_TabStop *)m_tabInfo.getNthItem(i);
244 UT_return_if_fail (pTabInfo);
245
246 // if we have a tab at that unit
247 if ( memcmp(cbuffer, _getTabString(pTabInfo), Dimension_size) == 0 )
248 {
249 // Delete the tab.
250
251 _deleteTabFromTabString(pTabInfo);
252
253 break;
254 }
255 }
256
257 // Add tab to list.
258
259 int NewOffset = strlen(m_pszTabStops);
260 char *p_temp = new char[NewOffset + 1 + strlen(cbuffer) + 1];
261 strcpy(p_temp, m_pszTabStops);
262 if(m_pszTabStops[0] != 0)
263 {
264 strcat(p_temp, ",");
265 }
266 strcat(p_temp, cbuffer);
267 delete [] m_pszTabStops;
268 m_pszTabStops = p_temp;
269
270 UT_return_if_fail (m_pFrame); // needs to be set from runModal for some of the event_'s to work
271
272 FV_View *pView = static_cast<FV_View *>(m_pFrame->getCurrentView());
273 UT_return_if_fail(pView);
274
275 buildTabStops(m_pszTabStops, m_tabInfo);
276
277 _setTabList(m_tabInfo.getItemCount());
278
279 // Select the new or changed tab in the list.
280
281 for (i = 0; i < m_tabInfo.getItemCount(); i++ )
282 {
283 fl_TabStop *pTabInfo = m_tabInfo.getNthItem(i);
284 UT_return_if_fail (pTabInfo);
285
286 // if we have a tab at that unit
287 if ( memcmp(cbuffer, _getTabString(pTabInfo), Dimension_size) == 0 )
288 {
289 _setSelectTab(i);
290 _setTabEdit( _getTabDimensionString(i) );
291 break;
292 }
293 }
294
295 // something changed...
296 _event_somethingChanged();
297
298 }
299
300 /*!
301 * Update the currently selected tab's properties.
302 * Ripped off from _event_Set(). This method does auto-apply.
303 */
_event_Update(void)304 void AP_Dialog_Tab::_event_Update(void)
305 {
306 fl_TabStop *pTabInfo1 = NULL;
307
308 // check the validity of the input
309 UT_String buffer;
310 bool res = buildTab(buffer);
311 if (!res)
312 {
313 // TODO: add a message box here to inform our user - MARCM
314 return;
315 }
316
317
318 // delete tab
319 UT_uint32 ndx = _gatherSelectTab();
320 pTabInfo1 = m_tabInfo.getNthItem(ndx);
321 _deleteTabFromTabString(pTabInfo1);
322 m_tabInfo.deleteNthItem(ndx);
323
324
325 // re-add
326 const char *cbuffer = buffer.c_str();
327 int Dimension_size = 0;
328 while(cbuffer[Dimension_size] != 0)
329 {
330
331 if(cbuffer[Dimension_size] == '/')
332 {
333 Dimension_size--;
334 break;
335 }
336
337 Dimension_size++;
338 }
339
340 // do we have the tab already.
341 UT_sint32 i;
342 for (i = 0; i < m_tabInfo.getItemCount(); i++ )
343 {
344 pTabInfo1 = (fl_TabStop *)m_tabInfo.getNthItem(i);
345 UT_return_if_fail (pTabInfo1);
346
347 // if we have a tab at that unit
348 if ( memcmp(cbuffer, _getTabString(pTabInfo1), Dimension_size) == 0 )
349 {
350 // Delete the tab.
351 _deleteTabFromTabString(pTabInfo1);
352 break;
353 }
354 }
355
356 // Add tab to list.
357 int NewOffset = strlen(m_pszTabStops);
358 char *p_temp = new char[NewOffset + 1 + strlen(cbuffer) + 1];
359 strcpy(p_temp, m_pszTabStops);
360 if(m_pszTabStops[0] != 0)
361 {
362 strcat(p_temp, ",");
363 }
364 strcat(p_temp, cbuffer);
365 delete [] m_pszTabStops;
366 m_pszTabStops = p_temp;
367
368 UT_return_if_fail (m_pFrame); // needs to be set from runModal for some of the event_'s to work
369
370 FV_View *pView = static_cast<FV_View *>(m_pFrame->getCurrentView());
371 UT_return_if_fail(pView);
372
373 buildTabStops(m_pszTabStops, m_tabInfo);
374
375 _setTabList(m_tabInfo.getItemCount());
376
377 // Select the new or changed tab in the list.
378
379 for (i = 0; i < m_tabInfo.getItemCount(); i++ )
380 {
381 fl_TabStop *pTabInfo = m_tabInfo.getNthItem(i);
382 UT_return_if_fail (pTabInfo);
383
384 // if we have a tab at that unit
385 if ( memcmp(cbuffer, _getTabString(pTabInfo), Dimension_size) == 0 )
386 {
387 _setSelectTab(i);
388 _setTabEdit( _getTabDimensionString(i) );
389 break;
390 }
391 }
392
393 // something changed...
394 _event_somethingChanged();
395 _storeWindowData ();
396 }
397
_event_Clear(void)398 void AP_Dialog_Tab::_event_Clear(void)
399 {
400 UT_DEBUGMSG(("AP_Dialog_Tab::_event_Clear\n"));
401
402 UT_sint32 index = _gatherSelectTab();
403
404 if(index != -1)
405 {
406 UT_return_if_fail(index < m_tabInfo.getItemCount());
407
408 _deleteTabFromTabString(m_tabInfo.getNthItem(index));
409
410 UT_return_if_fail(m_pFrame); // needs to be set from runModal for some of the event_'s to work
411
412 buildTabStops(m_pszTabStops, m_tabInfo);
413
414 _setTabList(m_tabInfo.getItemCount());
415
416 if(m_tabInfo.getItemCount() > 0)
417 {
418 _setSelectTab(0);
419 _event_TabSelected(0);
420 }
421 else
422 {
423 _setSelectTab(-1);
424 }
425
426 // something changed...
427 _event_somethingChanged();
428 }
429 }
430
_event_ClearAll(void)431 void AP_Dialog_Tab::_event_ClearAll(void)
432 {
433 UT_DEBUGMSG(("AP_Dialog_Tab::_event_ClearAll\n"));
434
435 UT_return_if_fail(m_pFrame); // needs to be set from runModal for some of the event_'s to work
436
437 delete [] m_pszTabStops;
438 m_pszTabStops = new char [1]; m_pszTabStops[0] = 0;
439
440 buildTabStops(m_pszTabStops, m_tabInfo);
441
442 _clearList();
443
444 // something changed...
445 _event_somethingChanged();
446 }
447
AlignmentToChar(eTabType a)448 /*static*/ unsigned char AP_Dialog_Tab::AlignmentToChar( eTabType a )
449 {
450 char ch;
451
452 switch ( a )
453 {
454 case FL_TAB_LEFT:
455 ch = 'L';
456 break;
457
458 case FL_TAB_RIGHT:
459 ch = 'R';
460 break;
461
462 case FL_TAB_CENTER:
463 ch = 'C';
464 break;
465
466 case FL_TAB_DECIMAL:
467 ch = 'D';
468 break;
469
470 case FL_TAB_BAR:
471 ch = 'B';
472 break;
473
474 default:
475 UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED);
476 ch = 'L';
477 break;
478 }
479
480 return ch;
481 }
482
CharToAlignment(unsigned char ch)483 /*static*/ eTabType AP_Dialog_Tab::CharToAlignment( unsigned char ch )
484 {
485 eTabType a;
486 switch ( ch )
487 {
488 case 'L':
489 a = FL_TAB_LEFT;
490 break;
491
492 case 'R':
493 a = FL_TAB_RIGHT;
494 break;
495
496 case 'C':
497 a = FL_TAB_CENTER;
498 break;
499
500 case 'D':
501 a = FL_TAB_DECIMAL;
502 break;
503
504 case 'B': // not implemented, fall though
505 a = FL_TAB_BAR;
506 break;
507
508 default:
509 UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED);
510 a = FL_TAB_LEFT;
511 }
512 return a;
513 }
514
clearList()515 void AP_Dialog_Tab::clearList()
516 {
517 _clearList();
518
519 UT_VECTOR_PURGEALL(fl_TabStop *, m_tabInfo);
520 }
521
522
buildTab(UT_String & buffer)523 bool AP_Dialog_Tab::buildTab( UT_String & buffer )
524 {
525 // get current value from member
526 const gchar* szOld = _gatherTabEdit();
527 bool res = UT_isValidDimensionString(szOld, MAX_TAB_LENGTH);
528 if (res)
529 {
530 const gchar* szNew = UT_reformatDimensionString(m_dim, szOld);
531
532 UT_String_sprintf( buffer, "%s/%c%c", szNew, AlignmentToChar(_gatherAlignment()),
533 (static_cast<char>(_gatherLeader()))+'0');
534 }
535
536 return res;
537 }
538
_event_somethingChanged()539 void AP_Dialog_Tab::_event_somethingChanged()
540 {
541 UT_String buffer;
542
543 buildTab( buffer );
544 const char *cbuffer = buffer.c_str();
545 UT_DEBUGMSG(("AP_Dialog_Tab::_event_somethingChanged [%s]\n", cbuffer ));
546
547 // check to see if the current tab is in the list
548 bool bEnableClear = false;
549 bool bEnableSet = true; // only disabled if current selection exactly matches current ones
550 // or there are no items in the list.
551
552 // this just looks broken for the initial tab thingie.
553 #if 0
554 if(m_tabInfo.getItemCount() == 0)
555 {
556 bEnableSet = false;
557 }
558 #endif
559
560 for ( UT_sint32 i = 0; i < m_tabInfo.getItemCount(); i++ )
561 {
562 fl_TabStop *pTabInfo = m_tabInfo.getNthItem(i);
563 UT_return_if_fail (pTabInfo);
564
565 // if we have a tab at that unit
566 if ( !strcmp(cbuffer, _getTabString(pTabInfo)) )
567 {
568 bEnableClear = true;
569
570 // if everything is the same, disable the set
571 if ( pTabInfo->getType() == _gatherAlignment() &&
572 pTabInfo->getLeader() == _gatherLeader() ){
573 // Disabled to fix bug 5143 and match behavior in the remainder of the program. TODO: Cause focus to shift to OK button here,
574 // and beef up the enable/disable routines for the set button. Then, this can be re-enabled.
575 // bEnableSet = false;
576 }
577
578 }
579 }
580
581 _controlEnable( id_BUTTON_SET, bEnableSet );
582 _controlEnable( id_BUTTON_CLEAR, bEnableClear );
583
584 _controlEnable( id_BUTTON_CLEAR_ALL, m_tabInfo.getItemCount() == 0 ? false : true );
585
586 }
587
_getTabDimensionString(UT_sint32 tabIndex)588 char *AP_Dialog_Tab::_getTabDimensionString(UT_sint32 tabIndex)
589 {
590
591 UT_return_val_if_fail (tabIndex < m_tabInfo.getItemCount(), NULL);
592
593 fl_TabStop *pTabInfo = m_tabInfo.getNthItem(tabIndex);
594
595 const char* pStart = &m_pszTabStops[pTabInfo->getOffset()];
596 const char* pEnd = pStart;
597 while (*pEnd && (*pEnd != '/'))
598 {
599 pEnd++;
600 }
601
602 UT_uint32 iLen = pEnd - pStart;
603 UT_return_val_if_fail (iLen<20, NULL);
604
605 strncpy(buf, pStart, iLen);
606 buf[iLen]=0;
607
608 return buf;
609 }
610
_getTabString(fl_TabStop * pTabInfo)611 char *AP_Dialog_Tab::_getTabString(fl_TabStop *pTabInfo)
612 {
613 const char* pStart = &m_pszTabStops[pTabInfo->getOffset()];
614 const char* pEnd = pStart;
615 while (*pEnd && (*pEnd != ','))
616 {
617 pEnd++;
618 }
619
620 UT_uint32 iLen = pEnd - pStart;
621
622 strncpy(buf, pStart, iLen);
623 buf[iLen]=0;
624
625 return buf;
626 }
627
_deleteTabFromTabString(fl_TabStop * pTabInfo)628 void AP_Dialog_Tab::_deleteTabFromTabString(fl_TabStop *pTabInfo)
629 {
630 int Tab_data_size = 0;
631 int Offset = pTabInfo->getOffset();
632
633 while(m_pszTabStops[Offset + Tab_data_size] != 0)
634 {
635 if(m_pszTabStops[Offset + Tab_data_size] == ',')
636 {
637 break;
638 }
639
640 Tab_data_size++;
641 }
642
643 if(Offset > 0)
644 {
645 // include leading comma.
646 Offset--;
647 Tab_data_size++;
648 }
649
650 if(Offset == 0)
651 {
652 // include trailing comma if there is one.
653
654 if(m_pszTabStops[Offset + Tab_data_size] == ',')
655 {
656 Tab_data_size++;
657 }
658 }
659
660
661
662 memmove(m_pszTabStops + Offset,
663 m_pszTabStops + Offset + Tab_data_size,
664 strlen(m_pszTabStops) - (Offset + Tab_data_size));
665
666 m_pszTabStops[strlen(m_pszTabStops) - Tab_data_size] = 0;
667 }
668
669 #define SPIN_INCR_IN 0.1
670 #define SPIN_INCR_CM 0.5
671 #define SPIN_INCR_MM 1.0
672 #define SPIN_INCR_PI 6.0
673 #define SPIN_INCR_PT 1.0
674 #define SPIN_INCR_none 0.1
675
676
_doSpin(tControl id,UT_sint32 amt)677 void AP_Dialog_Tab::_doSpin(tControl id, UT_sint32 amt)
678 {
679 UT_DEBUGMSG (("ROB: _doSpin %d, %d\n", id, amt));
680
681 // UT_ASSERT(amt); // zero makes no sense
682 UT_return_if_fail (id == id_SPIN_DEFAULT_TAB_STOP);
683 if(amt == 0 )
684 {
685 UT_DEBUGMSG(("AMOUNT = 0 amt = %d \n",amt));
686 }
687 // get current value from member
688 const gchar* szOld = _gatherDefaultTabStop();
689 double d = UT_convertDimensionless(szOld);
690
691 // figure out which dimension and units to spin in
692 UT_Dimension dimSpin = m_dim;
693 double dSpinUnit = SPIN_INCR_PT;
694 double dMin = 0.0;
695 switch (dimSpin)
696 {
697 case DIM_IN:
698 dSpinUnit = SPIN_INCR_IN;
699 dMin = 0.1;
700 break;
701
702 case DIM_CM:
703 dSpinUnit = SPIN_INCR_CM;
704 dMin = 0.1;
705 break;
706
707 case DIM_MM:
708 dSpinUnit = SPIN_INCR_MM;
709 dMin = 1.0;
710 break;
711
712 case DIM_PI:
713 dSpinUnit = SPIN_INCR_PI;
714 dMin = 6.0;
715 break;
716
717 case DIM_PT:
718 dSpinUnit = SPIN_INCR_PT;
719 dMin = 1.0;
720 break;
721 default:
722
723 UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
724 break;
725 }
726
727 // figure out spin precision, too
728 const char * szPrecision = ".1";
729 if ((dimSpin == DIM_PT) ||
730 (dimSpin == DIM_PI))
731 szPrecision = ".0";
732
733 // if needed, switch unit systems and round off
734 UT_Dimension dimOld = UT_determineDimension(szOld, dimSpin);
735
736 if (dimOld != dimSpin)
737 {
738 double dInches = UT_convertToInches(szOld);
739 d = UT_convertInchesToDimension(dInches, dimSpin);
740 }
741
742 // value is now in desired units, so change it
743 d += (dSpinUnit * static_cast<double>(amt));
744 if (d < dMin)
745 d = dMin;
746
747 const gchar* szNew = UT_formatDimensionString(dimSpin, d, szPrecision);
748
749 _setDefaultTabStop(szNew);
750 }
751