]> git.saurik.com Git - wxWidgets.git/blob - src/generic/editlbox.cpp
Don't crash in wxGridCellAutoWrapStringRenderer when the column is hidden.
[wxWidgets.git] / src / generic / editlbox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/editlbox.cpp
3 // Purpose: ListBox with editable items
4 // Author: Vaclav Slavik
5 // Copyright: (c) Vaclav Slavik
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx/wx.h".
10 #include "wx/wxprec.h"
11
12 #ifdef __BORLANDC__
13 #pragma hdrstop
14 #endif
15
16 #if wxUSE_EDITABLELISTBOX
17
18 // for all others, include the necessary headers (this file is usually all you
19 // need because it includes almost all "standard" wxWidgets headers)
20 #ifndef WX_PRECOMP
21 #include "wx/wx.h"
22 #endif
23
24 #include "wx/editlbox.h"
25 #include "wx/sizer.h"
26 #include "wx/listctrl.h"
27
28 // ============================================================================
29 // implementation
30 // ============================================================================
31
32 const char wxEditableListBoxNameStr[] = "editableListBox";
33
34 static const char* const eledit_xpm[] = {
35 "16 16 3 1",
36 " c None",
37 ". c #000000",
38 "+ c #00007F",
39 " ",
40 " ",
41 " .. .. ",
42 " . ",
43 " . ",
44 " ++++ . ++++ ",
45 " ++ . ++ ++",
46 " +++++ . ++++++",
47 " ++ ++ . ++ ",
48 " ++ ++ . ++ ++",
49 " +++++ . ++++ ",
50 " . ",
51 " . ",
52 " .. .. ",
53 " ",
54 " "};
55
56 static const char* const elnew_xpm[] = {
57 "16 16 5 1",
58 " c None",
59 ". c #7F7F7F",
60 "+ c #FFFFFF",
61 "@ c #FFFF00",
62 "# c #000000",
63 " ",
64 " ",
65 " . .+ .@ ",
66 " . .@.@# # # ",
67 " @.@+.... # ",
68 " ... @@ ",
69 " @ . @. # ",
70 " .# .@ ",
71 " . # ",
72 " # ",
73 " # ",
74 " # ",
75 " # ",
76 " # # # # # # ",
77 " ",
78 " "};
79
80 static const char* const eldel_xpm[] = {
81 "16 16 3 1",
82 " c None",
83 ". c #7F0000",
84 "+ c #FFFFFF",
85 " ",
86 " ",
87 " ",
88 " ..+ ..+ ",
89 " ....+ ..+ ",
90 " ....+ ..+ ",
91 " ...+ .+ ",
92 " .....+ ",
93 " ...+ ",
94 " .....+ ",
95 " ...+ ..+ ",
96 " ...+ ..+ ",
97 " ...+ .+ ",
98 " ...+ .+ ",
99 " . . ",
100 " "};
101
102 static const char* const eldown_xpm[] = {
103 "16 16 2 1",
104 " c None",
105 ". c #000000",
106 " ",
107 " ",
108 " ... ",
109 " ... ",
110 " ... ",
111 " ... ",
112 " ... ",
113 " ... ",
114 " ........... ",
115 " ......... ",
116 " ....... ",
117 " ..... ",
118 " ... ",
119 " . ",
120 " ",
121 " "};
122
123 static const char* const elup_xpm[] = {
124 "16 16 2 1",
125 " c None",
126 ". c #000000",
127 " ",
128 " . ",
129 " ... ",
130 " ..... ",
131 " ....... ",
132 " ......... ",
133 " ........... ",
134 " ... ",
135 " ... ",
136 " ... ",
137 " ... ",
138 " ... ",
139 " ... ",
140 " ",
141 " ",
142 " "};
143
144 // list control with auto-resizable column:
145 class CleverListCtrl : public wxListCtrl
146 {
147 public:
148 CleverListCtrl(wxWindow *parent,
149 wxWindowID id = wxID_ANY,
150 const wxPoint &pos = wxDefaultPosition,
151 const wxSize &size = wxDefaultSize,
152 long style = wxLC_ICON,
153 const wxValidator& validator = wxDefaultValidator,
154 const wxString &name = wxListCtrlNameStr)
155 : wxListCtrl(parent, id, pos, size, style, validator, name)
156 {
157 CreateColumns();
158 }
159
160 void CreateColumns()
161 {
162 InsertColumn(0, wxT("item"));
163 SizeColumns();
164 }
165
166 void SizeColumns()
167 {
168 int w = GetSize().x;
169 #ifdef __WXMSW__
170 w -= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) + 6;
171 #else
172 w -= 2*wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
173 #endif
174 if (w < 0) w = 0;
175 SetColumnWidth(0, w);
176 }
177
178 private:
179 DECLARE_EVENT_TABLE()
180 void OnSize(wxSizeEvent& event)
181 {
182 SizeColumns();
183 event.Skip();
184 }
185 };
186
187 BEGIN_EVENT_TABLE(CleverListCtrl, wxListCtrl)
188 EVT_SIZE(CleverListCtrl::OnSize)
189 END_EVENT_TABLE()
190
191
192 // ----------------------------------------------------------------------------
193 // wxEditableListBox
194 // ----------------------------------------------------------------------------
195
196 IMPLEMENT_CLASS(wxEditableListBox, wxPanel)
197
198 // NB: generate the IDs at runtime to avoid conflict with XRCID values,
199 // they could cause XRCCTRL() failures in XRC-based dialogs
200 const wxWindowIDRef wxID_ELB_DELETE = wxWindow::NewControlId();
201 const wxWindowIDRef wxID_ELB_EDIT = wxWindow::NewControlId();
202 const wxWindowIDRef wxID_ELB_NEW = wxWindow::NewControlId();
203 const wxWindowIDRef wxID_ELB_UP = wxWindow::NewControlId();
204 const wxWindowIDRef wxID_ELB_DOWN = wxWindow::NewControlId();
205 const wxWindowIDRef wxID_ELB_LISTCTRL = wxWindow::NewControlId();
206
207 BEGIN_EVENT_TABLE(wxEditableListBox, wxPanel)
208 EVT_LIST_ITEM_SELECTED(wxID_ELB_LISTCTRL, wxEditableListBox::OnItemSelected)
209 EVT_LIST_END_LABEL_EDIT(wxID_ELB_LISTCTRL, wxEditableListBox::OnEndLabelEdit)
210 EVT_BUTTON(wxID_ELB_NEW, wxEditableListBox::OnNewItem)
211 EVT_BUTTON(wxID_ELB_UP, wxEditableListBox::OnUpItem)
212 EVT_BUTTON(wxID_ELB_DOWN, wxEditableListBox::OnDownItem)
213 EVT_BUTTON(wxID_ELB_EDIT, wxEditableListBox::OnEditItem)
214 EVT_BUTTON(wxID_ELB_DELETE, wxEditableListBox::OnDelItem)
215 END_EVENT_TABLE()
216
217 bool wxEditableListBox::Create(wxWindow *parent, wxWindowID id,
218 const wxString& label,
219 const wxPoint& pos, const wxSize& size,
220 long style,
221 const wxString& name)
222 {
223 if (!wxPanel::Create(parent, id, pos, size, wxTAB_TRAVERSAL, name))
224 return false;
225
226 m_style = style;
227
228 wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
229
230 wxPanel *subp = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
231 wxSUNKEN_BORDER | wxTAB_TRAVERSAL);
232 wxSizer *subsizer = new wxBoxSizer(wxHORIZONTAL);
233 subsizer->Add(new wxStaticText(subp, wxID_ANY, label), 1, wxALIGN_CENTRE_VERTICAL | wxLEFT, 4);
234
235 #ifdef __WXMSW__
236 #define BTN_BORDER 4
237 // FIXME - why is this needed? There's some reason why sunken border is
238 // ignored by sizers in wxMSW but not in wxGTK that I can't
239 // figure out...
240 #else
241 #define BTN_BORDER 0
242 #endif
243
244 if ( m_style & wxEL_ALLOW_EDIT )
245 {
246 m_bEdit = new wxBitmapButton(subp, wxID_ELB_EDIT, wxBitmap(eledit_xpm));
247 subsizer->Add(m_bEdit, 0, wxALIGN_CENTRE_VERTICAL | wxTOP | wxBOTTOM, BTN_BORDER);
248 }
249
250 if ( m_style & wxEL_ALLOW_NEW )
251 {
252 m_bNew = new wxBitmapButton(subp, wxID_ELB_NEW, wxBitmap(elnew_xpm));
253 subsizer->Add(m_bNew, 0, wxALIGN_CENTRE_VERTICAL | wxTOP | wxBOTTOM, BTN_BORDER);
254 }
255
256 if ( m_style & wxEL_ALLOW_DELETE )
257 {
258 m_bDel = new wxBitmapButton(subp, wxID_ELB_DELETE, wxBitmap(eldel_xpm));
259 subsizer->Add(m_bDel, 0, wxALIGN_CENTRE_VERTICAL | wxTOP | wxBOTTOM, BTN_BORDER);
260 }
261
262 if (!(m_style & wxEL_NO_REORDER))
263 {
264 m_bUp = new wxBitmapButton(subp, wxID_ELB_UP, wxBitmap(elup_xpm));
265 subsizer->Add(m_bUp, 0, wxALIGN_CENTRE_VERTICAL | wxTOP | wxBOTTOM, BTN_BORDER);
266
267 m_bDown = new wxBitmapButton(subp, wxID_ELB_DOWN, wxBitmap(eldown_xpm));
268 subsizer->Add(m_bDown, 0, wxALIGN_CENTRE_VERTICAL | wxTOP | wxBOTTOM, BTN_BORDER);
269 }
270
271 #if wxUSE_TOOLTIPS
272 if ( m_bEdit ) m_bEdit->SetToolTip(_("Edit item"));
273 if ( m_bNew ) m_bNew->SetToolTip(_("New item"));
274 if ( m_bDel ) m_bDel->SetToolTip(_("Delete item"));
275 if ( m_bUp ) m_bUp->SetToolTip(_("Move up"));
276 if ( m_bDown ) m_bDown->SetToolTip(_("Move down"));
277 #endif
278
279 subp->SetSizer(subsizer);
280 subsizer->Fit(subp);
281
282 sizer->Add(subp, 0, wxEXPAND);
283
284 long st = wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL | wxSUNKEN_BORDER;
285 if ( style & wxEL_ALLOW_EDIT )
286 st |= wxLC_EDIT_LABELS;
287 m_listCtrl = new CleverListCtrl(this, wxID_ELB_LISTCTRL,
288 wxDefaultPosition, wxDefaultSize, st);
289 wxArrayString empty_ar;
290 SetStrings(empty_ar);
291
292 sizer->Add(m_listCtrl, 1, wxEXPAND);
293
294 SetSizer(sizer);
295 Layout();
296
297 return true;
298 }
299
300 void wxEditableListBox::SetStrings(const wxArrayString& strings)
301 {
302 m_listCtrl->DeleteAllItems();
303 size_t i;
304
305 for (i = 0; i < strings.GetCount(); i++)
306 m_listCtrl->InsertItem(i, strings[i]);
307
308 m_listCtrl->InsertItem(strings.GetCount(), wxEmptyString);
309 m_listCtrl->SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
310 }
311
312 void wxEditableListBox::GetStrings(wxArrayString& strings) const
313 {
314 strings.Clear();
315
316 for (int i = 0; i < m_listCtrl->GetItemCount()-1; i++)
317 strings.Add(m_listCtrl->GetItemText(i));
318 }
319
320 void wxEditableListBox::OnItemSelected(wxListEvent& event)
321 {
322 m_selection = event.GetIndex();
323 if (!(m_style & wxEL_NO_REORDER))
324 {
325 m_bUp->Enable(m_selection != 0 && m_selection < m_listCtrl->GetItemCount()-1);
326 m_bDown->Enable(m_selection < m_listCtrl->GetItemCount()-2);
327 }
328
329 if (m_style & wxEL_ALLOW_EDIT)
330 m_bEdit->Enable(m_selection < m_listCtrl->GetItemCount()-1);
331 if (m_style & wxEL_ALLOW_DELETE)
332 m_bDel->Enable(m_selection < m_listCtrl->GetItemCount()-1);
333 }
334
335 void wxEditableListBox::OnNewItem(wxCommandEvent& WXUNUSED(event))
336 {
337 m_listCtrl->SetItemState(m_listCtrl->GetItemCount()-1,
338 wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
339 m_listCtrl->EditLabel(m_selection);
340 }
341
342 void wxEditableListBox::OnEndLabelEdit(wxListEvent& event)
343 {
344 if ( event.GetIndex() == m_listCtrl->GetItemCount()-1 &&
345 !event.GetText().empty() )
346 {
347 // The user edited last (empty) line, i.e. added new entry. We have to
348 // add new empty line here so that adding one more line is still
349 // possible:
350 m_listCtrl->InsertItem(m_listCtrl->GetItemCount(), wxEmptyString);
351
352 // Simulate a wxEVT_LIST_ITEM_SELECTED event for the new item,
353 // so that the buttons are enabled/disabled properly
354 wxListEvent selectionEvent(wxEVT_LIST_ITEM_SELECTED, m_listCtrl->GetId());
355 selectionEvent.m_itemIndex = event.GetIndex();
356 m_listCtrl->GetEventHandler()->ProcessEvent(selectionEvent);
357 }
358 }
359
360 void wxEditableListBox::OnDelItem(wxCommandEvent& WXUNUSED(event))
361 {
362 m_listCtrl->DeleteItem(m_selection);
363 m_listCtrl->SetItemState(m_selection,
364 wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
365 }
366
367 void wxEditableListBox::OnEditItem(wxCommandEvent& WXUNUSED(event))
368 {
369 m_listCtrl->EditLabel(m_selection);
370 }
371
372 void wxEditableListBox::SwapItems(long i1, long i2)
373 {
374 // swap the text
375 wxString t1 = m_listCtrl->GetItemText(i1);
376 wxString t2 = m_listCtrl->GetItemText(i2);
377 m_listCtrl->SetItemText(i1, t2);
378 m_listCtrl->SetItemText(i2, t1);
379
380 // swap the item data
381 long d1 = m_listCtrl->GetItemData(i1);
382 long d2 = m_listCtrl->GetItemData(i2);
383 m_listCtrl->SetItemData(i1, d2);
384 m_listCtrl->SetItemData(i2, d1);
385 }
386
387
388 void wxEditableListBox::OnUpItem(wxCommandEvent& WXUNUSED(event))
389 {
390 SwapItems(m_selection - 1, m_selection);
391 m_listCtrl->SetItemState(m_selection - 1,
392 wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
393 }
394
395 void wxEditableListBox::OnDownItem(wxCommandEvent& WXUNUSED(event))
396 {
397 SwapItems(m_selection + 1, m_selection);
398 m_listCtrl->SetItemState(m_selection + 1,
399 wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
400 }
401
402 #endif // wxUSE_EDITABLELISTBOX