OSDN Git Service

02574c79e39ba0a170ea23dc4a40df1852c40277
[tpi/lychee.git] / src / lychee / dlg_make.cpp
1 /*******************************************************************************\r
2   TPI - flexible but useless plug-in framework.\r
3   Copyright (C) 2002-2009 Silky\r
4 \r
5   This library is free software; you can redistribute it and/or modify it under\r
6   the terms of the GNU Lesser General Public License as published by the Free\r
7   Software Foundation; either version 2.1 of the License, or (at your option)\r
8   any later version.\r
9 \r
10   This library is distributed in the hope that it will be useful, but WITHOUT\r
11   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \r
12   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License\r
13   for more details.\r
14 \r
15   You should have received a copy of the GNU Lesser General Public License along\r
16   with this library; if not, write to the Free Software Foundation, Inc.,\r
17   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
18 \r
19   $Id$\r
20 *******************************************************************************/\r
21 \r
22 #include "lychee.h"\r
23 \r
24 #include "dlg_make.h"\r
25 #include "frm_main.h"\r
26 #include "functions.h"\r
27 \r
28 #include <wx/dirdlg.h>\r
29 #include <wx/arrimpl.cpp>\r
30 \r
31 WX_DEFINE_OBJARRAY(ArrayTPI_FORMATINFO);\r
32 \r
33 //******************************************************************************\r
34 // MakeDialog\r
35 //******************************************************************************\r
36 \r
37 MakeDialog::MakeDialog(): wxDialog()\r
38 {\r
39         this->uCommand = TPI_COMMAND_CREATE;\r
40 }\r
41 \r
42 //******************************************************************************\r
43 // Event Table.\r
44 //******************************************************************************\r
45 \r
46 BEGIN_EVENT_TABLE(MakeDialog, wxDialog)\r
47         EVT_INIT_DIALOG(                 MakeDialog::OnInit)\r
48         EVT_BUTTON(XRCID("btnDefault"),  MakeDialog::OnBtnDefault)\r
49         EVT_BUTTON(XRCID("btnDesktop"),  MakeDialog::OnBtnDesktop)\r
50         EVT_BUTTON(XRCID("btnCurrent"),  MakeDialog::OnBtnCurrent)\r
51         EVT_BUTTON(XRCID("btnBrowse"),   MakeDialog::OnBtnBrowse)\r
52         EVT_BUTTON(XRCID("btnBrowseKF"), MakeDialog::OnBtnBrowseKF)\r
53         EVT_BUTTON(XRCID("btnOK"),       MakeDialog::OnBtnOK)\r
54         EVT_BUTTON(XRCID("btnCancel"),   MakeDialog::OnBtnCancel)\r
55         EVT_CHOICE(XRCID("chType"),      MakeDialog::OnChoice)\r
56         EVT_CHECKBOX(XRCID("cbUnmask"),  MakeDialog::OnCbUnmask)\r
57         EVT_CHECKBOX(XRCID("cbMakeSFX"), MakeDialog::OnCbMakeSFX)\r
58         EVT_NOTEBOOK_PAGE_CHANGED(XRCID("nbTabs"), MakeDialog::OnTabChanged)\r
59 END_EVENT_TABLE()\r
60 \r
61 //******************************************************************************\r
62 // Event handler.\r
63 //******************************************************************************\r
64 \r
65 void MakeDialog::OnInit(wxInitDialogEvent&)\r
66 {\r
67         // XRCと結びつけ。\r
68         // "General"タブ\r
69         this->cbDir             = XRCCTRL(* this, "cbDir",        wxComboBox);\r
70         this->cbFileName        = XRCCTRL(* this, "cbFileName",   wxComboBox);\r
71         this->cbOpenAfter       = XRCCTRL(* this, "cbOpenAfter",  wxCheckBox);\r
72         this->cbIgnorePath      = XRCCTRL(* this, "cbIgnorePath", wxCheckBox);\r
73         this->cbExitAfter       = XRCCTRL(* this, "cbExitAfter",  wxCheckBox);\r
74         this->chType            = XRCCTRL(* this, "chType",       wxChoice);\r
75         this->chDirMake         = XRCCTRL(* this, "chDirMake",    wxChoice);\r
76         // "Config"タブ\r
77         this->scLevel           = XRCCTRL(* this, "scLevel",      wxSpinCtrl);\r
78         this->scRR              = XRCCTRL(* this, "scRR",         wxSpinCtrl);\r
79         this->cbSolid           = XRCCTRL(* this, "cbSolid",      wxCheckBox);\r
80         this->cbMMOptimize      = XRCCTRL(* this, "cbMMOptimize", wxCheckBox);\r
81         this->cbCompressHeader  = XRCCTRL(* this, "cbCompressHeader", wxCheckBox);\r
82         this->cbMakeSFX         = XRCCTRL(* this, "cbMakeSFX",    wxCheckBox);\r
83         // "Comment"タブ\r
84         this->tcComment         = XRCCTRL(* this, "tcComment",    wxTextCtrl);\r
85         // "Encryption"タブ\r
86         this->tcPassword        = XRCCTRL(* this, "tcPassword",   wxTextCtrl);\r
87         this->tcKeyfile         = XRCCTRL(* this, "tcKeyfile",    wxTextCtrl);\r
88         this->cbSplitSize       = XRCCTRL(* this, "cbSplitSize",  wxComboBox);\r
89         this->cbUnmask          = XRCCTRL(* this, "cbUnmask",     wxCheckBox);\r
90         this->cbEncryptHeader   = XRCCTRL(* this, "cbEncryptHeader", wxCheckBox);\r
91         this->chEncryptMethod   = XRCCTRL(* this, "chEncryptMethod", wxChoice);\r
92         // "Files"タブ\r
93         this->lcFiles           = XRCCTRL(* this, "lcFiles",      myListCtrl2);\r
94 \r
95         // ListCtrlに列を追加。\r
96         // wxGTKでは直接wxLC_VIRTUALを指定しないと反映されない。\r
97         this->lcFiles->SetSingleStyle(wxLC_VIRTUAL);\r
98         this->lcFiles->InsertColumn(0, _("Input"),  wxLIST_FORMAT_LEFT,  150);\r
99         this->lcFiles->InsertColumn(1, _("Output"), wxLIST_FORMAT_LEFT,  290);\r
100         this->lcFiles->asInput = & this->files;\r
101         this->lcFiles->atDangerItem.SetTextColour(* wxRED);\r
102         this->lcFiles->atExistItem.SetTextColour(* wxBLUE);\r
103 \r
104         ::wxXmlResource::Get()->Unload(L_DIR_S_XRC wxT("dlg_make.xrc"));\r
105 \r
106         // 事前準備。\r
107         MainFrame * frm_main = (MainFrame *) this->GetParent();\r
108         wxString szArcPath = frm_main->fnArchive.GetPath();\r
109 \r
110         // パス履歴読み込み。\r
111         for (size_t i = 0; i < frm_main->conf.GetHistoryCount(CONF_HISTORY_PATH); i++)\r
112         {\r
113                 wxString sz = frm_main->conf.ReadHistory(CONF_HISTORY_PATH, i);\r
114                 if (sz.IsEmpty())\r
115                 {\r
116                         continue;\r
117                 }\r
118 \r
119                 this->cbDir->Append(sz);\r
120         }\r
121         // 書庫名履歴読み込み。\r
122         for (size_t i = 0; i < frm_main->conf.GetHistoryCount(CONF_HISTORY_NAME); i++)\r
123         {\r
124                 wxString sz = frm_main->conf.ReadHistory(CONF_HISTORY_NAME, i);\r
125                 if (sz.IsEmpty())\r
126                 {\r
127                         continue;\r
128                 }\r
129 \r
130                 this->cbFileName->Append(sz);\r
131         }\r
132 \r
133         // パスを設定。\r
134         this->cbDir->SetValue(szArcPath);\r
135 \r
136         // コマンド別の処理。\r
137         switch (this->uCommand)\r
138         {\r
139         case TPI_COMMAND_EXTRACT:\r
140                 // 初期値を設定。\r
141                 this->SetTitle(_("Extract"));\r
142                 this->tcComment->SetValue(frm_main->aiArchive.szComment);\r
143                 this->cbDir->SetFocus();\r
144 \r
145                 // コントロールを無効化。\r
146                 this->scLevel->Disable();\r
147                 this->scRR->Disable();\r
148                 this->cbSolid->Disable();\r
149                 this->cbMMOptimize->Disable();\r
150                 this->tcComment->SetEditable(false);\r
151                 this->cbEncryptHeader->Disable();\r
152                 this->chEncryptMethod->Disable();\r
153         case TPI_COMMAND_ADD:\r
154                 // コントロールを無効化(展開時も)。\r
155                 this->cbFileName->Disable();\r
156                 this->chType->Disable();\r
157                 this->chDirMake->Enable();\r
158                 this->cbSplitSize->Disable();\r
159                 this->cbCompressHeader->Disable();\r
160                 this->cbMakeSFX->Disable();\r
161 \r
162                 // 書庫名を設定。\r
163                 this->cbFileName->SetValue(frm_main->fnArchive.GetFullName());\r
164 \r
165                 // 書庫形式欄を設定。\r
166                 this->afInfo.Add(frm_main->aiArchive.fiInfo);\r
167                 this->chType->Append(frm_main->aiArchive.fiInfo.szTypeName);\r
168                 this->chType->SetSelection(0);\r
169                 {\r
170                         wxCommandEvent e;\r
171                         e.SetInt(0);\r
172                         this->OnChoice(e);\r
173                 }\r
174 \r
175                 if (this->uCommand != TPI_COMMAND_ADD)\r
176                 {\r
177                         break;\r
178                 }\r
179                 this->SetTitle(_("Add"));\r
180 \r
181                 // コントロールを無効化(追加時のみ)。\r
182                 this->cbDir->Disable();\r
183                 this->chDirMake->Disable();\r
184                 XRCCTRL(* this, "btnDefault", wxButton)->Disable();\r
185                 XRCCTRL(* this, "btnDesktop", wxButton)->Disable();\r
186                 XRCCTRL(* this, "btnCurrent", wxButton)->Disable();\r
187                 XRCCTRL(* this, "btnBrowse",  wxButton)->Disable();\r
188                 break;\r
189         case TPI_COMMAND_CREATE:\r
190                 this->SetTitle(_("Create"));\r
191 \r
192                 // 書庫名を設定。初期化の都合上.を付加しておく。\r
193                 this->cbFileName->SetValue(frm_main->fnArchive.GetName() + wxT('.'));\r
194                 this->cbFileName->SetFocus();\r
195 \r
196                 // ライブラリを検索。\r
197                 TPIHandle tpi;\r
198                 wxDir fs(L_DIR_B_LIB);\r
199                 wxString szTPIName;\r
200                 if (fs.GetFirst(& szTPIName,wxT("*" TPI_EXT)))\r
201                 {\r
202                         do\r
203                         {\r
204                                 // ロード。\r
205                                 wxString szLibName = L_DIR_B_LIB + szTPIName;\r
206                                 if (tpi.InitLibrary(szLibName, wxEmptyString))\r
207                                 {\r
208                                         // 対応する形式名を取得。\r
209                                         TPI_FORMATINFO fiInfo;\r
210                                         if (tpi.GetFormatInformation(& fiInfo, true))\r
211                                         {\r
212                                                 do\r
213                                                 {\r
214                                                         if (fiInfo.eSupportedCommand & TPI_COMMAND_CREATE && (this->lcFiles->asInput->GetCount() == 1 || fiInfo.fArchive))\r
215                                                         {\r
216                                                                 fiInfo.szTPIName = szLibName;\r
217                                                                 this->afInfo.Add(fiInfo);\r
218                                                                 this->chType->Append(fiInfo.szTypeName);\r
219                                                         }\r
220                                                 }\r
221                                                 while (tpi.GetFormatInformation(& fiInfo));\r
222                                         }\r
223                                         tpi.FreeLibrary();\r
224                                 }\r
225                         }\r
226                         while (fs.GetNext(& szTPIName));\r
227                 }\r
228 \r
229                 if (this->chType->GetCount() == 0)\r
230                 {\r
231                         // 形式の候補が一つもない場合。\r
232                         XRCCTRL(* this, "btnOK", wxButton)->Disable();\r
233                         break;\r
234                 }\r
235 \r
236                 // とりあえず最初の形式にしておく。\r
237                 this->chType->SetSelection(0);\r
238                 wxCommandEvent e;\r
239                 e.SetInt(0);\r
240                 this->OnChoice(e);\r
241                 break;\r
242         }\r
243 \r
244         // 展開/格納先を予測。ただしDTVスキャンに時間がかかる場合はスキップ可能。\r
245         if (this->lcFiles->asInput->GetCount() < 3000 ||\r
246                 ::AskDlg(\r
247                         this->uCommand == TPI_COMMAND_EXTRACT ?\r
248                                 _("This archive contains so many files that it takes long to check Directory Traversal Vulnerability(DTV) problem. If you are sure this archive is safe, you can skip this scanning process. Do you want to scan for DTV problem?") :\r
249                                 _("The files you want to store are too many, so it takes long to check Directory Traversal Vulnerability(DTV) problem. If you are sure the path of the files are no problem, you can skip this scanning process. Do you want to scan for DTV problem?"),\r
250                         this\r
251                 ) == wxYES)\r
252         {\r
253                 wxNotebookEvent e;\r
254                 e.SetSelection(4);\r
255                 this->OnTabChanged(e);\r
256         }\r
257 \r
258         this->Raise();\r
259 }\r
260 \r
261 void MakeDialog::OnBtnDefault(wxCommandEvent&)\r
262 {\r
263         this->cbDir->SetValue(((MainFrame *) this->GetParent())->conf.ReadId(CONF_DEFAULT_PATH, (wxString) wxEmptyString));\r
264 }\r
265 \r
266 void MakeDialog::OnBtnDesktop(wxCommandEvent&)\r
267 {\r
268         wxFileName fn(wxFileName::GetHomeDir(), wxT("Desktop"));\r
269         this->cbDir->SetValue(fn.GetFullPath());\r
270 }\r
271 \r
272 void MakeDialog::OnBtnCurrent(wxCommandEvent&)\r
273 {\r
274         this->cbDir->SetValue(((MainFrame *) this->GetParent())->fnArchive.GetPath());\r
275 }\r
276 \r
277 void MakeDialog::OnBtnBrowse(wxCommandEvent&)\r
278 {\r
279         wxDirDialog dd(this);\r
280         dd.SetPath(this->cbDir->GetValue());\r
281         if (dd.ShowModal() == wxID_OK)\r
282         {\r
283                 this->cbDir->SetValue(dd.GetPath());\r
284         }\r
285 }\r
286 \r
287 void MakeDialog::OnBtnBrowseKF(wxCommandEvent&)\r
288 {\r
289         wxFileDialog fd(this);\r
290         fd.SetWindowStyleFlag(wxFD_OPEN | wxFD_FILE_MUST_EXIST);\r
291         if (fd.ShowModal() == wxID_OK)\r
292         {\r
293                 this->tcKeyfile->SetValue(fd.GetPath());\r
294         }\r
295 }\r
296 \r
297 void MakeDialog::OnBtnOK(wxCommandEvent&)\r
298 {\r
299         // 履歴書き込み。\r
300         if (this->uCommand != TPI_COMMAND_ADD)\r
301         {\r
302                 MainFrame * frm_main = (MainFrame *) this->GetParent();\r
303                 wxFileName fnCurrent(this->cbDir->GetValue(), this->uCommand == TPI_COMMAND_CREATE ? this->cbFileName->GetValue() : (wxString) wxEmptyString);\r
304                 frm_main->conf.WriteHistory(CONF_HISTORY_FULL, fnCurrent.GetFullPath());\r
305                 frm_main->conf.WriteHistory(CONF_HISTORY_PATH, fnCurrent.GetPath());\r
306                 frm_main->conf.WriteHistory(CONF_HISTORY_NAME, fnCurrent.GetFullName());\r
307         }\r
308         this->EndModal(wxID_OK);\r
309 }\r
310 \r
311 void MakeDialog::OnBtnCancel(wxCommandEvent&)\r
312 {\r
313         this->EndModal(wxID_CANCEL);\r
314 }\r
315 \r
316 void MakeDialog::OnChoice(wxCommandEvent& e)\r
317 {\r
318         TPI_FORMATINFO * fiInfo = & this->afInfo[e.GetInt()];\r
319         // 形式が各種設定に対応しているか。\r
320         // 作成時/追加時/展開時設定。\r
321         this->tcPassword->Enable(fiInfo->fEncryptPassword);\r
322         this->tcKeyfile->Enable(fiInfo->fEncryptKeyFile);\r
323         this->cbUnmask->Enable(fiInfo->fEncryptPassword);\r
324         this->chEncryptMethod->Enable(fiInfo->fEncryptPassword || fiInfo->fEncryptKeyFile);\r
325         XRCCTRL(* this, "btnBrowseKF", wxButton)->Enable(fiInfo->fEncryptKeyFile);\r
326         if (this->uCommand == TPI_COMMAND_EXTRACT)\r
327         {\r
328                 return;\r
329         }\r
330 \r
331         // 作成時/追加時設定。\r
332         this->scLevel->SetRange(fiInfo->nCompressLevelMin, fiInfo->nCompressLevelMax);\r
333         this->scLevel->SetValue(fiInfo->nCompressLevelMax);\r
334         this->scLevel->Enable(fiInfo->nCompressLevelMin != fiInfo->nCompressLevelMax);\r
335         this->scRR->SetRange(fiInfo->nRecoveryRecordMin, fiInfo->nRecoveryRecordMax);\r
336         this->scRR->SetValue(fiInfo->nRecoveryRecordMin);\r
337         this->scRR->Enable(fiInfo->nRecoveryRecordMin != fiInfo->nRecoveryRecordMax);\r
338         this->cbEncryptHeader->Enable(fiInfo->fEncryptHeader);\r
339         this->cbCompressHeader->Enable(fiInfo->fCompressHeader);\r
340         this->cbSolid->Enable(fiInfo->fSolid);\r
341         this->cbMMOptimize->Enable(fiInfo->fMMOptimize);\r
342         this->tcComment->Enable(fiInfo->fComment);\r
343         if (this->uCommand == TPI_COMMAND_ADD)\r
344         {\r
345                 return;\r
346         }\r
347 \r
348         // 作成時設定。\r
349         this->cbSplitSize->Enable(fiInfo->fMultiVolume);\r
350         this->cbMakeSFX->Enable(fiInfo->fSFX);\r
351 \r
352         wxFileName fn(this->cbFileName->GetValue());\r
353         fn.SetExt(this->cbMakeSFX->IsEnabled() && this->cbMakeSFX->IsChecked() ? EXE_EXT : fiInfo->szSuffix.BeforeFirst(wxT(';')));\r
354         this->cbFileName->SetValue(fn.GetFullName());\r
355 }\r
356 \r
357 void MakeDialog::OnCbUnmask(wxCommandEvent&)\r
358 {\r
359         this->tcPassword->SetWindowStyle(this->tcPassword->GetWindowStyle() & (this->cbUnmask->IsChecked() ? ~ wxTE_PASSWORD : wxTE_PASSWORD));\r
360         this->tcPassword->Refresh();\r
361 }\r
362 \r
363 void MakeDialog::OnCbMakeSFX(wxCommandEvent&)\r
364 {\r
365         wxFileName fn(this->cbFileName->GetValue());\r
366         fn.SetExt(this->cbMakeSFX->IsChecked() ? EXE_EXT : this->afInfo[this->chType->GetSelection()].szSuffix.BeforeFirst(wxT(';')));\r
367         this->cbFileName->SetValue(fn.GetFullName());\r
368 }\r
369 \r
370 void MakeDialog::OnTabChanged(wxNotebookEvent& e)\r
371 {\r
372         // "Files"タブのときは処理。\r
373         if (e.GetSelection() != 4)\r
374         {\r
375                 return;\r
376         }\r
377         this->lcFiles->DeleteAllItems();\r
378         this->lcFiles->asOutput.Clear();\r
379         this->lcFiles->apItem.Clear();\r
380 \r
381         bool fDTVWarning = false;\r
382         switch (this->uCommand)\r
383         {\r
384         case TPI_COMMAND_EXTRACT:\r
385         {\r
386                 // ファイルの出力先を推測。\r
387                 wxString szOutputRootDir = WillMakeDirByArcName((MainFrame *) this->GetParent(), this) ? MakeDirPath(wxFileName::DirName(this->cbDir->GetValue()), wxFileName(this->cbFileName->GetValue()).GetName(), false).GetPath() : this->cbDir->GetValue();\r
388 \r
389                 // 各ファイルにパスを付加。\r
390                 for (size_t i = 0; i < this->lcFiles->asInput->GetCount(); i++)\r
391                 {\r
392                         wxString szOutputFile = szOutputRootDir + wxFileName::GetPathSeparator();\r
393                         wxFileName fnStored(this->lcFiles->asInput->Item(i));\r
394                         if (! this->cbIgnorePath->IsChecked())\r
395                         {\r
396                                 szOutputFile += fnStored.GetPathWithSep();\r
397                         }\r
398                         wxFileName fnOutput(szOutputFile + fnStored.GetFullName());\r
399                         if (! fnOutput.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE | wxPATH_NORM_LONG) || ! fnOutput.GetFullPath().StartsWith(szOutputRootDir))\r
400                         {\r
401                                 fDTVWarning = true;\r
402                                 this->lcFiles->apItem.Add(& this->lcFiles->atDangerItem);\r
403                         }\r
404                         else\r
405                         {\r
406                                 this->lcFiles->apItem.Add(fnOutput.FileExists() || ::wxDirExists(fnOutput.GetFullPath()) ? & this->lcFiles->atExistItem : NULL);\r
407                         }\r
408                         this->lcFiles->asOutput.Add(fnOutput.GetFullPath());\r
409                 }\r
410                 break;\r
411         }\r
412         case TPI_COMMAND_ADD:\r
413         case TPI_COMMAND_CREATE:\r
414                 // 格納パスを推測。\r
415                 for (size_t i = 0; i < this->lcFiles->asInput->GetCount(); i++)\r
416                 {\r
417                         wxFileName fnStored(this->lcFiles->asInput->Item(i));\r
418                         this->lcFiles->asOutput.Add(this->cbIgnorePath->IsChecked() ? fnStored.GetFullName() : fnStored.GetFullPath());\r
419                         fnStored = wxFileName(this->lcFiles->asOutput.Item(i));\r
420                         if (fnStored.GetPathWithSep(wxPATH_UNIX).Find(wxT("../")) != wxNOT_FOUND)\r
421                         {\r
422                                 fDTVWarning = true;\r
423                                 this->lcFiles->apItem.Add(& this->lcFiles->atDangerItem);\r
424                         }\r
425                         else\r
426                         {\r
427                                 this->lcFiles->apItem.Add(NULL);\r
428                         }\r
429                 }\r
430                 break;\r
431         }\r
432 \r
433         // DTV検査で異常があれば警告。\r
434         if (fDTVWarning)\r
435         {\r
436                 wxString sz;\r
437                 sz.Printf(\r
438                         _("This archive may have Directory Traversal Vulnerability(DTV) problem, %s It is strongly recommended to ignore file path. Would you like to do so?"),\r
439                         (this->uCommand == TPI_COMMAND_EXTRACT ?\r
440                                 _("and some danger files may be extracted to the unexpected system directory!") :\r
441                                 _("and some danger files may be stored!")\r
442                         )\r
443                 );\r
444                 if (::AskDlg(sz, this) == wxYES)\r
445                 {\r
446                         this->cbIgnorePath->SetValue(true);\r
447                         this->OnTabChanged(e);\r
448                 }\r
449         }\r
450 \r
451         // リストビューに表示。\r
452         this->lcFiles->SetItemCount(this->lcFiles->asInput->GetCount());\r
453 }\r
454 \r
455 //******************************************************************************\r
456 // myListCtrl2\r
457 //******************************************************************************\r
458 \r
459 IMPLEMENT_DYNAMIC_CLASS(myListCtrl2, wxListView)\r
460 \r
461 //******************************************************************************\r
462 // Event handler.\r
463 //******************************************************************************\r
464 wxString myListCtrl2::OnGetItemText(long i, long column) const\r
465 {\r
466         // リストビューに項目を追加。\r
467         switch (column)\r
468         {\r
469         case 0:\r
470                 return this->asInput->Item(i);\r
471         case 1:\r
472                 return this->asOutput[i];\r
473         default:\r
474                 return wxEmptyString;\r
475         }\r
476 }\r
477 \r
478 wxListItemAttr * myListCtrl2::OnGetItemAttr(long i) const\r
479 {\r
480         return (wxListItemAttr *) this->apItem[i];\r
481 }\r