xref: /trunk/main/sw/source/ui/misc/glosbib.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 #ifdef SW_DLLIMPLEMENTATION
31 #undef SW_DLLIMPLEMENTATION
32 #endif
33 
34 
35 
36 #define _SVSTDARR_STRINGS
37 #include <tools/urlobj.hxx>
38 #include <tools/stream.hxx>
39 #ifndef _MSGBOX_HXX //autogen
40 #include <vcl/msgbox.hxx>
41 #endif
42 #include <vcl/help.hxx>
43 #include <unotools/transliterationwrapper.hxx>
44 #include <unotools/tempfile.hxx>
45 
46 #include <svl/svstdarr.hxx>
47 #include <unotools/pathoptions.hxx>
48 #include <swtypes.hxx>
49 #include <glosbib.hxx>
50 #include <gloshdl.hxx>
51 #include <actctrl.hxx>
52 #include <glossary.hxx>
53 #include <glosdoc.hxx>
54 #include <swunohelper.hxx>
55 
56 #ifndef _GLOSBIB_HRC
57 #include <glosbib.hrc>
58 #endif
59 #ifndef _MISC_HRC
60 #include <misc.hrc>
61 #endif
62 #ifndef _HELPID_H
63 #include <helpid.h>
64 #endif
65 
66 
67 #define PATH_CASE_SENSITIVE 0x01
68 #define PATH_READONLY       0x02
69 
70 #define RENAME_TOKEN_DELIM      (sal_Unicode)1
71 
72 /*-----------------09.06.97 13:05-------------------
73 
74 --------------------------------------------------*/
75 SwGlossaryGroupDlg::SwGlossaryGroupDlg(Window * pParent,
76                         const SvStrings* pPathArr,
77                         SwGlossaryHdl *pHdl) :
78     SvxStandardDialog(pParent, SW_RES(DLG_BIB_BASE)),
79     aBibFT(     this, SW_RES(FT_BIB)),
80     aNameED(    this, SW_RES(ED_NAME)),
81     aPathFT(     this, SW_RES(FT_PATH)),
82     aPathLB(    this, SW_RES(LB_PATH)),
83     aSelectFT(   this, SW_RES(FT_SELECT)),
84     aGroupTLB(  this, SW_RES(TLB_GROUPS)),
85 
86     aOkPB(      this, SW_RES(BT_OK)),
87     aCancelPB(  this, SW_RES(BT_CANCEL)),
88     aHelpPB(    this, SW_RES(BT_HELP)),
89     aNewPB(     this, SW_RES(PB_NEW)),
90     aDelPB(     this, SW_RES(PB_DELETE)),
91     aRenamePB(  this, SW_RES(PB_RENAME)),
92 
93     pRemovedArr(0),
94     pInsertedArr(0),
95     pRenamedArr(0),
96     pGlosHdl(pHdl)
97 {
98     sal_uInt16 i;
99 
100     FreeResource();
101 
102     long nTabs[] =
103     {   2, // Number of Tabs
104         0, 160
105     };
106 
107     aGroupTLB.SetHelpId(HID_GLOS_GROUP_TREE);
108     aGroupTLB.SetTabs( &nTabs[0], MAP_APPFONT );
109     aGroupTLB.SetStyle(aGroupTLB.GetStyle()|WB_HSCROLL|WB_CLIPCHILDREN|WB_SORT);
110     aGroupTLB.SetSelectHdl(LINK(this, SwGlossaryGroupDlg, SelectHdl));
111     aGroupTLB.GetModel()->SetSortMode(SortAscending);
112     aNewPB.SetClickHdl(LINK(this, SwGlossaryGroupDlg, NewHdl));
113     aDelPB.SetClickHdl(LINK(this, SwGlossaryGroupDlg, DeleteHdl));
114     aNameED.SetModifyHdl(LINK(this, SwGlossaryGroupDlg, ModifyHdl));
115     aPathLB.SetSelectHdl(LINK(this, SwGlossaryGroupDlg, ModifyHdl));
116     aRenamePB.SetClickHdl(LINK(this, SwGlossaryGroupDlg, RenameHdl));
117     for( i = 0; i < pPathArr->Count(); i++)
118     {
119         String sPath(*(*pPathArr)[i]);
120         INetURLObject aTempURL(sPath);
121         sPath = aTempURL.GetMainURL(INetURLObject::DECODE_WITH_CHARSET );
122         aPathLB.InsertEntry(sPath);
123         sal_uLong nCaseReadonly = 0;
124         utl::TempFile aTempFile(&sPath);
125         aTempFile.EnableKillingFile();
126         if(!aTempFile.IsValid())
127             nCaseReadonly |= PATH_READONLY;
128         else if( SWUnoHelper::UCB_IsCaseSensitiveFileName( aTempFile.GetURL()))
129             nCaseReadonly |= PATH_CASE_SENSITIVE;
130         aPathLB.SetEntryData(i, (void*)nCaseReadonly);
131     }
132     aPathLB.SelectEntryPos(0);
133     aPathLB.Enable(sal_True);
134 
135     const sal_uInt16 nCount = pHdl->GetGroupCnt();
136     for(i = 0; i < nCount; ++i)
137     {
138         String sTitle;
139         String sGroup = pHdl->GetGroupName(i, &sTitle);
140         if(!sGroup.Len())
141             continue;
142         GlosBibUserData* pData = new GlosBibUserData;
143         pData->sGroupName = sGroup;
144         pData->sGroupTitle = sTitle;
145         String sTemp(sTitle);
146         //sGroup.GetToken(0, GLOS_DELIM)
147         sTemp += '\t';
148         pData->sPath = aPathLB.GetEntry((sal_uInt16)sGroup.GetToken(1, GLOS_DELIM).ToInt32());
149         sTemp += pData->sPath;
150         SvLBoxEntry* pEntry = aGroupTLB.InsertEntry(sTemp);
151         pEntry->SetUserData(pData);
152 
153     }
154     aGroupTLB.GetModel()->Resort();
155 }
156 
157 /*-----------------09.06.97 13:05-------------------
158 
159 --------------------------------------------------*/
160 SwGlossaryGroupDlg::~SwGlossaryGroupDlg()
161 {
162 
163     if(pInsertedArr)
164     {
165         pInsertedArr->DeleteAndDestroy(0, pInsertedArr->Count());
166         delete pInsertedArr;
167     }
168     if(pRemovedArr)
169     {
170         pRemovedArr->DeleteAndDestroy(0, pRemovedArr->Count());
171         delete pRemovedArr;
172     }
173     if(pRenamedArr)
174     {
175         pRenamedArr->DeleteAndDestroy(0, pRenamedArr->Count());
176         delete pRenamedArr;
177     }
178 
179 }
180 
181 /*-----------------09.06.97 13:11-------------------
182 
183 --------------------------------------------------*/
184 
185 void __EXPORT SwGlossaryGroupDlg::Apply()
186 {
187     if(aNewPB.IsEnabled())
188         NewHdl(&aNewPB);
189 
190     String aActGroup = SwGlossaryDlg::GetCurrGroup();
191 
192     if(pRemovedArr && pRemovedArr->Count())
193     {
194         sal_uInt16 nCount = pRemovedArr->Count();
195         for(sal_uInt16 i = 0; i < nCount; ++i)
196         {
197             const String* pDelEntry = (*pRemovedArr)[i];
198             const String sDelGroup = pDelEntry->GetToken(0, '\t');
199             if( sDelGroup == aActGroup )
200             {
201                 //soll die aktuelle Gruppe geloescht werden, muss die akt. Gruppe
202                 //umgesetzt werden
203                 if(aGroupTLB.GetEntryCount())
204                 {
205                     SvLBoxEntry* pFirst = aGroupTLB.First();
206                     GlosBibUserData* pUserData = (GlosBibUserData*)pFirst->GetUserData();
207                     pGlosHdl->SetCurGroup(pUserData->sGroupName);
208                 }
209             }
210             String sMsg(SW_RES(STR_QUERY_DELETE_GROUP1));
211             String sTitle(pDelEntry->GetToken(1, '\t'));
212             if(sTitle.Len())
213                 sMsg += sTitle;
214             else
215                 sDelGroup.GetToken(1, GLOS_DELIM);
216             sMsg += SW_RESSTR(STR_QUERY_DELETE_GROUP2);
217             QueryBox aQuery(this->GetParent(), WB_YES_NO|WB_DEF_NO, sMsg );
218             if(RET_YES == aQuery.Execute())
219                 pGlosHdl->DelGroup( sDelGroup );
220         }
221 
222     }
223     //erst umbenennen, falls es schon eins gab
224     if(pRenamedArr && pRenamedArr->Count())
225     {
226         sal_uInt16 nCount = pRenamedArr->Count();
227         for(sal_uInt16 i = 0; i < nCount; ++i)
228         {
229             String * pEntry = (*pRenamedArr)[i];
230             xub_StrLen nStrSttPos = 0;
231             String sOld( pEntry->GetToken(0, RENAME_TOKEN_DELIM, nStrSttPos ) );
232             String sNew( pEntry->GetToken(0, RENAME_TOKEN_DELIM, nStrSttPos) );
233             String sTitle( pEntry->GetToken(0, RENAME_TOKEN_DELIM, nStrSttPos) );
234             pGlosHdl->RenameGroup(sOld, sNew, sTitle);
235             if(!i)
236                 sCreatedGroup = sNew;
237         }
238     }
239     if(pInsertedArr && pInsertedArr->Count())
240     {
241         sal_uInt16 nCount = pInsertedArr->Count();
242         for(sal_uInt16 i = 0; i < nCount; ++i)
243         {
244             String sNewGroup = *(*pInsertedArr)[i];
245             String sNewTitle = sNewGroup.GetToken(0, GLOS_DELIM);
246             if( *(*pInsertedArr)[i] != aActGroup )
247             {
248                 pGlosHdl->NewGroup(sNewGroup, sNewTitle);
249                 if(!sCreatedGroup.Len())
250                     sCreatedGroup = sNewGroup;
251             }
252         }
253     }
254 }
255 /*-----------------09.06.97 13:12-------------------
256 
257 --------------------------------------------------*/
258 IMPL_LINK( SwGlossaryGroupDlg, SelectHdl, SvTabListBox*, EMPTYARG  )
259 {
260     aNewPB.Enable(sal_False);
261     SvLBoxEntry* pFirstEntry = aGroupTLB.FirstSelected();
262     if(pFirstEntry)
263     {
264         GlosBibUserData* pUserData = (GlosBibUserData*)pFirstEntry->GetUserData();
265         String sEntry(pUserData->sGroupName);
266         String sName(aNameED.GetText());
267         sal_Bool bExists = sal_False;
268         sal_uLong nPos = aGroupTLB.GetEntryPos(sName, 0);
269         if( 0xffffffff > nPos)
270         {
271             SvLBoxEntry* pEntry = aGroupTLB.GetEntry(nPos);
272             GlosBibUserData* pFoundData = (GlosBibUserData*)pEntry->GetUserData();
273             String sGroup = pFoundData->sGroupName;
274             bExists = sGroup == sEntry;
275         }
276 
277         aRenamePB.Enable(!bExists && sName.Len());
278         aDelPB.Enable(IsDeleteAllowed(sEntry));
279     }
280     return 0;
281 }
282 
283 /*-----------------09.06.97 13:22-------------------
284 
285 --------------------------------------------------*/
286 IMPL_LINK( SwGlossaryGroupDlg, NewHdl, Button*, EMPTYARG )
287 {
288     String sGroup(aNameED.GetText());
289 //  sGroup.ToLower();
290     sGroup += GLOS_DELIM;
291     sGroup += String::CreateFromInt32(aPathLB.GetSelectEntryPos());
292     DBG_ASSERT(!pGlosHdl->FindGroupName(sGroup), "Gruppe bereits vorhanden!");
293     if(!pInsertedArr)
294         pInsertedArr = new SvStrings;
295     pInsertedArr->Insert(new String(sGroup), pInsertedArr->Count());
296     String sTemp(aNameED.GetText());
297 //  sTemp.ToLower();
298     sTemp += '\t';
299     sTemp += aPathLB.GetSelectEntry();
300     SvLBoxEntry* pEntry = aGroupTLB.InsertEntry(sTemp);
301     GlosBibUserData* pData = new GlosBibUserData;
302     pData->sPath = aPathLB.GetSelectEntry();
303     pData->sGroupName = sGroup;
304     pData->sGroupTitle = aNameED.GetText();
305     pEntry->SetUserData(pData);
306     aGroupTLB.Select(pEntry);
307     aGroupTLB.MakeVisible(pEntry);
308     aGroupTLB.GetModel()->Resort();
309 
310     return 0;
311 }
312 /*-----------------09.06.97 13:22-------------------
313 
314 --------------------------------------------------*/
315 IMPL_LINK( SwGlossaryGroupDlg, DeleteHdl, Button*, pButton  )
316 {
317     SvLBoxEntry* pEntry = aGroupTLB.FirstSelected();
318     if(!pEntry)
319     {
320         pButton->Enable(sal_False);
321         return 0;
322     }
323     GlosBibUserData* pUserData = (GlosBibUserData*)pEntry->GetUserData();
324     String sEntry(pUserData->sGroupName);
325     // befindet sich der zu loeschende Name schon unter den
326     // den neuen - dann weg damit
327     sal_Bool bDelete = sal_True;
328     if(pInsertedArr && pInsertedArr->Count())
329     {
330         sal_uInt16 nCount = pInsertedArr->Count();
331         for(sal_uInt16 i = 0; i < nCount; ++i)
332         {
333             const String* pTemp = (*pInsertedArr)[i];
334             if(*pTemp == sEntry)
335             {
336                 pInsertedArr->Remove(i);
337                 bDelete = sal_False;
338                 break;
339             }
340 
341         }
342     }
343     // moeglicherweise sollte es schon umbenannt werden?
344     if(bDelete)
345     {
346         if(pRenamedArr && pRenamedArr->Count())
347         {
348             sal_uInt16 nCount = pRenamedArr->Count();
349             for(sal_uInt16 i = 0; i < nCount; ++i)
350             {
351                 const String* pTemp = (*pRenamedArr)[i];
352                 String sTemp( pTemp->GetToken(0, RENAME_TOKEN_DELIM ));
353                 if(sTemp == sEntry)
354                 {
355                     pRenamedArr->Remove(i);
356                     bDelete = sal_False;
357                     break;
358                 }
359             }
360         }
361     }
362     if(bDelete)
363     {
364         if(!pRemovedArr)
365             pRemovedArr = new SvStrings;
366         String sGroupEntry(pUserData->sGroupName);
367         sGroupEntry += '\t';
368         sGroupEntry += pUserData->sGroupTitle;
369         pRemovedArr->Insert(new String(sGroupEntry), pRemovedArr->Count());
370     }
371     delete pUserData;
372     aGroupTLB.GetModel()->Remove(pEntry);
373     if(!aGroupTLB.First())
374         pButton->Enable(sal_False);
375     //the content must be deleted - otherwise the new handler would be called in Apply()
376     aNameED.SetText(aEmptyStr);
377     return 0;
378 }
379 
380 /* -----------------23.11.98 12:26-------------------
381  *
382  * --------------------------------------------------*/
383 IMPL_LINK( SwGlossaryGroupDlg, RenameHdl, Button *, EMPTYARG )
384 {
385     SvLBoxEntry* pEntry = aGroupTLB.FirstSelected();
386     GlosBibUserData* pUserData = (GlosBibUserData*)pEntry->GetUserData();
387     String sEntryText(aGroupTLB.GetEntryText(pEntry));
388     String sEntry(pUserData->sGroupName);
389 
390     String sNewName(aNameED.GetText());
391     String sNewTitle(sNewName);
392 
393     sNewName += GLOS_DELIM;
394     sNewName += String::CreateFromInt32(aPathLB.GetSelectEntryPos());
395     DBG_ASSERT(!pGlosHdl->FindGroupName(sNewName), "Gruppe bereits vorhanden!");
396 
397     // befindet sich der umzubenennende Name unter den
398     // den neuen - dann austauschen
399     sal_Bool bDone = sal_False;
400     if(pInsertedArr && pInsertedArr->Count())
401     {
402         sal_uInt16 nCount = pInsertedArr->Count();
403         for(sal_uInt16 i = 0; i < nCount; ++i)
404         {
405             const String* pTemp = (*pInsertedArr)[i];
406             if(*pTemp == sEntry)
407             {
408                 pInsertedArr->Remove(i);
409                 pInsertedArr->Insert(new String(sNewName), pInsertedArr->Count());
410                 bDone = sal_True;
411                 break;
412             }
413         }
414     }
415     if(!bDone)
416     {
417         if(!pRenamedArr)
418             pRenamedArr = new SvStrings;
419         sEntry += RENAME_TOKEN_DELIM;
420         sEntry += sNewName;
421         sEntry += RENAME_TOKEN_DELIM;
422         sEntry += sNewTitle;
423         pRenamedArr->Insert(new String(sEntry), pRenamedArr->Count());
424     }
425     delete (GlosBibUserData*)pEntry->GetUserData();
426     aGroupTLB.GetModel()->Remove(pEntry);
427     String sTemp(aNameED.GetText());
428 //  sTemp.ToLower();
429     sTemp += '\t';
430     sTemp += aPathLB.GetSelectEntry();
431     pEntry = aGroupTLB.InsertEntry(sTemp);
432     GlosBibUserData* pData = new GlosBibUserData;
433     pData->sPath = aPathLB.GetSelectEntry();
434     pData->sGroupName = sNewName;
435     pData->sGroupTitle = sNewTitle;
436     pEntry->SetUserData(pData);
437     aGroupTLB.Select(pEntry);
438     aGroupTLB.MakeVisible(pEntry);
439     aGroupTLB.GetModel()->Resort();
440     return 0;
441 }
442 /*-----------------09.06.97 13:42-------------------
443 
444 --------------------------------------------------*/
445 IMPL_LINK( SwGlossaryGroupDlg, ModifyHdl, Edit*, EMPTYARG )
446 {
447     String sEntry(aNameED.GetText());
448 //  sEntry.ToLower();
449     sal_Bool bEnableNew = sal_True;
450     sal_Bool bEnableDel = sal_False;
451     sal_uLong nCaseReadonly =
452             (sal_uLong)aPathLB.GetEntryData(aPathLB.GetSelectEntryPos());
453     sal_Bool bDirReadonly = 0 != (nCaseReadonly&PATH_READONLY);
454 
455     if(!sEntry.Len() || bDirReadonly)
456         bEnableNew = sal_False;
457     else if(sEntry.Len())
458     {
459         sal_uLong nPos = 0xffffffff;
460 
461 
462         nPos = aGroupTLB.GetEntryPos(sEntry, 0);
463         //ist es nicht case sensitive muss man selbst suchen
464         if( 0xffffffff == nPos)
465         {
466             const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
467             for(sal_uInt16 i = 0; i < aGroupTLB.GetEntryCount(); i++)
468             {
469                 String sTemp = aGroupTLB.GetEntryText( i, 0 );
470                 nCaseReadonly = (sal_uLong)aPathLB.GetEntryData(
471                     aPathLB.GetEntryPos(aGroupTLB.GetEntryText(i,1)));
472                 sal_Bool bCase = 0 != (nCaseReadonly & PATH_CASE_SENSITIVE);
473 
474                 if( !bCase && rSCmp.isEqual( sTemp, sEntry ))
475                 {
476                     nPos = i;
477                     break;
478                 }
479             }
480         }
481         if( 0xffffffff > nPos)
482         {
483             bEnableNew = sal_False;
484             aGroupTLB.Select(aGroupTLB.GetEntry( nPos ));
485             aGroupTLB.MakeVisible(aGroupTLB.GetEntry( nPos ));
486         }
487     }
488     SvLBoxEntry* pEntry = aGroupTLB.FirstSelected();
489     if(pEntry)
490     {
491         GlosBibUserData* pUserData = (GlosBibUserData*)pEntry->GetUserData();
492         bEnableDel = IsDeleteAllowed(pUserData->sGroupName);
493 
494 //      String sGroup = aGroupTLB.GetEntryText(pEntry, 0);
495 //      sGroup += GLOS_DELIM;
496 //      sGroup += String::CreateFromInt32(aPathLB.GetEntryPos(aGroupTLB.GetEntryText(pEntry, 1)));
497 //      bEnableDel = IsDeleteAllowed(sGroup);
498     }
499 
500     aDelPB.Enable(bEnableDel);
501     aNewPB.Enable(bEnableNew);
502     aRenamePB.Enable(bEnableNew && pEntry);
503     return 0;
504 }
505 
506 /*------------------------------------------------------------------------
507  Beschreibung:
508 ------------------------------------------------------------------------*/
509 
510 sal_Bool SwGlossaryGroupDlg::IsDeleteAllowed(const String &rGroup)
511 {
512     sal_Bool bDel = (!pGlosHdl->IsReadOnly(&rGroup));
513 
514     // OM: befindet sich der Name unter den den neuen Bereichsnamen,
515     // dann ist er auch loeschbar! Bei noch nicht existenten Bereichsnamen
516     // liefert ReadOnly naemlich sal_True.
517 
518     if(pInsertedArr && pInsertedArr->Count())
519     {
520         sal_uInt16 nCount = pInsertedArr->Count();
521         for(sal_uInt16 i = 0; i < nCount; ++i)
522         {
523             const String* pTemp = (*pInsertedArr)[i];
524             if(*pTemp == rGroup)
525             {
526                 bDel = sal_True;
527                 break;
528             }
529         }
530     }
531 
532     return bDel;
533 }
534 
535 /*-----------------18.07.97 19:06-------------------
536 
537 --------------------------------------------------*/
538 void FEdit::KeyInput( const KeyEvent& rKEvent )
539 {
540     KeyCode aCode = rKEvent.GetKeyCode();
541     if( KEYGROUP_CURSOR == aCode.GetGroup() ||
542         ( KEYGROUP_MISC == aCode.GetGroup() &&
543           KEY_DELETE >= aCode.GetCode() ) ||
544         SVT_SEARCHPATH_DELIMITER != rKEvent.GetCharCode() )
545         Edit::KeyInput( rKEvent );
546 }
547 /* -----------------------------08.02.00 15:07--------------------------------
548 
549  ---------------------------------------------------------------------------*/
550 void    SwGlossaryGroupTLB::RequestHelp( const HelpEvent& rHEvt )
551 {
552     Point aPos( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ));
553     SvLBoxEntry* pEntry = GetEntry( aPos );
554     if(pEntry)
555     {
556         SvLBoxTab* pTab;
557         SvLBoxItem* pItem = GetItem( pEntry, aPos.X(), &pTab );
558         if(pItem)
559         {
560             aPos = GetEntryPosition( pEntry );
561             Size aSize(pItem->GetSize( this, pEntry ));
562             aPos.X() = GetTabPos( pEntry, pTab );
563 
564             if((aPos.X() + aSize.Width()) > GetSizePixel().Width())
565                 aSize.Width() = GetSizePixel().Width() - aPos.X();
566             aPos = OutputToScreenPixel(aPos);
567             Rectangle aItemRect( aPos, aSize );
568             String sMsg;
569             GlosBibUserData* pData = (GlosBibUserData*)pEntry->GetUserData();
570             sMsg = pData->sPath;
571             sMsg += INET_PATH_TOKEN;
572             sMsg += pData->sGroupName.GetToken(0, GLOS_DELIM);
573             sMsg += SwGlossaries::GetExtension();
574 
575             Help::ShowQuickHelp( this, aItemRect, sMsg,
576                         QUICKHELP_LEFT|QUICKHELP_VCENTER );
577         }
578     }
579 }
580