PageRenderTime 39ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/generic/listctrl.cpp

https://bitbucket.org/cafu/cafu
C++ | 5477 lines | 3958 code | 974 blank | 545 comment | 729 complexity | f6cb5567847a4bd218f002128d7b0f1f MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.0, BSD-3-Clause, LGPL-3.0, LGPL-2.1, AGPL-3.0
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/generic/listctrl.cpp
  3. // Purpose: generic implementation of wxListCtrl
  4. // Author: Robert Roebling
  5. // Vadim Zeitlin (virtual list control support)
  6. // Copyright: (c) 1998 Robert Roebling
  7. // Licence: wxWindows licence
  8. /////////////////////////////////////////////////////////////////////////////
  9. // TODO
  10. //
  11. // 1. we need to implement searching/sorting for virtual controls somehow
  12. // 2. when changing selection the lines are refreshed twice
  13. // For compilers that support precompilation, includes "wx.h".
  14. #include "wx/wxprec.h"
  15. #ifdef __BORLANDC__
  16. #pragma hdrstop
  17. #endif
  18. #if wxUSE_LISTCTRL
  19. #include "wx/listctrl.h"
  20. #ifndef WX_PRECOMP
  21. #include "wx/scrolwin.h"
  22. #include "wx/timer.h"
  23. #include "wx/settings.h"
  24. #include "wx/dynarray.h"
  25. #include "wx/dcclient.h"
  26. #include "wx/dcscreen.h"
  27. #include "wx/math.h"
  28. #include "wx/settings.h"
  29. #include "wx/sizer.h"
  30. #endif
  31. #include "wx/imaglist.h"
  32. #include "wx/renderer.h"
  33. #include "wx/generic/private/listctrl.h"
  34. #ifdef __WXMAC__
  35. #include "wx/osx/private.h"
  36. #endif
  37. #if defined(__WXMSW__) && !defined(__WXWINCE__) && !defined(__WXUNIVERSAL__)
  38. #define "wx/msw/wrapwin.h"
  39. #endif
  40. // NOTE: If using the wxListBox visual attributes works everywhere then this can
  41. // be removed, as well as the #else case below.
  42. #define _USE_VISATTR 0
  43. // ----------------------------------------------------------------------------
  44. // constants
  45. // ----------------------------------------------------------------------------
  46. // // the height of the header window (FIXME: should depend on its font!)
  47. // static const int HEADER_HEIGHT = 23;
  48. static const int SCROLL_UNIT_X = 15;
  49. // the spacing between the lines (in report mode)
  50. static const int LINE_SPACING = 0;
  51. // extra margins around the text label
  52. #ifdef __WXGTK__
  53. static const int EXTRA_WIDTH = 6;
  54. #else
  55. static const int EXTRA_WIDTH = 4;
  56. #endif
  57. #ifdef __WXGTK__
  58. static const int EXTRA_HEIGHT = 6;
  59. #else
  60. static const int EXTRA_HEIGHT = 4;
  61. #endif
  62. // margin between the window and the items
  63. static const int EXTRA_BORDER_X = 2;
  64. static const int EXTRA_BORDER_Y = 2;
  65. // offset for the header window
  66. static const int HEADER_OFFSET_X = 0;
  67. static const int HEADER_OFFSET_Y = 0;
  68. // margin between rows of icons in [small] icon view
  69. static const int MARGIN_BETWEEN_ROWS = 6;
  70. // when autosizing the columns, add some slack
  71. static const int AUTOSIZE_COL_MARGIN = 10;
  72. // default width for the header columns
  73. static const int WIDTH_COL_DEFAULT = 80;
  74. // the space between the image and the text in the report mode
  75. static const int IMAGE_MARGIN_IN_REPORT_MODE = 5;
  76. // the space between the image and the text in the report mode in header
  77. static const int HEADER_IMAGE_MARGIN_IN_REPORT_MODE = 2;
  78. // ----------------------------------------------------------------------------
  79. // arrays/list implementations
  80. // ----------------------------------------------------------------------------
  81. #include "wx/listimpl.cpp"
  82. WX_DEFINE_LIST(wxListItemDataList)
  83. #include "wx/arrimpl.cpp"
  84. WX_DEFINE_OBJARRAY(wxListLineDataArray)
  85. #include "wx/listimpl.cpp"
  86. WX_DEFINE_LIST(wxListHeaderDataList)
  87. // ----------------------------------------------------------------------------
  88. // wxListItemData
  89. // ----------------------------------------------------------------------------
  90. wxListItemData::~wxListItemData()
  91. {
  92. // in the virtual list control the attributes are managed by the main
  93. // program, so don't delete them
  94. if ( !m_owner->IsVirtual() )
  95. delete m_attr;
  96. delete m_rect;
  97. }
  98. void wxListItemData::Init()
  99. {
  100. m_image = -1;
  101. m_data = 0;
  102. m_attr = NULL;
  103. }
  104. wxListItemData::wxListItemData(wxListMainWindow *owner)
  105. {
  106. Init();
  107. m_owner = owner;
  108. if ( owner->InReportView() )
  109. m_rect = NULL;
  110. else
  111. m_rect = new wxRect;
  112. }
  113. void wxListItemData::SetItem( const wxListItem &info )
  114. {
  115. if ( info.m_mask & wxLIST_MASK_TEXT )
  116. SetText(info.m_text);
  117. if ( info.m_mask & wxLIST_MASK_IMAGE )
  118. m_image = info.m_image;
  119. if ( info.m_mask & wxLIST_MASK_DATA )
  120. m_data = info.m_data;
  121. if ( info.HasAttributes() )
  122. {
  123. if ( m_attr )
  124. m_attr->AssignFrom(*info.GetAttributes());
  125. else
  126. m_attr = new wxListItemAttr(*info.GetAttributes());
  127. }
  128. if ( m_rect )
  129. {
  130. m_rect->x =
  131. m_rect->y =
  132. m_rect->height = 0;
  133. m_rect->width = info.m_width;
  134. }
  135. }
  136. void wxListItemData::SetPosition( int x, int y )
  137. {
  138. wxCHECK_RET( m_rect, wxT("unexpected SetPosition() call") );
  139. m_rect->x = x;
  140. m_rect->y = y;
  141. }
  142. void wxListItemData::SetSize( int width, int height )
  143. {
  144. wxCHECK_RET( m_rect, wxT("unexpected SetSize() call") );
  145. if ( width != -1 )
  146. m_rect->width = width;
  147. if ( height != -1 )
  148. m_rect->height = height;
  149. }
  150. bool wxListItemData::IsHit( int x, int y ) const
  151. {
  152. wxCHECK_MSG( m_rect, false, wxT("can't be called in this mode") );
  153. return wxRect(GetX(), GetY(), GetWidth(), GetHeight()).Contains(x, y);
  154. }
  155. int wxListItemData::GetX() const
  156. {
  157. wxCHECK_MSG( m_rect, 0, wxT("can't be called in this mode") );
  158. return m_rect->x;
  159. }
  160. int wxListItemData::GetY() const
  161. {
  162. wxCHECK_MSG( m_rect, 0, wxT("can't be called in this mode") );
  163. return m_rect->y;
  164. }
  165. int wxListItemData::GetWidth() const
  166. {
  167. wxCHECK_MSG( m_rect, 0, wxT("can't be called in this mode") );
  168. return m_rect->width;
  169. }
  170. int wxListItemData::GetHeight() const
  171. {
  172. wxCHECK_MSG( m_rect, 0, wxT("can't be called in this mode") );
  173. return m_rect->height;
  174. }
  175. void wxListItemData::GetItem( wxListItem &info ) const
  176. {
  177. long mask = info.m_mask;
  178. if ( !mask )
  179. // by default, get everything for backwards compatibility
  180. mask = -1;
  181. if ( mask & wxLIST_MASK_TEXT )
  182. info.m_text = m_text;
  183. if ( mask & wxLIST_MASK_IMAGE )
  184. info.m_image = m_image;
  185. if ( mask & wxLIST_MASK_DATA )
  186. info.m_data = m_data;
  187. if ( m_attr )
  188. {
  189. if ( m_attr->HasTextColour() )
  190. info.SetTextColour(m_attr->GetTextColour());
  191. if ( m_attr->HasBackgroundColour() )
  192. info.SetBackgroundColour(m_attr->GetBackgroundColour());
  193. if ( m_attr->HasFont() )
  194. info.SetFont(m_attr->GetFont());
  195. }
  196. }
  197. //-----------------------------------------------------------------------------
  198. // wxListHeaderData
  199. //-----------------------------------------------------------------------------
  200. void wxListHeaderData::Init()
  201. {
  202. m_mask = 0;
  203. m_image = -1;
  204. m_format = 0;
  205. m_width = 0;
  206. m_xpos = 0;
  207. m_ypos = 0;
  208. m_height = 0;
  209. m_state = 0;
  210. }
  211. wxListHeaderData::wxListHeaderData()
  212. {
  213. Init();
  214. }
  215. wxListHeaderData::wxListHeaderData( const wxListItem &item )
  216. {
  217. Init();
  218. SetItem( item );
  219. }
  220. void wxListHeaderData::SetItem( const wxListItem &item )
  221. {
  222. m_mask = item.m_mask;
  223. if ( m_mask & wxLIST_MASK_TEXT )
  224. m_text = item.m_text;
  225. if ( m_mask & wxLIST_MASK_IMAGE )
  226. m_image = item.m_image;
  227. if ( m_mask & wxLIST_MASK_FORMAT )
  228. m_format = item.m_format;
  229. if ( m_mask & wxLIST_MASK_WIDTH )
  230. SetWidth(item.m_width);
  231. if ( m_mask & wxLIST_MASK_STATE )
  232. SetState(item.m_state);
  233. }
  234. void wxListHeaderData::SetPosition( int x, int y )
  235. {
  236. m_xpos = x;
  237. m_ypos = y;
  238. }
  239. void wxListHeaderData::SetHeight( int h )
  240. {
  241. m_height = h;
  242. }
  243. void wxListHeaderData::SetWidth( int w )
  244. {
  245. m_width = w < 0 ? WIDTH_COL_DEFAULT : w;
  246. }
  247. void wxListHeaderData::SetState( int flag )
  248. {
  249. m_state = flag;
  250. }
  251. void wxListHeaderData::SetFormat( int format )
  252. {
  253. m_format = format;
  254. }
  255. bool wxListHeaderData::HasImage() const
  256. {
  257. return m_image != -1;
  258. }
  259. bool wxListHeaderData::IsHit( int x, int y ) const
  260. {
  261. return ((x >= m_xpos) && (x <= m_xpos+m_width) && (y >= m_ypos) && (y <= m_ypos+m_height));
  262. }
  263. void wxListHeaderData::GetItem( wxListItem& item )
  264. {
  265. long mask = item.m_mask;
  266. if ( !mask )
  267. {
  268. // by default, get everything for backwards compatibility
  269. mask = -1;
  270. }
  271. if ( mask & wxLIST_MASK_STATE )
  272. item.m_state = m_state;
  273. if ( mask & wxLIST_MASK_TEXT )
  274. item.m_text = m_text;
  275. if ( mask & wxLIST_MASK_IMAGE )
  276. item.m_image = m_image;
  277. if ( mask & wxLIST_MASK_WIDTH )
  278. item.m_width = m_width;
  279. if ( mask & wxLIST_MASK_FORMAT )
  280. item.m_format = m_format;
  281. }
  282. int wxListHeaderData::GetImage() const
  283. {
  284. return m_image;
  285. }
  286. int wxListHeaderData::GetWidth() const
  287. {
  288. return m_width;
  289. }
  290. int wxListHeaderData::GetFormat() const
  291. {
  292. return m_format;
  293. }
  294. int wxListHeaderData::GetState() const
  295. {
  296. return m_state;
  297. }
  298. //-----------------------------------------------------------------------------
  299. // wxListLineData
  300. //-----------------------------------------------------------------------------
  301. inline int wxListLineData::GetMode() const
  302. {
  303. return m_owner->GetListCtrl()->GetWindowStyleFlag() & wxLC_MASK_TYPE;
  304. }
  305. inline bool wxListLineData::InReportView() const
  306. {
  307. return m_owner->HasFlag(wxLC_REPORT);
  308. }
  309. inline bool wxListLineData::IsVirtual() const
  310. {
  311. return m_owner->IsVirtual();
  312. }
  313. wxListLineData::wxListLineData( wxListMainWindow *owner )
  314. {
  315. m_owner = owner;
  316. if ( InReportView() )
  317. m_gi = NULL;
  318. else // !report
  319. m_gi = new GeometryInfo;
  320. m_highlighted = false;
  321. InitItems( GetMode() == wxLC_REPORT ? m_owner->GetColumnCount() : 1 );
  322. }
  323. void wxListLineData::CalculateSize( wxDC *dc, int spacing )
  324. {
  325. wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
  326. wxCHECK_RET( node, wxT("no subitems at all??") );
  327. wxListItemData *item = node->GetData();
  328. wxString s;
  329. wxCoord lw, lh;
  330. switch ( GetMode() )
  331. {
  332. case wxLC_ICON:
  333. case wxLC_SMALL_ICON:
  334. m_gi->m_rectAll.width = spacing;
  335. s = item->GetText();
  336. if ( s.empty() )
  337. {
  338. lh =
  339. m_gi->m_rectLabel.width =
  340. m_gi->m_rectLabel.height = 0;
  341. }
  342. else // has label
  343. {
  344. dc->GetTextExtent( s, &lw, &lh );
  345. lw += EXTRA_WIDTH;
  346. lh += EXTRA_HEIGHT;
  347. m_gi->m_rectAll.height = spacing + lh;
  348. if (lw > spacing)
  349. m_gi->m_rectAll.width = lw;
  350. m_gi->m_rectLabel.width = lw;
  351. m_gi->m_rectLabel.height = lh;
  352. }
  353. if (item->HasImage())
  354. {
  355. int w, h;
  356. m_owner->GetImageSize( item->GetImage(), w, h );
  357. m_gi->m_rectIcon.width = w + 8;
  358. m_gi->m_rectIcon.height = h + 8;
  359. if ( m_gi->m_rectIcon.width > m_gi->m_rectAll.width )
  360. m_gi->m_rectAll.width = m_gi->m_rectIcon.width;
  361. if ( m_gi->m_rectIcon.height + lh > m_gi->m_rectAll.height - 4 )
  362. m_gi->m_rectAll.height = m_gi->m_rectIcon.height + lh + 4;
  363. }
  364. if ( item->HasText() )
  365. {
  366. m_gi->m_rectHighlight.width = m_gi->m_rectLabel.width;
  367. m_gi->m_rectHighlight.height = m_gi->m_rectLabel.height;
  368. }
  369. else // no text, highlight the icon
  370. {
  371. m_gi->m_rectHighlight.width = m_gi->m_rectIcon.width;
  372. m_gi->m_rectHighlight.height = m_gi->m_rectIcon.height;
  373. }
  374. break;
  375. case wxLC_LIST:
  376. s = item->GetTextForMeasuring();
  377. dc->GetTextExtent( s, &lw, &lh );
  378. lw += EXTRA_WIDTH;
  379. lh += EXTRA_HEIGHT;
  380. m_gi->m_rectLabel.width = lw;
  381. m_gi->m_rectLabel.height = lh;
  382. m_gi->m_rectAll.width = lw;
  383. m_gi->m_rectAll.height = lh;
  384. if (item->HasImage())
  385. {
  386. int w, h;
  387. m_owner->GetImageSize( item->GetImage(), w, h );
  388. m_gi->m_rectIcon.width = w;
  389. m_gi->m_rectIcon.height = h;
  390. m_gi->m_rectAll.width += 4 + w;
  391. if (h > m_gi->m_rectAll.height)
  392. m_gi->m_rectAll.height = h;
  393. }
  394. m_gi->m_rectHighlight.width = m_gi->m_rectAll.width;
  395. m_gi->m_rectHighlight.height = m_gi->m_rectAll.height;
  396. break;
  397. case wxLC_REPORT:
  398. wxFAIL_MSG( wxT("unexpected call to SetSize") );
  399. break;
  400. default:
  401. wxFAIL_MSG( wxT("unknown mode") );
  402. break;
  403. }
  404. }
  405. void wxListLineData::SetPosition( int x, int y, int spacing )
  406. {
  407. wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
  408. wxCHECK_RET( node, wxT("no subitems at all??") );
  409. wxListItemData *item = node->GetData();
  410. switch ( GetMode() )
  411. {
  412. case wxLC_ICON:
  413. case wxLC_SMALL_ICON:
  414. m_gi->m_rectAll.x = x;
  415. m_gi->m_rectAll.y = y;
  416. if ( item->HasImage() )
  417. {
  418. m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 4 +
  419. (m_gi->m_rectAll.width - m_gi->m_rectIcon.width) / 2;
  420. m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 4;
  421. }
  422. if ( item->HasText() )
  423. {
  424. if (m_gi->m_rectAll.width > spacing)
  425. m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2);
  426. else
  427. m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2) + (spacing / 2) - (m_gi->m_rectLabel.width / 2);
  428. m_gi->m_rectLabel.y = m_gi->m_rectAll.y + m_gi->m_rectAll.height + 2 - m_gi->m_rectLabel.height;
  429. m_gi->m_rectHighlight.x = m_gi->m_rectLabel.x - 2;
  430. m_gi->m_rectHighlight.y = m_gi->m_rectLabel.y - 2;
  431. }
  432. else // no text, highlight the icon
  433. {
  434. m_gi->m_rectHighlight.x = m_gi->m_rectIcon.x - 4;
  435. m_gi->m_rectHighlight.y = m_gi->m_rectIcon.y - 4;
  436. }
  437. break;
  438. case wxLC_LIST:
  439. m_gi->m_rectAll.x = x;
  440. m_gi->m_rectAll.y = y;
  441. m_gi->m_rectHighlight.x = m_gi->m_rectAll.x;
  442. m_gi->m_rectHighlight.y = m_gi->m_rectAll.y;
  443. m_gi->m_rectLabel.y = m_gi->m_rectAll.y + 2;
  444. if (item->HasImage())
  445. {
  446. m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 2;
  447. m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 2;
  448. m_gi->m_rectLabel.x = m_gi->m_rectAll.x + 4 + (EXTRA_WIDTH/2) + m_gi->m_rectIcon.width;
  449. }
  450. else
  451. {
  452. m_gi->m_rectLabel.x = m_gi->m_rectAll.x + (EXTRA_WIDTH/2);
  453. }
  454. break;
  455. case wxLC_REPORT:
  456. wxFAIL_MSG( wxT("unexpected call to SetPosition") );
  457. break;
  458. default:
  459. wxFAIL_MSG( wxT("unknown mode") );
  460. break;
  461. }
  462. }
  463. void wxListLineData::InitItems( int num )
  464. {
  465. for (int i = 0; i < num; i++)
  466. m_items.Append( new wxListItemData(m_owner) );
  467. }
  468. void wxListLineData::SetItem( int index, const wxListItem &info )
  469. {
  470. wxListItemDataList::compatibility_iterator node = m_items.Item( index );
  471. wxCHECK_RET( node, wxT("invalid column index in SetItem") );
  472. wxListItemData *item = node->GetData();
  473. item->SetItem( info );
  474. }
  475. void wxListLineData::GetItem( int index, wxListItem &info )
  476. {
  477. wxListItemDataList::compatibility_iterator node = m_items.Item( index );
  478. if (node)
  479. {
  480. wxListItemData *item = node->GetData();
  481. item->GetItem( info );
  482. }
  483. }
  484. wxString wxListLineData::GetText(int index) const
  485. {
  486. wxString s;
  487. wxListItemDataList::compatibility_iterator node = m_items.Item( index );
  488. if (node)
  489. {
  490. wxListItemData *item = node->GetData();
  491. s = item->GetText();
  492. }
  493. return s;
  494. }
  495. void wxListLineData::SetText( int index, const wxString& s )
  496. {
  497. wxListItemDataList::compatibility_iterator node = m_items.Item( index );
  498. if (node)
  499. {
  500. wxListItemData *item = node->GetData();
  501. item->SetText( s );
  502. }
  503. }
  504. void wxListLineData::SetImage( int index, int image )
  505. {
  506. wxListItemDataList::compatibility_iterator node = m_items.Item( index );
  507. wxCHECK_RET( node, wxT("invalid column index in SetImage()") );
  508. wxListItemData *item = node->GetData();
  509. item->SetImage(image);
  510. }
  511. int wxListLineData::GetImage( int index ) const
  512. {
  513. wxListItemDataList::compatibility_iterator node = m_items.Item( index );
  514. wxCHECK_MSG( node, -1, wxT("invalid column index in GetImage()") );
  515. wxListItemData *item = node->GetData();
  516. return item->GetImage();
  517. }
  518. wxListItemAttr *wxListLineData::GetAttr() const
  519. {
  520. wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
  521. wxCHECK_MSG( node, NULL, wxT("invalid column index in GetAttr()") );
  522. wxListItemData *item = node->GetData();
  523. return item->GetAttr();
  524. }
  525. void wxListLineData::SetAttr(wxListItemAttr *attr)
  526. {
  527. wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
  528. wxCHECK_RET( node, wxT("invalid column index in SetAttr()") );
  529. wxListItemData *item = node->GetData();
  530. item->SetAttr(attr);
  531. }
  532. void wxListLineData::ApplyAttributes(wxDC *dc,
  533. const wxRect& rectHL,
  534. bool highlighted,
  535. bool current)
  536. {
  537. const wxListItemAttr * const attr = GetAttr();
  538. wxWindow * const listctrl = m_owner->GetParent();
  539. const bool hasFocus = listctrl->HasFocus()
  540. #if defined(__WXMAC__) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON
  541. && IsControlActive( (ControlRef)listctrl->GetHandle() )
  542. #endif
  543. ;
  544. // fg colour
  545. // don't use foreground colour for drawing highlighted items - this might
  546. // make them completely invisible (and there is no way to do bit
  547. // arithmetics on wxColour, unfortunately)
  548. wxColour colText;
  549. if ( highlighted )
  550. {
  551. #ifdef __WXMAC__
  552. if ( hasFocus )
  553. colText = *wxWHITE;
  554. else
  555. colText = *wxBLACK;
  556. #else
  557. if ( hasFocus )
  558. colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
  559. else
  560. colText = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT);
  561. #endif
  562. }
  563. else if ( attr && attr->HasTextColour() )
  564. colText = attr->GetTextColour();
  565. else
  566. colText = listctrl->GetForegroundColour();
  567. dc->SetTextForeground(colText);
  568. // font
  569. wxFont font;
  570. if ( attr && attr->HasFont() )
  571. font = attr->GetFont();
  572. else
  573. font = listctrl->GetFont();
  574. dc->SetFont(font);
  575. // background
  576. if ( highlighted )
  577. {
  578. // Use the renderer method to ensure that the selected items use the
  579. // native look.
  580. int flags = wxCONTROL_SELECTED;
  581. if ( hasFocus )
  582. flags |= wxCONTROL_FOCUSED;
  583. if (current)
  584. flags |= wxCONTROL_CURRENT;
  585. wxRendererNative::Get().
  586. DrawItemSelectionRect( m_owner, *dc, rectHL, flags );
  587. }
  588. else if ( attr && attr->HasBackgroundColour() )
  589. {
  590. // Draw the background using the items custom background colour.
  591. dc->SetBrush(attr->GetBackgroundColour());
  592. dc->SetPen(*wxTRANSPARENT_PEN);
  593. dc->DrawRectangle(rectHL);
  594. }
  595. // just for debugging to better see where the items are
  596. #if 0
  597. dc->SetPen(*wxRED_PEN);
  598. dc->SetBrush(*wxTRANSPARENT_BRUSH);
  599. dc->DrawRectangle( m_gi->m_rectAll );
  600. dc->SetPen(*wxGREEN_PEN);
  601. dc->DrawRectangle( m_gi->m_rectIcon );
  602. #endif
  603. }
  604. void wxListLineData::Draw(wxDC *dc, bool current)
  605. {
  606. wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
  607. wxCHECK_RET( node, wxT("no subitems at all??") );
  608. ApplyAttributes(dc, m_gi->m_rectHighlight, IsHighlighted(), current);
  609. wxListItemData *item = node->GetData();
  610. if (item->HasImage())
  611. {
  612. // centre the image inside our rectangle, this looks nicer when items
  613. // ae aligned in a row
  614. const wxRect& rectIcon = m_gi->m_rectIcon;
  615. m_owner->DrawImage(item->GetImage(), dc, rectIcon.x, rectIcon.y);
  616. }
  617. if (item->HasText())
  618. {
  619. const wxRect& rectLabel = m_gi->m_rectLabel;
  620. wxDCClipper clipper(*dc, rectLabel);
  621. dc->DrawText(item->GetText(), rectLabel.x, rectLabel.y);
  622. }
  623. }
  624. void wxListLineData::DrawInReportMode( wxDC *dc,
  625. const wxRect& rect,
  626. const wxRect& rectHL,
  627. bool highlighted,
  628. bool current )
  629. {
  630. // TODO: later we should support setting different attributes for
  631. // different columns - to do it, just add "col" argument to
  632. // GetAttr() and move these lines into the loop below
  633. ApplyAttributes(dc, rectHL, highlighted, current);
  634. wxCoord x = rect.x + HEADER_OFFSET_X,
  635. yMid = rect.y + rect.height/2;
  636. #ifdef __WXGTK__
  637. // This probably needs to be done
  638. // on all platforms as the icons
  639. // otherwise nearly touch the border
  640. x += 2;
  641. #endif
  642. size_t col = 0;
  643. for ( wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
  644. node;
  645. node = node->GetNext(), col++ )
  646. {
  647. wxListItemData *item = node->GetData();
  648. int width = m_owner->GetColumnWidth(col);
  649. int xOld = x;
  650. x += width;
  651. width -= 8;
  652. const int wText = width;
  653. wxDCClipper clipper(*dc, xOld, rect.y, wText, rect.height);
  654. if ( item->HasImage() )
  655. {
  656. int ix, iy;
  657. m_owner->GetImageSize( item->GetImage(), ix, iy );
  658. m_owner->DrawImage( item->GetImage(), dc, xOld, yMid - iy/2 );
  659. ix += IMAGE_MARGIN_IN_REPORT_MODE;
  660. xOld += ix;
  661. width -= ix;
  662. }
  663. if ( item->HasText() )
  664. DrawTextFormatted(dc, item->GetText(), col, xOld, yMid, width);
  665. }
  666. }
  667. void wxListLineData::DrawTextFormatted(wxDC *dc,
  668. const wxString& textOrig,
  669. int col,
  670. int x,
  671. int yMid,
  672. int width)
  673. {
  674. // we don't support displaying multiple lines currently (and neither does
  675. // wxMSW FWIW) so just merge all the lines
  676. wxString text(textOrig);
  677. text.Replace(wxT("\n"), wxT(" "));
  678. wxCoord w, h;
  679. dc->GetTextExtent(text, &w, &h);
  680. const wxCoord y = yMid - (h + 1)/2;
  681. wxDCClipper clipper(*dc, x, y, width, h);
  682. // determine if the string can fit inside the current width
  683. if (w <= width)
  684. {
  685. // it can, draw it using the items alignment
  686. wxListItem item;
  687. m_owner->GetColumn(col, item);
  688. switch ( item.GetAlign() )
  689. {
  690. case wxLIST_FORMAT_LEFT:
  691. // nothing to do
  692. break;
  693. case wxLIST_FORMAT_RIGHT:
  694. x += width - w;
  695. break;
  696. case wxLIST_FORMAT_CENTER:
  697. x += (width - w) / 2;
  698. break;
  699. default:
  700. wxFAIL_MSG( wxT("unknown list item format") );
  701. break;
  702. }
  703. dc->DrawText(text, x, y);
  704. }
  705. else // otherwise, truncate and add an ellipsis if possible
  706. {
  707. // determine the base width
  708. wxString ellipsis(wxT("..."));
  709. wxCoord base_w;
  710. dc->GetTextExtent(ellipsis, &base_w, &h);
  711. // continue until we have enough space or only one character left
  712. wxCoord w_c, h_c;
  713. size_t len = text.length();
  714. wxString drawntext = text.Left(len);
  715. while (len > 1)
  716. {
  717. dc->GetTextExtent(drawntext.Last(), &w_c, &h_c);
  718. drawntext.RemoveLast();
  719. len--;
  720. w -= w_c;
  721. if (w + base_w <= width)
  722. break;
  723. }
  724. // if still not enough space, remove ellipsis characters
  725. while (ellipsis.length() > 0 && w + base_w > width)
  726. {
  727. ellipsis = ellipsis.Left(ellipsis.length() - 1);
  728. dc->GetTextExtent(ellipsis, &base_w, &h);
  729. }
  730. // now draw the text
  731. dc->DrawText(drawntext, x, y);
  732. dc->DrawText(ellipsis, x + w, y);
  733. }
  734. }
  735. bool wxListLineData::Highlight( bool on )
  736. {
  737. wxCHECK_MSG( !IsVirtual(), false, wxT("unexpected call to Highlight") );
  738. if ( on == m_highlighted )
  739. return false;
  740. m_highlighted = on;
  741. return true;
  742. }
  743. void wxListLineData::ReverseHighlight( void )
  744. {
  745. Highlight(!IsHighlighted());
  746. }
  747. //-----------------------------------------------------------------------------
  748. // wxListHeaderWindow
  749. //-----------------------------------------------------------------------------
  750. BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow)
  751. EVT_PAINT (wxListHeaderWindow::OnPaint)
  752. EVT_MOUSE_EVENTS (wxListHeaderWindow::OnMouse)
  753. END_EVENT_TABLE()
  754. void wxListHeaderWindow::Init()
  755. {
  756. m_currentCursor = NULL;
  757. m_isDragging = false;
  758. m_dirty = false;
  759. m_sendSetColumnWidth = false;
  760. }
  761. wxListHeaderWindow::wxListHeaderWindow()
  762. {
  763. Init();
  764. m_owner = NULL;
  765. m_resizeCursor = NULL;
  766. }
  767. bool wxListHeaderWindow::Create( wxWindow *win,
  768. wxWindowID id,
  769. wxListMainWindow *owner,
  770. const wxPoint& pos,
  771. const wxSize& size,
  772. long style,
  773. const wxString &name )
  774. {
  775. if ( !wxWindow::Create(win, id, pos, size, style, name) )
  776. return false;
  777. Init();
  778. m_owner = owner;
  779. m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
  780. #if _USE_VISATTR
  781. wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
  782. SetOwnForegroundColour( attr.colFg );
  783. SetOwnBackgroundColour( attr.colBg );
  784. if (!m_hasFont)
  785. SetOwnFont( attr.font );
  786. #else
  787. SetOwnForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
  788. SetOwnBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
  789. if (!m_hasFont)
  790. SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT ));
  791. #endif
  792. return true;
  793. }
  794. wxListHeaderWindow::~wxListHeaderWindow()
  795. {
  796. delete m_resizeCursor;
  797. }
  798. #ifdef __WXUNIVERSAL__
  799. #include "wx/univ/renderer.h"
  800. #include "wx/univ/theme.h"
  801. #endif
  802. // shift the DC origin to match the position of the main window horz
  803. // scrollbar: this allows us to always use logical coords
  804. void wxListHeaderWindow::AdjustDC(wxDC& dc)
  805. {
  806. wxGenericListCtrl *parent = m_owner->GetListCtrl();
  807. int xpix;
  808. parent->GetScrollPixelsPerUnit( &xpix, NULL );
  809. int view_start;
  810. parent->GetViewStart( &view_start, NULL );
  811. int org_x = 0;
  812. int org_y = 0;
  813. dc.GetDeviceOrigin( &org_x, &org_y );
  814. // account for the horz scrollbar offset
  815. #ifdef __WXGTK__
  816. if (GetLayoutDirection() == wxLayout_RightToLeft)
  817. {
  818. // Maybe we just have to check for m_signX
  819. // in the DC, but I leave the #ifdef __WXGTK__
  820. // for now
  821. dc.SetDeviceOrigin( org_x + (view_start * xpix), org_y );
  822. }
  823. else
  824. #endif
  825. dc.SetDeviceOrigin( org_x - (view_start * xpix), org_y );
  826. }
  827. void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
  828. {
  829. wxGenericListCtrl *parent = m_owner->GetListCtrl();
  830. wxPaintDC dc( this );
  831. AdjustDC( dc );
  832. dc.SetFont( GetFont() );
  833. // width and height of the entire header window
  834. int w, h;
  835. GetClientSize( &w, &h );
  836. parent->CalcUnscrolledPosition(w, 0, &w, NULL);
  837. dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
  838. dc.SetTextForeground(GetForegroundColour());
  839. int x = HEADER_OFFSET_X;
  840. int numColumns = m_owner->GetColumnCount();
  841. wxListItem item;
  842. for ( int i = 0; i < numColumns && x < w; i++ )
  843. {
  844. m_owner->GetColumn( i, item );
  845. int wCol = item.m_width;
  846. int cw = wCol;
  847. int ch = h;
  848. int flags = 0;
  849. if (!m_parent->IsEnabled())
  850. flags |= wxCONTROL_DISABLED;
  851. // NB: The code below is not really Mac-specific, but since we are close
  852. // to 2.8 release and I don't have time to test on other platforms, I
  853. // defined this only for wxMac. If this behaviour is desired on
  854. // other platforms, please go ahead and revise or remove the #ifdef.
  855. #ifdef __WXMAC__
  856. if ( !m_owner->IsVirtual() && (item.m_mask & wxLIST_MASK_STATE) &&
  857. (item.m_state & wxLIST_STATE_SELECTED) )
  858. flags |= wxCONTROL_SELECTED;
  859. #endif
  860. if (i == 0)
  861. flags |= wxCONTROL_SPECIAL; // mark as first column
  862. wxRendererNative::Get().DrawHeaderButton
  863. (
  864. this,
  865. dc,
  866. wxRect(x, HEADER_OFFSET_Y, cw, ch),
  867. flags
  868. );
  869. // see if we have enough space for the column label
  870. // for this we need the width of the text
  871. wxCoord wLabel;
  872. wxCoord hLabel;
  873. dc.GetTextExtent(item.GetText(), &wLabel, &hLabel);
  874. wLabel += 2 * EXTRA_WIDTH;
  875. // and the width of the icon, if any
  876. int ix = 0, iy = 0; // init them just to suppress the compiler warnings
  877. const int image = item.m_image;
  878. wxImageList *imageList;
  879. if ( image != -1 )
  880. {
  881. imageList = m_owner->GetSmallImageList();
  882. if ( imageList )
  883. {
  884. imageList->GetSize(image, ix, iy);
  885. wLabel += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE;
  886. }
  887. }
  888. else
  889. {
  890. imageList = NULL;
  891. }
  892. // ignore alignment if there is not enough space anyhow
  893. int xAligned;
  894. switch ( wLabel < cw ? item.GetAlign() : wxLIST_FORMAT_LEFT )
  895. {
  896. default:
  897. wxFAIL_MSG( wxT("unknown list item format") );
  898. // fall through
  899. case wxLIST_FORMAT_LEFT:
  900. xAligned = x;
  901. break;
  902. case wxLIST_FORMAT_RIGHT:
  903. xAligned = x + cw - wLabel;
  904. break;
  905. case wxLIST_FORMAT_CENTER:
  906. xAligned = x + (cw - wLabel) / 2;
  907. break;
  908. }
  909. // draw the text and image clipping them so that they
  910. // don't overwrite the column boundary
  911. wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h);
  912. // if we have an image, draw it on the right of the label
  913. if ( imageList )
  914. {
  915. imageList->Draw
  916. (
  917. image,
  918. dc,
  919. xAligned + wLabel - ix - HEADER_IMAGE_MARGIN_IN_REPORT_MODE,
  920. HEADER_OFFSET_Y + (h - iy)/2,
  921. wxIMAGELIST_DRAW_TRANSPARENT
  922. );
  923. }
  924. dc.DrawText( item.GetText(),
  925. xAligned + EXTRA_WIDTH, (h - hLabel) / 2 );
  926. x += wCol;
  927. }
  928. // Fill in what's missing to the right of the columns, otherwise we will
  929. // leave an unpainted area when columns are removed (and it looks better)
  930. if ( x < w )
  931. {
  932. wxRendererNative::Get().DrawHeaderButton
  933. (
  934. this,
  935. dc,
  936. wxRect(x, HEADER_OFFSET_Y, w - x, h),
  937. wxCONTROL_DIRTY // mark as last column
  938. );
  939. }
  940. }
  941. void wxListHeaderWindow::OnInternalIdle()
  942. {
  943. wxWindow::OnInternalIdle();
  944. if (m_sendSetColumnWidth)
  945. {
  946. m_owner->SetColumnWidth( m_colToSend, m_widthToSend );
  947. m_sendSetColumnWidth = false;
  948. }
  949. }
  950. void wxListHeaderWindow::DrawCurrent()
  951. {
  952. #if 1
  953. // m_owner->SetColumnWidth( m_column, m_currentX - m_minX );
  954. m_sendSetColumnWidth = true;
  955. m_colToSend = m_column;
  956. m_widthToSend = m_currentX - m_minX;
  957. #else
  958. int x1 = m_currentX;
  959. int y1 = 0;
  960. m_owner->ClientToScreen( &x1, &y1 );
  961. int x2 = m_currentX;
  962. int y2 = 0;
  963. m_owner->GetClientSize( NULL, &y2 );
  964. m_owner->ClientToScreen( &x2, &y2 );
  965. wxScreenDC dc;
  966. dc.SetLogicalFunction( wxINVERT );
  967. dc.SetPen( wxPen(*wxBLACK, 2) );
  968. dc.SetBrush( *wxTRANSPARENT_BRUSH );
  969. AdjustDC(dc);
  970. dc.DrawLine( x1, y1, x2, y2 );
  971. dc.SetLogicalFunction( wxCOPY );
  972. dc.SetPen( wxNullPen );
  973. dc.SetBrush( wxNullBrush );
  974. #endif
  975. }
  976. void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
  977. {
  978. wxGenericListCtrl *parent = m_owner->GetListCtrl();
  979. // we want to work with logical coords
  980. int x;
  981. parent->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
  982. int y = event.GetY();
  983. if (m_isDragging)
  984. {
  985. SendListEvent(wxEVT_LIST_COL_DRAGGING, event.GetPosition());
  986. // we don't draw the line beyond our window, but we allow dragging it
  987. // there
  988. int w = 0;
  989. GetClientSize( &w, NULL );
  990. parent->CalcUnscrolledPosition(w, 0, &w, NULL);
  991. w -= 6;
  992. // erase the line if it was drawn
  993. if ( m_currentX < w )
  994. DrawCurrent();
  995. if (event.ButtonUp())
  996. {
  997. ReleaseMouse();
  998. m_isDragging = false;
  999. m_dirty = true;
  1000. m_owner->SetColumnWidth( m_column, m_currentX - m_minX );
  1001. SendListEvent(wxEVT_LIST_COL_END_DRAG, event.GetPosition());
  1002. }
  1003. else
  1004. {
  1005. if (x > m_minX + 7)
  1006. m_currentX = x;
  1007. else
  1008. m_currentX = m_minX + 7;
  1009. // draw in the new location
  1010. if ( m_currentX < w )
  1011. DrawCurrent();
  1012. }
  1013. }
  1014. else // not dragging
  1015. {
  1016. m_minX = 0;
  1017. bool hit_border = false;
  1018. // end of the current column
  1019. int xpos = 0;
  1020. // find the column where this event occurred
  1021. int col,
  1022. countCol = m_owner->GetColumnCount();
  1023. for (col = 0; col < countCol; col++)
  1024. {
  1025. xpos += m_owner->GetColumnWidth( col );
  1026. m_column = col;
  1027. if ( (abs(x-xpos) < 3) && (y < 22) )
  1028. {
  1029. // near the column border
  1030. hit_border = true;
  1031. break;
  1032. }
  1033. if ( x < xpos )
  1034. {
  1035. // inside the column
  1036. break;
  1037. }
  1038. m_minX = xpos;
  1039. }
  1040. if ( col == countCol )
  1041. m_column = -1;
  1042. if (event.LeftDown() || event.RightUp())
  1043. {
  1044. if (hit_border && event.LeftDown())
  1045. {
  1046. if ( SendListEvent(wxEVT_LIST_COL_BEGIN_DRAG,
  1047. event.GetPosition()) )
  1048. {
  1049. m_isDragging = true;
  1050. m_currentX = x;
  1051. CaptureMouse();
  1052. DrawCurrent();
  1053. }
  1054. //else: column resizing was vetoed by the user code
  1055. }
  1056. else // click on a column
  1057. {
  1058. // record the selected state of the columns
  1059. if (event.LeftDown())
  1060. {
  1061. for (int i=0; i < m_owner->GetColumnCount(); i++)
  1062. {
  1063. wxListItem colItem;
  1064. m_owner->GetColumn(i, colItem);
  1065. long state = colItem.GetState();
  1066. if (i == m_column)
  1067. colItem.SetState(state | wxLIST_STATE_SELECTED);
  1068. else
  1069. colItem.SetState(state & ~wxLIST_STATE_SELECTED);
  1070. m_owner->SetColumn(i, colItem);
  1071. }
  1072. }
  1073. SendListEvent( event.LeftDown()
  1074. ? wxEVT_LIST_COL_CLICK
  1075. : wxEVT_LIST_COL_RIGHT_CLICK,
  1076. event.GetPosition());
  1077. }
  1078. }
  1079. else if (event.Moving())
  1080. {
  1081. bool setCursor;
  1082. if (hit_border)
  1083. {
  1084. setCursor = m_currentCursor == wxSTANDARD_CURSOR;
  1085. m_currentCursor = m_resizeCursor;
  1086. }
  1087. else
  1088. {
  1089. setCursor = m_currentCursor != wxSTANDARD_CURSOR;
  1090. m_currentCursor = wxSTANDARD_CURSOR;
  1091. }
  1092. if ( setCursor )
  1093. SetCursor(*m_currentCursor);
  1094. }
  1095. }
  1096. }
  1097. bool wxListHeaderWindow::SendListEvent(wxEventType type, const wxPoint& pos)
  1098. {
  1099. wxWindow *parent = GetParent();
  1100. wxListEvent le( type, parent->GetId() );
  1101. le.SetEventObject( parent );
  1102. le.m_pointDrag = pos;
  1103. // the position should be relative to the parent window, not
  1104. // this one for compatibility with MSW and common sense: the
  1105. // user code doesn't know anything at all about this header
  1106. // window, so why should it get positions relative to it?
  1107. le.m_pointDrag.y -= GetSize().y;
  1108. le.m_col = m_column;
  1109. return !parent->GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
  1110. }
  1111. //-----------------------------------------------------------------------------
  1112. // wxListRenameTimer (internal)
  1113. //-----------------------------------------------------------------------------
  1114. wxListRenameTimer::wxListRenameTimer( wxListMainWindow *owner )
  1115. {
  1116. m_owner = owner;
  1117. }
  1118. void wxListRenameTimer::Notify()
  1119. {
  1120. m_owner->OnRenameTimer();
  1121. }
  1122. //-----------------------------------------------------------------------------
  1123. // wxListFindTimer (internal)
  1124. //-----------------------------------------------------------------------------
  1125. void wxListFindTimer::Notify()
  1126. {
  1127. m_owner->OnFindTimer();
  1128. }
  1129. //-----------------------------------------------------------------------------
  1130. // wxListTextCtrlWrapper (internal)
  1131. //-----------------------------------------------------------------------------
  1132. BEGIN_EVENT_TABLE(wxListTextCtrlWrapper, wxEvtHandler)
  1133. EVT_CHAR (wxListTextCtrlWrapper::OnChar)
  1134. EVT_KEY_UP (wxListTextCtrlWrapper::OnKeyUp)
  1135. EVT_KILL_FOCUS (wxListTextCtrlWrapper::OnKillFocus)
  1136. END_EVENT_TABLE()
  1137. wxListTextCtrlWrapper::wxListTextCtrlWrapper(wxListMainWindow *owner,
  1138. wxTextCtrl *text,
  1139. size_t itemEdit)
  1140. : m_startValue(owner->GetItemText(itemEdit)),
  1141. m_itemEdited(itemEdit)
  1142. {
  1143. m_owner = owner;
  1144. m_text = text;
  1145. m_aboutToFinish = false;
  1146. wxGenericListCtrl *parent = m_owner->GetListCtrl();
  1147. wxRect rectLabel = owner->GetLineLabelRect(itemEdit);
  1148. parent->CalcScrolledPosition(rectLabel.x, rectLabel.y,
  1149. &rectLabel.x, &rectLabel.y);
  1150. m_text->Create(owner, wxID_ANY, m_startValue,
  1151. wxPoint(rectLabel.x-4,rectLabel.y-4),
  1152. wxSize(rectLabel.width+11,rectLabel.height+8));
  1153. m_text->SetFocus();
  1154. m_text->PushEventHandler(this);
  1155. }
  1156. void wxListTextCtrlWrapper::EndEdit(EndReason reason)
  1157. {
  1158. m_aboutToFinish = true;
  1159. switch ( reason )
  1160. {
  1161. case End_Accept:
  1162. // Notify the owner about the changes
  1163. AcceptChanges();
  1164. // Even if vetoed, close the control (consistent with MSW)
  1165. Finish( true );
  1166. break;
  1167. case End_Discard:
  1168. m_owner->OnRenameCancelled(m_itemEdited);
  1169. Finish( true );
  1170. break;
  1171. case End_Destroy:
  1172. // Don't generate any notifications for the control being destroyed
  1173. // and don't set focus to it neither.
  1174. Finish(false);
  1175. break;
  1176. }
  1177. }
  1178. void wxListTextCtrlWrapper::Finish( bool setfocus )
  1179. {
  1180. m_text->RemoveEventHandler(this);
  1181. m_owner->ResetTextControl( m_text );
  1182. wxPendingDelete.Append( this );
  1183. if (setfocus)
  1184. m_owner->SetFocus();
  1185. }
  1186. bool wxListTextCtrlWrapper::AcceptChanges()
  1187. {
  1188. const wxString value = m_text->GetValue();
  1189. // notice that we should always call OnRenameAccept() to generate the "end
  1190. // label editing" event, even if the user hasn't really changed anything
  1191. if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
  1192. {
  1193. // vetoed by the user
  1194. return false;
  1195. }
  1196. // accepted, do rename the item (unless nothing changed)
  1197. if ( value != m_startValue )
  1198. m_owner->SetItemText(m_itemEdited, value);
  1199. return true;
  1200. }
  1201. void wxListTextCtrlWrapper::OnChar( wxKeyEvent &event )
  1202. {
  1203. if ( !CheckForEndEditKey(event) )
  1204. event.Skip();
  1205. }
  1206. bool wxListTextCtrlWrapper::CheckForEndEditKey(const wxKeyEvent& event)
  1207. {
  1208. switch ( event.m_keyCode )
  1209. {
  1210. case WXK_RETURN:
  1211. EndEdit( End_Accept );
  1212. break;
  1213. case WXK_ESCAPE:
  1214. EndEdit( End_Discard );
  1215. break;
  1216. default:
  1217. return false;
  1218. }
  1219. return true;
  1220. }
  1221. void wxListTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
  1222. {
  1223. if (m_aboutToFinish)
  1224. {
  1225. // auto-grow the textctrl:
  1226. wxSize parentSize = m_owner->GetSize();
  1227. wxPoint myPos = m_text->GetPosition();
  1228. wxSize mySize = m_text->GetSize();
  1229. int sx, sy;
  1230. m_text->GetTextExtent(m_text->GetValue() + wxT("MM"), &sx, &sy);
  1231. if (myPos.x + sx > parentSize.x)
  1232. sx = parentSize.x - myPos.x;
  1233. if (mySize.x > sx)
  1234. sx = mySize.x;
  1235. m_text->SetSize(sx, wxDefaultCoord);
  1236. }
  1237. event.Skip();
  1238. }
  1239. void wxListTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
  1240. {
  1241. if ( !m_aboutToFinish )
  1242. {
  1243. if ( !AcceptChanges() )
  1244. m_owner->OnRenameCancelled( m_itemEdited );
  1245. Finish( false );
  1246. }
  1247. // We must let the native text control handle focus
  1248. event.Skip();
  1249. }
  1250. //-----------------------------------------------------------------------------
  1251. // wxListMainWindow
  1252. //-----------------------------------------------------------------------------
  1253. BEGIN_EVENT_TABLE(wxListMainWindow, wxWindow)
  1254. EVT_PAINT (wxListMainWindow::OnPaint)
  1255. EVT_MOUSE_EVENTS (wxListMainWindow::OnMouse)
  1256. EVT_CHAR_HOOK (wxListMainWindow::OnCharHook)
  1257. EVT_CHAR (wxListMainWindow::OnChar)
  1258. EVT_KEY_DOWN (wxListMainWindow::OnKeyDown)
  1259. EVT_KEY_UP (wxListMainWindow::OnKeyUp)
  1260. EVT_SET_FOCUS (wxListMainWindow::OnSetFocus)
  1261. EVT_KILL_FOCUS (wxListMainWindow::OnKillFocus)
  1262. EVT_SCROLLWIN (wxListMainWindow::OnScroll)
  1263. EVT_CHILD_FOCUS (wxListMainWindow::OnChildFocus)
  1264. END_EVENT_TABLE()
  1265. void wxListMainWindow::Init()
  1266. {
  1267. m_dirty = true;
  1268. m_countVirt = 0;
  1269. m_lineFrom =
  1270. m_lineTo = (size_t)-1;
  1271. m_linesPerPage = 0;
  1272. m_headerWidth =
  1273. m_lineHeight = 0;
  1274. m_small_image_list = NULL;
  1275. m_normal_image_list = NULL;
  1276. m_small_spacing = 30;
  1277. m_normal_spacing = 40;
  1278. m_hasFocus = false;
  1279. m_dragCount = 0;
  1280. m_isCreated = false;
  1281. m_lastOnSame = false;
  1282. m_renameTimer = new wxListRenameTimer( this );
  1283. m_findTimer = NULL;
  1284. m_findBell = 0; // default is to not ring bell at all
  1285. m_textctrlWrapper = NULL;
  1286. m_current =
  1287. m_lineLastClicked =
  1288. m_lineSelectSingleOnUp =
  1289. m_lineBeforeLastClicked = (size_t)-1;
  1290. }
  1291. wxListMainWindow::wxListMainWindow()
  1292. {
  1293. Init();
  1294. m_highlightBrush =
  1295. m_highlightUnfocusedBrush = NULL;
  1296. }
  1297. wxListMainWindow::wxListMainWindow( wxWindow *parent,
  1298. wxWindowID id,
  1299. const wxPoint& pos,
  1300. const wxSize& size )
  1301. : wxWindow( parent, id, pos, size,
  1302. wxWANTS_CHARS | wxBORDER_NONE )
  1303. {
  1304. Init();
  1305. m_highlightBrush = new wxBrush
  1306. (
  1307. wxSystemSettings::GetColour
  1308. (
  1309. wxSYS_COLOUR_HIGHLIGHT
  1310. ),
  1311. wxBRUSHSTYLE_SOLID
  1312. );
  1313. m_highlightUnfocusedBrush = new wxBrush
  1314. (
  1315. wxSystemSettings::GetColour
  1316. (
  1317. wxSYS_COLOUR_BTNSHADOW
  1318. ),
  1319. wxBRUSHSTYLE_SOLID
  1320. );
  1321. wxVisualAttributes attr = wxGenericListCtrl::GetClassDefaultAttributes();
  1322. SetOwnForegroundColour( attr.colFg );
  1323. SetOwnBackgroundColour( attr.colBg );
  1324. if (!m_hasFont)
  1325. SetOwnFont( attr.font );
  1326. }
  1327. wxListMainWindow::~wxListMainWindow()
  1328. {
  1329. if ( m_textctrlWrapper )
  1330. m_textctrlWrapper->EndEdit(wxListTextCtrlWrapper::End_Destroy);
  1331. DoDeleteAllItems();
  1332. WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
  1333. WX_CLEAR_ARRAY(m_aColWidths);
  1334. delete m_highlightBrush;
  1335. delete m_highlightUnfocusedBrush;
  1336. delete m_renameTimer;
  1337. delete m_findTimer;
  1338. }
  1339. void wxListMainWindow::SetReportView(bool inReportView)
  1340. {
  1341. const size_t count = m_lines.size();
  1342. for ( size_t n = 0; n < count; n++ )
  1343. {
  1344. m_lines[n].SetReportView(inReportView);
  1345. }
  1346. }
  1347. void wxListMainWindow::CacheLineData(size_t line)
  1348. {
  1349. wxGenericListCtrl *listctrl = GetListCtrl();
  1350. wxListLineData *ld = GetDummyLine();
  1351. size_t countCol = GetColumnCount();
  1352. for ( size_t col = 0; col < countCol; col++ )
  1353. {
  1354. ld->SetText(col, listctrl->OnGetItemText(line, col));
  1355. ld->SetImage(col, listctrl->OnGetItemColumnImage(line, col));
  1356. }
  1357. ld->SetAttr(listctrl->OnGetItemAttr(line));
  1358. }
  1359. wxListLineData *wxListMainWindow::GetDummyLine() const
  1360. {
  1361. wxASSERT_MSG( !IsEmpty(), wxT("invalid line index") );
  1362. wxASSERT_MSG( IsVirtual(), wxT("GetDummyLine() shouldn't be called") );
  1363. wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
  1364. // we need to recreate the dummy line if the number of columns in the
  1365. // control changed as it would have the incorrect number of fields
  1366. // otherwise
  1367. if ( !m_lines.IsEmpty() &&
  1368. m_lines[0].m_items.GetCount() != (size_t)GetColumnCount() )
  1369. {
  1370. self->m_lines.Clear();
  1371. }
  1372. if ( m_lines.IsEmpty() )
  1373. {
  1374. wxListLineData *line = new wxListLineData(self);
  1375. self->m_lines.Add(line);
  1376. // don't waste extra memory -- there never going to be anything
  1377. // else/more in this array
  1378. self->m_lines.Shrink();
  1379. }
  1380. return &m_lines[0];
  1381. }
  1382. // ----------------------------------------------------------------------------
  1383. // line geometry (report mode only)
  1384. // ----------------------------------------------------------------------------
  1385. wxCoord wxListMainWindow::GetLineHeight() const
  1386. {
  1387. // we cache the line height as calling GetTextExtent() is slow
  1388. if ( !m_lineHeight )
  1389. {
  1390. wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
  1391. wxClientDC dc( self );
  1392. dc.SetFont( GetFont() );
  1393. wxCoord y;
  1394. dc.GetTextExtent(wxT("H"), NULL, &y);
  1395. if ( m_small_image_list && m_small_image_list->GetImageCount() )
  1396. {
  1397. int iw = 0, ih = 0;
  1398. m_small_image_list->GetSize(0, iw, ih);
  1399. y = wxMax(y, ih);
  1400. }
  1401. y += EXTRA_HEIGHT;
  1402. self->m_lineHeight = y + LINE_SPACING;
  1403. }
  1404. return m_lineHeight;
  1405. }
  1406. wxCoord wxListMainWindow::GetLineY(size_t line) const
  1407. {
  1408. wxASSERT_MSG( InReportView(), wxT("only works in report mode") );
  1409. return LINE_SPACING + line * GetLineHeight();
  1410. }
  1411. wxRect wxListMainWindow::GetLineRect(size_t line) const
  1412. {
  1413. if ( !InReportView() )
  1414. return GetLine(line)->m_gi->m_rectAll;
  1415. wxRect rect;
  1416. rect.x = HEADER_OFFSET_X;
  1417. rect.y = GetLineY(line);
  1418. rect.width = GetHeaderWidth();
  1419. rect.height = GetLineHeight();
  1420. return rect;
  1421. }
  1422. wxRect wxListMainWindow::GetLineLabelRect(size_t line) const
  1423. {
  1424. if ( !InReportView() )
  1425. return GetLine(line)->m_gi->m_rectLabel;
  1426. int image_x = 0;
  1427. wxListLineData *data = GetLine(line);
  1428. wxListItemDataList::compatibility_iterator node = data->m_items.GetFirst();
  1429. if (node)
  1430. {
  1431. wxListItemData *item = node->GetData();
  1432. if ( item->HasImage() )
  1433. {
  1434. int ix, iy;
  1435. GetImageSize( item->GetImage(), ix, iy );
  1436. image_x = 3 + ix + IMAGE_MARGIN_IN_REPORT_MODE;
  1437. }
  1438. }
  1439. wxRect rect;
  1440. rect.x = image_x + HEADER_OFFSET_X;
  1441. rect.y = GetLineY(line);
  1442. rect.width = GetColumnWidth(0) - image_x;
  1443. rect.height = GetLineHeight();
  1444. return rect;
  1445. }
  1446. wxRect wxListMainWindow::GetLineIconRect(size_t line) const
  1447. {
  1448. if ( !InReportView() )
  1449. return GetLine(line)->m_gi->m_rectIcon;
  1450. wxListLineData *ld = GetLine(line);
  1451. wxASSERT_MSG( ld->HasImage(), wxT("should have an image") );
  1452. wxRect rect;
  1453. rect.x = HEADER_OFFSET_X;
  1454. rect.y = GetLineY(line);
  1455. GetImageSize(ld->GetImage(), rect.width, rect.height);
  1456. return rect;
  1457. }
  1458. wxRect wxListMainWindow::GetLineHighlightRect(size_t line) const
  1459. {
  1460. return InReportView() ? GetLineRect(line)
  1461. : GetLine(line)->m_gi->m_rectHighlight;
  1462. }
  1463. long wxListMainWindow::HitTestLine(size_t line, int x, int y) const
  1464. {
  1465. wxASSERT_MSG( line < GetItemCount(), wxT("invalid line in HitTestLine") );
  1466. wxListLineData *ld = GetLine(line);
  1467. if ( ld->HasImage() && GetLineIconRect(line).Contains(x, y) )
  1468. return wxLIST_HITTEST_ONITEMICON;
  1469. // VS: Testing for "ld->HasText() || InReportView()" instead of
  1470. // "ld->HasText()" is needed to make empty lines in report view
  1471. // possible
  1472. if ( ld->HasText() || InReportView() )
  1473. {
  1474. wxRect rect = InReportView() ? GetLineRect(line)
  1475. : GetLineLabelRect(line);
  1476. if ( rect.Contains(x, y) )
  1477. return wxLIST_HITTEST_ONITEMLABEL;
  1478. }
  1479. return 0;
  1480. }
  1481. // ----------------------------------------------------------------------------
  1482. // highlight (selection) handling
  1483. // ----------------------------------------------------------------------------
  1484. bool wxListMainWindow::IsHighlighted(size_t line) const
  1485. {
  1486. if ( IsVirtual() )
  1487. {
  1488. return m_selStore.IsSelected(line);
  1489. }
  1490. else // !virtual
  1491. {
  1492. wxListLineData *ld = GetLine(line);
  1493. wxCHECK_MSG( ld, false, wxT("invalid index in IsHighlighted") );
  1494. return ld->IsHighlighted();
  1495. }
  1496. }
  1497. void wxListMainWindow::HighlightLines( size_t lineFrom,
  1498. size_t lineTo,
  1499. bool highlight )
  1500. {
  1501. if ( IsVirtual() )
  1502. {
  1503. wxArrayInt linesChanged;
  1504. if ( !m_selStore.SelectRange(lineFrom, lineTo, highlight,
  1505. &linesChanged) )
  1506. {
  1507. // meny items changed state, refresh everything
  1508. RefreshLines(lineFrom, lineTo);
  1509. }
  1510. else // only a few items changed state, refresh only them
  1511. {
  1512. size_t count = linesChanged.GetCount();
  1513. for ( size_t n = 0; n < count; n++ )
  1514. {
  1515. RefreshLine(linesChanged[n]);
  1516. }
  1517. }
  1518. }
  1519. else // iterate over all items in non report view
  1520. {
  1521. for ( size_t line = lineFrom; line <= lineTo; line++ )
  1522. {
  1523. if ( HighlightLine(line, highlight) )
  1524. RefreshLine(line);
  1525. }
  1526. }
  1527. }
  1528. bool wxListMainWindow::HighlightLine( size_t line, bool highlight )
  1529. {
  1530. bool changed;
  1531. if ( IsVirtual() )
  1532. {
  1533. changed = m_selStore.SelectItem(line, highlight);
  1534. }
  1535. else // !virtual
  1536. {
  1537. wxListLineData *ld = GetLine(line);
  1538. wxCHECK_MSG( ld, false, wxT("invalid index in HighlightLine") );
  1539. changed = ld->Highlight(highlight);
  1540. }
  1541. if ( changed )
  1542. {
  1543. SendNotify( line, highlight ? wxEVT_LIST_ITEM_SELECTED
  1544. : wxEVT_LIST_ITEM_DESELECTED );
  1545. }
  1546. return changed;
  1547. }
  1548. void wxListMainWindow::RefreshLine( size_t line )
  1549. {
  1550. if ( InReportView() )
  1551. {
  1552. size_t visibleFrom, visibleTo;
  1553. GetVisibleLinesRange(&visibleFrom, &visibleTo);
  1554. if ( line < visibleFrom || line > visibleTo )
  1555. return;
  1556. }
  1557. wxRect rect = GetLineRect(line);
  1558. GetListCtrl()->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
  1559. RefreshRect( rect );
  1560. }
  1561. void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo )
  1562. {
  1563. // we suppose that they are ordered by caller
  1564. wxASSERT_MSG( lineFrom <= lineTo, wxT("indices in disorder") );
  1565. wxASSERT_MSG( lineTo < GetItemCount(), wxT("invalid line range") );
  1566. if ( InReportView() )
  1567. {
  1568. size_t visibleFrom, visibleTo;
  1569. GetVisibleLinesRange(&visibleFrom, &visibleTo);
  1570. if ( lineFrom < visibleFrom )
  1571. lineFrom = visibleFrom;
  1572. if ( lineTo > visibleTo )
  1573. lineTo = visibleTo;
  1574. wxRect rect;
  1575. rect.x = 0;
  1576. rect.y = GetLineY(lineFrom);
  1577. rect.width = GetClientSize().x;
  1578. rect.height = GetLineY(lineTo) - rect.y + GetLineHeight();
  1579. GetListCtrl()->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
  1580. RefreshRect( rect );
  1581. }
  1582. else // !report
  1583. {
  1584. // TODO: this should be optimized...
  1585. for ( size_t line = lineFrom; line <= lineTo; line++ )
  1586. {
  1587. RefreshLine(line);
  1588. }
  1589. }
  1590. }
  1591. void wxListMainWindow::RefreshAfter( size_t lineFrom )
  1592. {
  1593. if ( InReportView() )
  1594. {
  1595. size_t visibleFrom, visibleTo;
  1596. GetVisibleLinesRange(&visibleFrom, &visibleTo);
  1597. if ( lineFrom < visibleFrom )
  1598. lineFrom = visibleFrom;
  1599. else if ( lineFrom > visibleTo )
  1600. return;
  1601. wxRect rect;
  1602. rect.x = 0;
  1603. rect.y = GetLineY(lineFrom);
  1604. GetListCtrl()->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
  1605. wxSize size = GetClientSize();
  1606. rect.width = size.x;
  1607. // refresh till the bottom of the window
  1608. rect.height = size.y - rect.y;
  1609. RefreshRect( rect );
  1610. }
  1611. else // !report
  1612. {
  1613. // TODO: how to do it more efficiently?
  1614. m_dirty = true;
  1615. }
  1616. }
  1617. void wxListMainWindow::RefreshSelected()
  1618. {
  1619. if ( IsEmpty() )
  1620. return;
  1621. size_t from, to;
  1622. if ( InReportView() )
  1623. {
  1624. GetVisibleLinesRange(&from, &to);
  1625. }
  1626. else // !virtual
  1627. {
  1628. from = 0;
  1629. to = GetItemCount() - 1;
  1630. }
  1631. if ( HasCurrent() && m_current >= from && m_current <= to )
  1632. RefreshLine(m_current);
  1633. for ( size_t line = from; line <= to; line++ )
  1634. {
  1635. // NB: the test works as expected even if m_current == -1
  1636. if ( line != m_current && IsHighlighted(line) )
  1637. RefreshLine(line);
  1638. }
  1639. }
  1640. void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
  1641. {
  1642. // Note: a wxPaintDC must be constructed even if no drawing is
  1643. // done (a Windows requirement).
  1644. wxPaintDC dc( this );
  1645. if ( IsEmpty() )
  1646. {
  1647. // nothing to draw or not the moment to draw it
  1648. return;
  1649. }
  1650. if ( m_dirty )
  1651. RecalculatePositions( false );
  1652. GetListCtrl()->PrepareDC( dc );
  1653. int dev_x, dev_y;
  1654. GetListCtrl()->CalcScrolledPosition( 0, 0, &dev_x, &dev_y );
  1655. dc.SetFont( GetFont() );
  1656. if ( InReportView() )
  1657. {
  1658. int lineHeight = GetLineHeight();
  1659. size_t visibleFrom, visibleTo;
  1660. GetVisibleLinesRange(&visibleFrom, &visibleTo);
  1661. wxRect rectLine;
  1662. int xOrig = dc.LogicalToDeviceX( 0 );
  1663. int yOrig = dc.LogicalToDeviceY( 0 );
  1664. // tell the caller cache to cache the data
  1665. if ( IsVirtual() )
  1666. {
  1667. wxListEvent evCache(wxEVT_LIST_CACHE_HINT,
  1668. GetParent()->GetId());
  1669. evCache.SetEventObject( GetParent() );
  1670. evCache.m_oldItemIndex = visibleFrom;
  1671. evCache.m_item.m_itemId =
  1672. evCache.m_itemIndex = visibleTo;
  1673. GetParent()->GetEventHandler()->ProcessEvent( evCache );
  1674. }
  1675. for ( size_t line = visibleFrom; line <= visibleTo; line++ )
  1676. {
  1677. rectLine = GetLineRect(line);
  1678. if ( !IsExposed(rectLine.x + xOrig, rectLine.y + yOrig,
  1679. rectLine.width, rectLine.height) )
  1680. {
  1681. // don't redraw unaffected lines to avoid flicker
  1682. continue;
  1683. }
  1684. GetLine(line)->DrawInReportMode( &dc,
  1685. rectLine,
  1686. GetLineHighlightRect(line),
  1687. IsHighlighted(line),
  1688. line == m_current );
  1689. }
  1690. if ( HasFlag(wxLC_HRULES) )
  1691. {
  1692. wxPen pen(GetRuleColour(), 1, wxPENSTYLE_SOLID);
  1693. wxSize clientSize = GetClientSize();
  1694. size_t i = visibleFrom;
  1695. if (i == 0) i = 1; // Don't draw the first one
  1696. for ( ; i <= visibleTo; i++ )
  1697. {
  1698. dc.SetPen(pen);
  1699. dc.SetBrush( *wxTRANSPARENT_BRUSH );
  1700. dc.DrawLine(0 - dev_x, i * lineHeight,
  1701. clientSize.x - dev_x, i * lineHeight);
  1702. }
  1703. // Draw last horizontal rule
  1704. if ( visibleTo == GetItemCount() - 1 )
  1705. {
  1706. dc.SetPen( pen );
  1707. dc.SetBrush( *wxTRANSPARENT_BRUSH );
  1708. dc.DrawLine(0 - dev_x, (m_lineTo + 1) * lineHeight,
  1709. clientSize.x - dev_x , (m_lineTo + 1) * lineHeight );
  1710. }
  1711. }
  1712. // Draw vertical rules if required
  1713. if ( HasFlag(wxLC_VRULES) && !IsEmpty() )
  1714. {
  1715. wxPen pen(GetRuleColour(), 1, wxPENSTYLE_SOLID);
  1716. wxRect firstItemRect, lastItemRect;
  1717. GetItemRect(visibleFrom, firstItemRect);
  1718. GetItemRect(visibleTo, lastItemRect);
  1719. int x = firstItemRect.GetX();
  1720. dc.SetPen(pen);
  1721. dc.SetBrush(* wxTRANSPARENT_BRUSH);
  1722. for (int col = 0; col < GetColumnCount(); col++)
  1723. {
  1724. int colWidth = GetColumnWidth(col);
  1725. x += colWidth;
  1726. int x_pos = x - dev_x;
  1727. if (col < GetColumnCount()-1) x_pos -= 2;
  1728. dc.DrawLine(x_pos, firstItemRect.GetY() - 1 - dev_y,
  1729. x_pos, lastItemRect.GetBottom() + 1 - dev_y);
  1730. }
  1731. }
  1732. }
  1733. else // !report
  1734. {
  1735. size_t count = GetItemCount();
  1736. for ( size_t i = 0; i < count; i++ )
  1737. {
  1738. GetLine(i)->Draw( &dc, i == m_current );
  1739. }
  1740. }
  1741. // DrawFocusRect() is unusable under Mac, it draws outside of the highlight
  1742. // rectangle somehow and so leaves traces when the item is not selected any
  1743. // more, see #12229.
  1744. #ifndef __WXMAC__
  1745. if ( HasCurrent() )
  1746. {
  1747. int flags = 0;
  1748. if ( IsHighlighted(m_current) )
  1749. flags |= wxCONTROL_SELECTED;
  1750. wxRendererNative::Get().
  1751. DrawFocusRect(this, dc, GetLineHighlightRect(m_current), flags);
  1752. }
  1753. #endif // !__WXMAC__
  1754. }
  1755. void wxListMainWindow::HighlightAll( bool on )
  1756. {
  1757. if ( IsSingleSel() )
  1758. {
  1759. wxASSERT_MSG( !on, wxT("can't do this in a single selection control") );
  1760. // we just have one item to turn off
  1761. if ( HasCurrent() && IsHighlighted(m_current) )
  1762. {
  1763. HighlightLine(m_current, false);
  1764. RefreshLine(m_current);
  1765. }
  1766. }
  1767. else // multi selection
  1768. {
  1769. if ( !IsEmpty() )
  1770. HighlightLines(0, GetItemCount() - 1, on);
  1771. }
  1772. }
  1773. void wxListMainWindow::OnChildFocus(wxChildFocusEvent& WXUNUSED(event))
  1774. {
  1775. // Do nothing here. This prevents the default handler in wxScrolledWindow
  1776. // from needlessly scrolling the window when the edit control is
  1777. // dismissed. See ticket #9563.
  1778. }
  1779. void wxListMainWindow::SendNotify( size_t line,
  1780. wxEventType command,
  1781. const wxPoint& point )
  1782. {
  1783. wxListEvent le( command, GetParent()->GetId() );
  1784. le.SetEventObject( GetParent() );
  1785. le.m_item.m_itemId =
  1786. le.m_itemIndex = line;
  1787. // set only for events which have position
  1788. if ( point != wxDefaultPosition )
  1789. le.m_pointDrag = point;
  1790. // don't try to get the line info for virtual list controls: the main
  1791. // program has it anyhow and if we did it would result in accessing all
  1792. // the lines, even those which are not visible now and this is precisely
  1793. // what we're trying to avoid
  1794. if ( !IsVirtual() )
  1795. {
  1796. if ( line != (size_t)-1 )
  1797. {
  1798. GetLine(line)->GetItem( 0, le.m_item );
  1799. }
  1800. //else: this happens for wxEVT_LIST_ITEM_FOCUSED event
  1801. }
  1802. //else: there may be no more such item
  1803. GetParent()->GetEventHandler()->ProcessEvent( le );
  1804. }
  1805. void wxListMainWindow::ChangeCurrent(size_t current)
  1806. {
  1807. m_current = current;
  1808. // as the current item changed, we shouldn't start editing it when the
  1809. // "slow click" timer expires as the click happened on another item
  1810. if ( m_renameTimer->IsRunning() )
  1811. m_renameTimer->Stop();
  1812. SendNotify(current, wxEVT_LIST_ITEM_FOCUSED);
  1813. }
  1814. wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass)
  1815. {
  1816. wxCHECK_MSG( (item >= 0) && ((size_t)item < GetItemCount()), NULL,
  1817. wxT("wrong index in wxGenericListCtrl::EditLabel()") );
  1818. wxASSERT_MSG( textControlClass->IsKindOf(wxCLASSINFO(wxTextCtrl)),
  1819. wxT("EditLabel() needs a text control") );
  1820. size_t itemEdit = (size_t)item;
  1821. wxListEvent le( wxEVT_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() );
  1822. le.SetEventObject( GetParent() );
  1823. le.m_item.m_itemId =
  1824. le.m_itemIndex = item;
  1825. wxListLineData *data = GetLine(itemEdit);
  1826. wxCHECK_MSG( data, NULL, wxT("invalid index in EditLabel()") );
  1827. data->GetItem( 0, le.m_item );
  1828. if ( GetParent()->GetEventHandler()->ProcessEvent( le ) && !le.IsAllowed() )
  1829. {
  1830. // vetoed by user code
  1831. return NULL;
  1832. }
  1833. if ( m_dirty )
  1834. {
  1835. // Ensure the display is updated before we start editing.
  1836. Update();
  1837. }
  1838. wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
  1839. m_textctrlWrapper = new wxListTextCtrlWrapper(this, text, item);
  1840. return m_textctrlWrapper->GetText();
  1841. }
  1842. void wxListMainWindow::OnRenameTimer()
  1843. {
  1844. wxCHECK_RET( HasCurrent(), wxT("unexpected rename timer") );
  1845. EditLabel( m_current );
  1846. }
  1847. bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value)
  1848. {
  1849. wxListEvent le( wxEVT_LIST_END_LABEL_EDIT, GetParent()->GetId() );
  1850. le.SetEventObject( GetParent() );
  1851. le.m_item.m_itemId =
  1852. le.m_itemIndex = itemEdit;
  1853. wxListLineData *data = GetLine(itemEdit);
  1854. wxCHECK_MSG( data, false, wxT("invalid index in OnRenameAccept()") );
  1855. data->GetItem( 0, le.m_item );
  1856. le.m_item.m_text = value;
  1857. return !GetParent()->GetEventHandler()->ProcessEvent( le ) ||
  1858. le.IsAllowed();
  1859. }
  1860. void wxListMainWindow::OnRenameCancelled(size_t itemEdit)
  1861. {
  1862. // let owner know that the edit was cancelled
  1863. wxListEvent le( wxEVT_LIST_END_LABEL_EDIT, GetParent()->GetId() );
  1864. le.SetEditCanceled(true);
  1865. le.SetEventObject( GetParent() );
  1866. le.m_item.m_itemId =
  1867. le.m_itemIndex = itemEdit;
  1868. wxListLineData *data = GetLine(itemEdit);
  1869. wxCHECK_RET( data, wxT("invalid index in OnRenameCancelled()") );
  1870. data->GetItem( 0, le.m_item );
  1871. GetEventHandler()->ProcessEvent( le );
  1872. }
  1873. void wxListMainWindow::OnFindTimer()
  1874. {
  1875. m_findPrefix.clear();
  1876. if ( m_findBell )
  1877. m_findBell = 1;
  1878. }
  1879. void wxListMainWindow::EnableBellOnNoMatch( bool on )
  1880. {
  1881. m_findBell = on;
  1882. }
  1883. void wxListMainWindow::OnMouse( wxMouseEvent &event )
  1884. {
  1885. #ifdef __WXMAC__
  1886. // On wxMac we can't depend on the EVT_KILL_FOCUS event to properly
  1887. // shutdown the edit control when the mouse is clicked elsewhere on the
  1888. // listctrl because the order of events is different (or something like
  1889. // that), so explicitly end the edit if it is active.
  1890. if ( event.LeftDown() && m_textctrlWrapper )
  1891. m_textctrlWrapper->EndEdit(wxListTextCtrlWrapper::End_Accept);
  1892. #endif // __WXMAC__
  1893. if ( event.LeftDown() )
  1894. {
  1895. // Ensure we skip the event to let the system set focus to this window.
  1896. event.Skip();
  1897. }
  1898. // Pretend that the event happened in wxListCtrl itself.
  1899. wxMouseEvent me(event);
  1900. me.SetEventObject( GetParent() );
  1901. me.SetId(GetParent()->GetId());
  1902. if ( GetParent()->GetEventHandler()->ProcessEvent( me ))
  1903. return;
  1904. if (event.GetEventType() == wxEVT_MOUSEWHEEL)
  1905. {
  1906. // let the base class handle mouse wheel events.
  1907. event.Skip();
  1908. return;
  1909. }
  1910. if ( !HasCurrent() || IsEmpty() )
  1911. {
  1912. if (event.RightDown())
  1913. {
  1914. SendNotify( (size_t)-1, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
  1915. wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU,
  1916. GetParent()->GetId(),
  1917. ClientToScreen(event.GetPosition()));
  1918. evtCtx.SetEventObject(GetParent());
  1919. GetParent()->GetEventHandler()->ProcessEvent(evtCtx);
  1920. }
  1921. return;
  1922. }
  1923. if (m_dirty)
  1924. return;
  1925. if ( !(event.Dragging() || event.ButtonDown() || event.LeftUp() ||
  1926. event.ButtonDClick()) )
  1927. return;
  1928. int x = event.GetX();
  1929. int y = event.GetY();
  1930. GetListCtrl()->CalcUnscrolledPosition( x, y, &x, &y );
  1931. // where did we hit it (if we did)?
  1932. long hitResult = 0;
  1933. size_t count = GetItemCount(),
  1934. current;
  1935. if ( InReportView() )
  1936. {
  1937. current = y / GetLineHeight();
  1938. if ( current < count )
  1939. hitResult = HitTestLine(current, x, y);
  1940. }
  1941. else // !report
  1942. {
  1943. // TODO: optimize it too! this is less simple than for report view but
  1944. // enumerating all items is still not a way to do it!!
  1945. for ( current = 0; current < count; current++ )
  1946. {
  1947. hitResult = HitTestLine(current, x, y);
  1948. if ( hitResult )
  1949. break;
  1950. }
  1951. }
  1952. // Update drag events counter first as we must do it even if the mouse is
  1953. // not on any item right now as we must keep count in case we started
  1954. // dragging from the empty control area but continued to do it over a valid
  1955. // item -- in this situation we must not start dragging this item.
  1956. if (event.Dragging())
  1957. m_dragCount++;
  1958. else
  1959. m_dragCount = 0;
  1960. // The only mouse event that can be generated without any valid item is
  1961. // wxEVT_LIST_ITEM_RIGHT_CLICK as it can be useful to have a global
  1962. // popup menu for the list control itself which should be shown even when
  1963. // the user clicks outside of any item.
  1964. if ( !hitResult )
  1965. {
  1966. // outside of any item
  1967. if (event.RightDown())
  1968. {
  1969. SendNotify( (size_t) -1, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
  1970. wxContextMenuEvent evtCtx(
  1971. wxEVT_CONTEXT_MENU,
  1972. GetParent()->GetId(),
  1973. ClientToScreen(event.GetPosition()));
  1974. evtCtx.SetEventObject(GetParent());
  1975. GetParent()->GetEventHandler()->ProcessEvent(evtCtx);
  1976. }
  1977. else
  1978. {
  1979. // reset the selection and bail out
  1980. HighlightAll(false);
  1981. }
  1982. return;
  1983. }
  1984. if ( event.Dragging() )
  1985. {
  1986. if (m_dragCount == 1)
  1987. {
  1988. // we have to report the raw, physical coords as we want to be
  1989. // able to call HitTest(event.m_pointDrag) from the user code to
  1990. // get the item being dragged
  1991. m_dragStart = event.GetPosition();
  1992. }
  1993. if (m_dragCount != 3)
  1994. return;
  1995. int command = event.RightIsDown() ? wxEVT_LIST_BEGIN_RDRAG
  1996. : wxEVT_LIST_BEGIN_DRAG;
  1997. SendNotify( m_lineLastClicked, command, m_dragStart );
  1998. return;
  1999. }
  2000. bool forceClick = false;
  2001. if (event.ButtonDClick())
  2002. {
  2003. if ( m_renameTimer->IsRunning() )
  2004. m_renameTimer->Stop();
  2005. m_lastOnSame = false;
  2006. if ( current == m_lineLastClicked )
  2007. {
  2008. SendNotify( current, wxEVT_LIST_ITEM_ACTIVATED );
  2009. return;
  2010. }
  2011. else
  2012. {
  2013. // The first click was on another item, so don't interpret this as
  2014. // a double click, but as a simple click instead
  2015. forceClick = true;
  2016. }
  2017. }
  2018. if (event.LeftUp())
  2019. {
  2020. if (m_lineSelectSingleOnUp != (size_t)-1)
  2021. {
  2022. // select single line
  2023. HighlightAll( false );
  2024. ReverseHighlight(m_lineSelectSingleOnUp);
  2025. }
  2026. if (m_lastOnSame)
  2027. {
  2028. if ((current == m_current) &&
  2029. (hitResult == wxLIST_HITTEST_ONITEMLABEL) &&
  2030. HasFlag(wxLC_EDIT_LABELS) )
  2031. {
  2032. if ( !InReportView() ||
  2033. GetLineLabelRect(current).Contains(x, y) )
  2034. {
  2035. int dclick = wxSystemSettings::GetMetric(wxSYS_DCLICK_MSEC);
  2036. m_renameTimer->Start(dclick > 0 ? dclick : 250, true);
  2037. }
  2038. }
  2039. m_lastOnSame = false;
  2040. }
  2041. m_lineSelectSingleOnUp = (size_t)-1;
  2042. }
  2043. else
  2044. {
  2045. // This is necessary, because after a DnD operation in
  2046. // from and to ourself, the up event is swallowed by the
  2047. // DnD code. So on next non-up event (which means here and
  2048. // now) m_lineSelectSingleOnUp should be reset.
  2049. m_lineSelectSingleOnUp = (size_t)-1;
  2050. }
  2051. if (event.RightDown())
  2052. {
  2053. m_lineBeforeLastClicked = m_lineLastClicked;
  2054. m_lineLastClicked = current;
  2055. // If the item is already selected, do not update the selection.
  2056. // Multi-selections should not be cleared if a selected item is clicked.
  2057. if (!IsHighlighted(current))
  2058. {
  2059. HighlightAll(false);
  2060. ChangeCurrent(current);
  2061. ReverseHighlight(m_current);
  2062. }
  2063. SendNotify( current, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
  2064. // Allow generation of context menu event
  2065. event.Skip();
  2066. }
  2067. else if (event.MiddleDown())
  2068. {
  2069. SendNotify( current, wxEVT_LIST_ITEM_MIDDLE_CLICK );
  2070. }
  2071. else if ( event.LeftDown() || forceClick )
  2072. {
  2073. m_lineBeforeLastClicked = m_lineLastClicked;
  2074. m_lineLastClicked = current;
  2075. size_t oldCurrent = m_current;
  2076. bool oldWasSelected = IsHighlighted(m_current);
  2077. bool cmdModifierDown = event.CmdDown();
  2078. if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
  2079. {
  2080. if ( IsSingleSel() || !IsHighlighted(current) )
  2081. {
  2082. HighlightAll( false );
  2083. ChangeCurrent(current);
  2084. ReverseHighlight(m_current);
  2085. }
  2086. else // multi sel & current is highlighted & no mod keys
  2087. {
  2088. m_lineSelectSingleOnUp = current;
  2089. ChangeCurrent(current); // change focus
  2090. }
  2091. }
  2092. else // multi sel & either ctrl or shift is down
  2093. {
  2094. if (cmdModifierDown)
  2095. {
  2096. ChangeCurrent(current);
  2097. ReverseHighlight(m_current);
  2098. }
  2099. else if (event.ShiftDown())
  2100. {
  2101. ChangeCurrent(current);
  2102. size_t lineFrom = oldCurrent,
  2103. lineTo = current;
  2104. if ( lineTo < lineFrom )
  2105. {
  2106. lineTo = lineFrom;
  2107. lineFrom = m_current;
  2108. }
  2109. HighlightLines(lineFrom, lineTo);
  2110. }
  2111. else // !ctrl, !shift
  2112. {
  2113. // test in the enclosing if should make it impossible
  2114. wxFAIL_MSG( wxT("how did we get here?") );
  2115. }
  2116. }
  2117. if (m_current != oldCurrent)
  2118. RefreshLine( oldCurrent );
  2119. // Set the flag telling us whether the next click on this item should
  2120. // start editing its label. This should happen if we clicked on the
  2121. // current item and it was already selected, i.e. if this click was not
  2122. // done to select it.
  2123. //
  2124. // It should not happen if this was a double click (forceClick is true)
  2125. // nor if we hadn't had the focus before as then this click was used to
  2126. // give focus to the control.
  2127. m_lastOnSame = (m_current == oldCurrent) && oldWasSelected &&
  2128. !forceClick && HasFocus();
  2129. }
  2130. }
  2131. void wxListMainWindow::MoveToItem(size_t item)
  2132. {
  2133. if ( item == (size_t)-1 )
  2134. return;
  2135. wxRect rect = GetLineRect(item);
  2136. int client_w, client_h;
  2137. GetClientSize( &client_w, &client_h );
  2138. const int hLine = GetLineHeight();
  2139. int view_x = SCROLL_UNIT_X * GetListCtrl()->GetScrollPos( wxHORIZONTAL );
  2140. int view_y = hLine * GetListCtrl()->GetScrollPos( wxVERTICAL );
  2141. if ( InReportView() )
  2142. {
  2143. // the next we need the range of lines shown it might be different,
  2144. // so recalculate it
  2145. ResetVisibleLinesRange();
  2146. if (rect.y < view_y)
  2147. GetListCtrl()->Scroll( -1, rect.y / hLine );
  2148. if (rect.y + rect.height + 5 > view_y + client_h)
  2149. GetListCtrl()->Scroll( -1, (rect.y + rect.height - client_h + hLine) / hLine );
  2150. #ifdef __WXMAC__
  2151. // At least on Mac the visible lines value will get reset inside of
  2152. // Scroll *before* it actually scrolls the window because of the
  2153. // Update() that happens there, so it will still have the wrong value.
  2154. // So let's reset it again and wait for it to be recalculated in the
  2155. // next paint event. I would expect this problem to show up in wxGTK
  2156. // too but couldn't duplicate it there. Perhaps the order of events
  2157. // is different... --Robin
  2158. ResetVisibleLinesRange();
  2159. #endif
  2160. }
  2161. else // !report
  2162. {
  2163. int sx = -1,
  2164. sy = -1;
  2165. if (rect.x-view_x < 5)
  2166. sx = (rect.x - 5) / SCROLL_UNIT_X;
  2167. if (rect.x + rect.width - 5 > view_x + client_w)
  2168. sx = (rect.x + rect.width - client_w + SCROLL_UNIT_X) / SCROLL_UNIT_X;
  2169. if (rect.y-view_y < 5)
  2170. sy = (rect.y - 5) / hLine;
  2171. if (rect.y + rect.height - 5 > view_y + client_h)
  2172. sy = (rect.y + rect.height - client_h + hLine) / hLine;
  2173. GetListCtrl()->Scroll(sx, sy);
  2174. }
  2175. }
  2176. bool wxListMainWindow::ScrollList(int WXUNUSED(dx), int dy)
  2177. {
  2178. if ( !InReportView() )
  2179. {
  2180. // TODO: this should work in all views but is not implemented now
  2181. return false;
  2182. }
  2183. size_t top, bottom;
  2184. GetVisibleLinesRange(&top, &bottom);
  2185. if ( bottom == (size_t)-1 )
  2186. return 0;
  2187. ResetVisibleLinesRange();
  2188. int hLine = GetLineHeight();
  2189. GetListCtrl()->Scroll(-1, top + dy / hLine);
  2190. #ifdef __WXMAC__
  2191. // see comment in MoveToItem() for why we do this
  2192. ResetVisibleLinesRange();
  2193. #endif
  2194. return true;
  2195. }
  2196. // ----------------------------------------------------------------------------
  2197. // keyboard handling
  2198. // ----------------------------------------------------------------------------
  2199. void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event)
  2200. {
  2201. wxCHECK_RET( newCurrent < (size_t)GetItemCount(),
  2202. wxT("invalid item index in OnArrowChar()") );
  2203. size_t oldCurrent = m_current;
  2204. // in single selection we just ignore Shift as we can't select several
  2205. // items anyhow
  2206. if ( event.ShiftDown() && !IsSingleSel() )
  2207. {
  2208. ChangeCurrent(newCurrent);
  2209. // refresh the old focus to remove it
  2210. RefreshLine( oldCurrent );
  2211. // select all the items between the old and the new one
  2212. if ( oldCurrent > newCurrent )
  2213. {
  2214. newCurrent = oldCurrent;
  2215. oldCurrent = m_current;
  2216. }
  2217. HighlightLines(oldCurrent, newCurrent);
  2218. }
  2219. else // !shift
  2220. {
  2221. // all previously selected items are unselected unless ctrl is held
  2222. // in a multiselection control
  2223. if ( !event.ControlDown() || IsSingleSel() )
  2224. HighlightAll(false);
  2225. ChangeCurrent(newCurrent);
  2226. // refresh the old focus to remove it
  2227. RefreshLine( oldCurrent );
  2228. // in single selection mode we must always have a selected item
  2229. if ( !event.ControlDown() || IsSingleSel() )
  2230. HighlightLine( m_current, true );
  2231. }
  2232. RefreshLine( m_current );
  2233. MoveToFocus();
  2234. }
  2235. void wxListMainWindow::OnKeyDown( wxKeyEvent &event )
  2236. {
  2237. wxWindow *parent = GetParent();
  2238. // propagate the key event upwards
  2239. wxKeyEvent ke(event);
  2240. ke.SetEventObject( parent );
  2241. ke.SetId(GetParent()->GetId());
  2242. if (parent->GetEventHandler()->ProcessEvent( ke ))
  2243. return;
  2244. // send a list event
  2245. wxListEvent le( wxEVT_LIST_KEY_DOWN, parent->GetId() );
  2246. le.m_item.m_itemId =
  2247. le.m_itemIndex = m_current;
  2248. if (HasCurrent())
  2249. GetLine(m_current)->GetItem( 0, le.m_item );
  2250. le.m_code = event.GetKeyCode();
  2251. le.SetEventObject( parent );
  2252. if (parent->GetEventHandler()->ProcessEvent( le ))
  2253. return;
  2254. event.Skip();
  2255. }
  2256. void wxListMainWindow::OnKeyUp( wxKeyEvent &event )
  2257. {
  2258. wxWindow *parent = GetParent();
  2259. // propagate the key event upwards
  2260. wxKeyEvent ke(event);
  2261. ke.SetEventObject( parent );
  2262. ke.SetId(GetParent()->GetId());
  2263. if (parent->GetEventHandler()->ProcessEvent( ke ))
  2264. return;
  2265. event.Skip();
  2266. }
  2267. void wxListMainWindow::OnCharHook( wxKeyEvent &event )
  2268. {
  2269. if ( m_textctrlWrapper )
  2270. {
  2271. // When an in-place editor is active we should ensure that it always
  2272. // gets the key events that are special to it.
  2273. if ( m_textctrlWrapper->CheckForEndEditKey(event) )
  2274. {
  2275. // Skip the call to wxEvent::Skip() below.
  2276. return;
  2277. }
  2278. }
  2279. event.Skip();
  2280. }
  2281. void wxListMainWindow::OnChar( wxKeyEvent &event )
  2282. {
  2283. wxWindow *parent = GetParent();
  2284. // propagate the char event upwards
  2285. wxKeyEvent ke(event);
  2286. ke.SetEventObject( parent );
  2287. ke.SetId(GetParent()->GetId());
  2288. if (parent->GetEventHandler()->ProcessEvent( ke ))
  2289. return;
  2290. if ( HandleAsNavigationKey(event) )
  2291. return;
  2292. // no item -> nothing to do
  2293. if (!HasCurrent())
  2294. {
  2295. event.Skip();
  2296. return;
  2297. }
  2298. // don't use m_linesPerPage directly as it might not be computed yet
  2299. const int pageSize = GetCountPerPage();
  2300. wxCHECK_RET( pageSize, wxT("should have non zero page size") );
  2301. if (GetLayoutDirection() == wxLayout_RightToLeft)
  2302. {
  2303. if (event.GetKeyCode() == WXK_RIGHT)
  2304. event.m_keyCode = WXK_LEFT;
  2305. else if (event.GetKeyCode() == WXK_LEFT)
  2306. event.m_keyCode = WXK_RIGHT;
  2307. }
  2308. int keyCode = event.GetKeyCode();
  2309. switch ( keyCode )
  2310. {
  2311. case WXK_UP:
  2312. if ( m_current > 0 )
  2313. OnArrowChar( m_current - 1, event );
  2314. break;
  2315. case WXK_DOWN:
  2316. if ( m_current < (size_t)GetItemCount() - 1 )
  2317. OnArrowChar( m_current + 1, event );
  2318. break;
  2319. case WXK_END:
  2320. if (!IsEmpty())
  2321. OnArrowChar( GetItemCount() - 1, event );
  2322. break;
  2323. case WXK_HOME:
  2324. if (!IsEmpty())
  2325. OnArrowChar( 0, event );
  2326. break;
  2327. case WXK_PAGEUP:
  2328. {
  2329. int steps = InReportView() ? pageSize - 1
  2330. : m_current % pageSize;
  2331. int index = m_current - steps;
  2332. if (index < 0)
  2333. index = 0;
  2334. OnArrowChar( index, event );
  2335. }
  2336. break;
  2337. case WXK_PAGEDOWN:
  2338. {
  2339. int steps = InReportView()
  2340. ? pageSize - 1
  2341. : pageSize - (m_current % pageSize) - 1;
  2342. size_t index = m_current + steps;
  2343. size_t count = GetItemCount();
  2344. if ( index >= count )
  2345. index = count - 1;
  2346. OnArrowChar( index, event );
  2347. }
  2348. break;
  2349. case WXK_LEFT:
  2350. if ( !InReportView() )
  2351. {
  2352. int index = m_current - pageSize;
  2353. if (index < 0)
  2354. index = 0;
  2355. OnArrowChar( index, event );
  2356. }
  2357. break;
  2358. case WXK_RIGHT:
  2359. if ( !InReportView() )
  2360. {
  2361. size_t index = m_current + pageSize;
  2362. size_t count = GetItemCount();
  2363. if ( index >= count )
  2364. index = count - 1;
  2365. OnArrowChar( index, event );
  2366. }
  2367. break;
  2368. case WXK_SPACE:
  2369. if ( IsSingleSel() )
  2370. {
  2371. if ( event.ControlDown() )
  2372. {
  2373. ReverseHighlight(m_current);
  2374. }
  2375. else // normal space press
  2376. {
  2377. SendNotify( m_current, wxEVT_LIST_ITEM_ACTIVATED );
  2378. }
  2379. }
  2380. else // multiple selection
  2381. {
  2382. ReverseHighlight(m_current);
  2383. }
  2384. break;
  2385. case WXK_RETURN:
  2386. case WXK_EXECUTE:
  2387. SendNotify( m_current, wxEVT_LIST_ITEM_ACTIVATED );
  2388. break;
  2389. default:
  2390. if ( !event.HasModifiers() &&
  2391. ((keyCode >= '0' && keyCode <= '9') ||
  2392. (keyCode >= 'a' && keyCode <= 'z') ||
  2393. (keyCode >= 'A' && keyCode <= 'Z') ||
  2394. (keyCode == '_') ||
  2395. (keyCode == '+') ||
  2396. (keyCode == '*') ||
  2397. (keyCode == '-')))
  2398. {
  2399. // find the next item starting with the given prefix
  2400. wxChar ch = (wxChar)keyCode;
  2401. size_t item;
  2402. // if the same character is typed multiple times then go to the
  2403. // next entry starting with that character instead of searching
  2404. // for an item starting with multiple copies of this character,
  2405. // this is more useful and is how it works under Windows.
  2406. if ( m_findPrefix.length() == 1 && m_findPrefix[0] == ch )
  2407. {
  2408. item = PrefixFindItem(m_current, ch);
  2409. }
  2410. else
  2411. {
  2412. const wxString newPrefix(m_findPrefix + ch);
  2413. item = PrefixFindItem(m_current, newPrefix);
  2414. if ( item != (size_t)-1 )
  2415. m_findPrefix = newPrefix;
  2416. }
  2417. // also start the timer to reset the current prefix if the user
  2418. // doesn't press any more alnum keys soon -- we wouldn't want
  2419. // to use this prefix for a new item search
  2420. if ( !m_findTimer )
  2421. {
  2422. m_findTimer = new wxListFindTimer( this );
  2423. }
  2424. // Notice that we should start the timer even if we didn't find
  2425. // anything to make sure we reset the search state later.
  2426. m_findTimer->Start(wxListFindTimer::DELAY, wxTIMER_ONE_SHOT);
  2427. // restart timer even when there's no match so bell get's reset
  2428. if ( item != (size_t)-1 )
  2429. {
  2430. // Select the found item and go to it.
  2431. HighlightAll(false);
  2432. SetItemState(item,
  2433. wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED,
  2434. wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
  2435. // Reset the bell flag if it had been temporarily disabled
  2436. // before.
  2437. if ( m_findBell )
  2438. m_findBell = 1;
  2439. }
  2440. else // No such item
  2441. {
  2442. // Signal it with a bell if enabled.
  2443. if ( m_findBell == 1 )
  2444. {
  2445. ::wxBell();
  2446. // Disable it for the next unsuccessful match, we only
  2447. // beep once, this is usually enough and continuing to
  2448. // do it would be annoying.
  2449. m_findBell = -1;
  2450. }
  2451. }
  2452. }
  2453. else
  2454. {
  2455. event.Skip();
  2456. }
  2457. }
  2458. }
  2459. // ----------------------------------------------------------------------------
  2460. // focus handling
  2461. // ----------------------------------------------------------------------------
  2462. void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
  2463. {
  2464. if ( GetParent() )
  2465. {
  2466. wxFocusEvent event( wxEVT_SET_FOCUS, GetParent()->GetId() );
  2467. event.SetEventObject( GetParent() );
  2468. if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
  2469. return;
  2470. }
  2471. // wxGTK sends us EVT_SET_FOCUS events even if we had never got
  2472. // EVT_KILL_FOCUS before which means that we finish by redrawing the items
  2473. // which are already drawn correctly resulting in horrible flicker - avoid
  2474. // it
  2475. if ( !m_hasFocus )
  2476. {
  2477. m_hasFocus = true;
  2478. RefreshSelected();
  2479. }
  2480. }
  2481. void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
  2482. {
  2483. if ( GetParent() )
  2484. {
  2485. wxFocusEvent event( wxEVT_KILL_FOCUS, GetParent()->GetId() );
  2486. event.SetEventObject( GetParent() );
  2487. if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
  2488. return;
  2489. }
  2490. m_hasFocus = false;
  2491. RefreshSelected();
  2492. }
  2493. void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y )
  2494. {
  2495. if ( HasFlag(wxLC_ICON) && (m_normal_image_list))
  2496. {
  2497. m_normal_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
  2498. }
  2499. else if ( HasFlag(wxLC_SMALL_ICON) && (m_small_image_list))
  2500. {
  2501. m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
  2502. }
  2503. else if ( HasFlag(wxLC_LIST) && (m_small_image_list))
  2504. {
  2505. m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
  2506. }
  2507. else if ( InReportView() && (m_small_image_list))
  2508. {
  2509. m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT );
  2510. }
  2511. }
  2512. void wxListMainWindow::GetImageSize( int index, int &width, int &height ) const
  2513. {
  2514. if ( HasFlag(wxLC_ICON) && m_normal_image_list )
  2515. {
  2516. m_normal_image_list->GetSize( index, width, height );
  2517. }
  2518. else if ( HasFlag(wxLC_SMALL_ICON) && m_small_image_list )
  2519. {
  2520. m_small_image_list->GetSize( index, width, height );
  2521. }
  2522. else if ( HasFlag(wxLC_LIST) && m_small_image_list )
  2523. {
  2524. m_small_image_list->GetSize( index, width, height );
  2525. }
  2526. else if ( InReportView() && m_small_image_list )
  2527. {
  2528. m_small_image_list->GetSize( index, width, height );
  2529. }
  2530. else
  2531. {
  2532. width =
  2533. height = 0;
  2534. }
  2535. }
  2536. void wxListMainWindow::SetImageList( wxImageList *imageList, int which )
  2537. {
  2538. m_dirty = true;
  2539. // calc the spacing from the icon size
  2540. int width = 0, height = 0;
  2541. if ((imageList) && (imageList->GetImageCount()) )
  2542. imageList->GetSize(0, width, height);
  2543. if (which == wxIMAGE_LIST_NORMAL)
  2544. {
  2545. m_normal_image_list = imageList;
  2546. m_normal_spacing = width + 8;
  2547. }
  2548. if (which == wxIMAGE_LIST_SMALL)
  2549. {
  2550. m_small_image_list = imageList;
  2551. m_small_spacing = width + 14;
  2552. m_lineHeight = 0; // ensure that the line height will be recalc'd
  2553. }
  2554. }
  2555. void wxListMainWindow::SetItemSpacing( int spacing, bool isSmall )
  2556. {
  2557. m_dirty = true;
  2558. if (isSmall)
  2559. m_small_spacing = spacing;
  2560. else
  2561. m_normal_spacing = spacing;
  2562. }
  2563. int wxListMainWindow::GetItemSpacing( bool isSmall )
  2564. {
  2565. return isSmall ? m_small_spacing : m_normal_spacing;
  2566. }
  2567. // ----------------------------------------------------------------------------
  2568. // columns
  2569. // ----------------------------------------------------------------------------
  2570. int
  2571. wxListMainWindow::ComputeMinHeaderWidth(const wxListHeaderData* column) const
  2572. {
  2573. wxClientDC dc(const_cast<wxListMainWindow*>(this));
  2574. int width = dc.GetTextExtent(column->GetText()).x + AUTOSIZE_COL_MARGIN;
  2575. width += 2*EXTRA_WIDTH;
  2576. // check for column header's image availability
  2577. const int image = column->GetImage();
  2578. if ( image != -1 )
  2579. {
  2580. if ( m_small_image_list )
  2581. {
  2582. int ix = 0, iy = 0;
  2583. m_small_image_list->GetSize(image, ix, iy);
  2584. width += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE;
  2585. }
  2586. }
  2587. return width;
  2588. }
  2589. void wxListMainWindow::SetColumn( int col, const wxListItem &item )
  2590. {
  2591. wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
  2592. wxCHECK_RET( node, wxT("invalid column index in SetColumn") );
  2593. wxListHeaderData *column = node->GetData();
  2594. column->SetItem( item );
  2595. if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER )
  2596. column->SetWidth(ComputeMinHeaderWidth(column));
  2597. wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
  2598. if ( headerWin )
  2599. headerWin->m_dirty = true;
  2600. m_dirty = true;
  2601. // invalidate it as it has to be recalculated
  2602. m_headerWidth = 0;
  2603. }
  2604. void wxListMainWindow::SetColumnWidth( int col, int width )
  2605. {
  2606. wxCHECK_RET( col >= 0 && col < GetColumnCount(),
  2607. wxT("invalid column index") );
  2608. wxCHECK_RET( InReportView(),
  2609. wxT("SetColumnWidth() can only be called in report mode.") );
  2610. m_dirty = true;
  2611. wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
  2612. if ( headerWin )
  2613. headerWin->m_dirty = true;
  2614. wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
  2615. wxCHECK_RET( node, wxT("no column?") );
  2616. wxListHeaderData *column = node->GetData();
  2617. size_t count = GetItemCount();
  2618. if (width == wxLIST_AUTOSIZE_USEHEADER)
  2619. {
  2620. width = ComputeMinHeaderWidth(column);
  2621. }
  2622. else if ( width == wxLIST_AUTOSIZE )
  2623. {
  2624. width = ComputeMinHeaderWidth(column);
  2625. if ( !IsVirtual() )
  2626. {
  2627. wxClientDC dc(this);
  2628. dc.SetFont( GetFont() );
  2629. int max = AUTOSIZE_COL_MARGIN;
  2630. // if the cached column width isn't valid then recalculate it
  2631. if (m_aColWidths.Item(col)->bNeedsUpdate)
  2632. {
  2633. for (size_t i = 0; i < count; i++)
  2634. {
  2635. wxListLineData *line = GetLine( i );
  2636. wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
  2637. wxCHECK_RET( n, wxT("no subitem?") );
  2638. wxListItemData *itemData = n->GetData();
  2639. wxListItem item;
  2640. itemData->GetItem(item);
  2641. int itemWidth = GetItemWidthWithImage(&item);
  2642. if (itemWidth > max)
  2643. max = itemWidth;
  2644. }
  2645. m_aColWidths.Item(col)->bNeedsUpdate = false;
  2646. m_aColWidths.Item(col)->nMaxWidth = max;
  2647. }
  2648. max = m_aColWidths.Item(col)->nMaxWidth + AUTOSIZE_COL_MARGIN;
  2649. if ( width < max )
  2650. width = max;
  2651. }
  2652. }
  2653. column->SetWidth( width );
  2654. // invalidate it as it has to be recalculated
  2655. m_headerWidth = 0;
  2656. }
  2657. int wxListMainWindow::GetHeaderWidth() const
  2658. {
  2659. if ( !m_headerWidth )
  2660. {
  2661. wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
  2662. size_t count = GetColumnCount();
  2663. for ( size_t col = 0; col < count; col++ )
  2664. {
  2665. self->m_headerWidth += GetColumnWidth(col);
  2666. }
  2667. }
  2668. return m_headerWidth;
  2669. }
  2670. void wxListMainWindow::GetColumn( int col, wxListItem &item ) const
  2671. {
  2672. wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
  2673. wxCHECK_RET( node, wxT("invalid column index in GetColumn") );
  2674. wxListHeaderData *column = node->GetData();
  2675. column->GetItem( item );
  2676. }
  2677. int wxListMainWindow::GetColumnWidth( int col ) const
  2678. {
  2679. wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
  2680. wxCHECK_MSG( node, 0, wxT("invalid column index") );
  2681. wxListHeaderData *column = node->GetData();
  2682. return column->GetWidth();
  2683. }
  2684. // ----------------------------------------------------------------------------
  2685. // item state
  2686. // ----------------------------------------------------------------------------
  2687. void wxListMainWindow::SetItem( wxListItem &item )
  2688. {
  2689. long id = item.m_itemId;
  2690. wxCHECK_RET( id >= 0 && (size_t)id < GetItemCount(),
  2691. wxT("invalid item index in SetItem") );
  2692. if ( !IsVirtual() )
  2693. {
  2694. wxListLineData *line = GetLine((size_t)id);
  2695. line->SetItem( item.m_col, item );
  2696. // Set item state if user wants
  2697. if ( item.m_mask & wxLIST_MASK_STATE )
  2698. SetItemState( item.m_itemId, item.m_state, item.m_state );
  2699. if (InReportView())
  2700. {
  2701. // update the Max Width Cache if needed
  2702. int width = GetItemWidthWithImage(&item);
  2703. if (width > m_aColWidths.Item(item.m_col)->nMaxWidth)
  2704. m_aColWidths.Item(item.m_col)->nMaxWidth = width;
  2705. }
  2706. }
  2707. // update the item on screen unless we're going to update everything soon
  2708. // anyhow
  2709. if ( !m_dirty )
  2710. {
  2711. wxRect rectItem;
  2712. GetItemRect(id, rectItem);
  2713. RefreshRect(rectItem);
  2714. }
  2715. }
  2716. void wxListMainWindow::SetItemStateAll(long state, long stateMask)
  2717. {
  2718. if ( IsEmpty() )
  2719. return;
  2720. // first deal with selection
  2721. if ( stateMask & wxLIST_STATE_SELECTED )
  2722. {
  2723. // set/clear select state
  2724. if ( IsVirtual() )
  2725. {
  2726. // optimized version for virtual listctrl.
  2727. m_selStore.SelectRange(0, GetItemCount() - 1, state == wxLIST_STATE_SELECTED);
  2728. Refresh();
  2729. }
  2730. else if ( state & wxLIST_STATE_SELECTED )
  2731. {
  2732. const long count = GetItemCount();
  2733. for( long i = 0; i < count; i++ )
  2734. {
  2735. SetItemState( i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
  2736. }
  2737. }
  2738. else
  2739. {
  2740. // clear for non virtual (somewhat optimized by using GetNextItem())
  2741. long i = -1;
  2742. while ( (i = GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1 )
  2743. {
  2744. SetItemState( i, 0, wxLIST_STATE_SELECTED );
  2745. }
  2746. }
  2747. }
  2748. if ( HasCurrent() && (state == 0) && (stateMask & wxLIST_STATE_FOCUSED) )
  2749. {
  2750. // unfocus all: only one item can be focussed, so clearing focus for
  2751. // all items is simply clearing focus of the focussed item.
  2752. SetItemState(m_current, state, stateMask);
  2753. }
  2754. //(setting focus to all items makes no sense, so it is not handled here.)
  2755. }
  2756. void wxListMainWindow::SetItemState( long litem, long state, long stateMask )
  2757. {
  2758. if ( litem == -1 )
  2759. {
  2760. SetItemStateAll(state, stateMask);
  2761. return;
  2762. }
  2763. wxCHECK_RET( litem >= 0 && (size_t)litem < GetItemCount(),
  2764. wxT("invalid list ctrl item index in SetItem") );
  2765. size_t oldCurrent = m_current;
  2766. size_t item = (size_t)litem; // safe because of the check above
  2767. // do we need to change the focus?
  2768. if ( stateMask & wxLIST_STATE_FOCUSED )
  2769. {
  2770. if ( state & wxLIST_STATE_FOCUSED )
  2771. {
  2772. // don't do anything if this item is already focused
  2773. if ( item != m_current )
  2774. {
  2775. ChangeCurrent(item);
  2776. if ( oldCurrent != (size_t)-1 )
  2777. {
  2778. if ( IsSingleSel() )
  2779. {
  2780. HighlightLine(oldCurrent, false);
  2781. }
  2782. RefreshLine(oldCurrent);
  2783. }
  2784. RefreshLine( m_current );
  2785. }
  2786. }
  2787. else // unfocus
  2788. {
  2789. // don't do anything if this item is not focused
  2790. if ( item == m_current )
  2791. {
  2792. ResetCurrent();
  2793. if ( IsSingleSel() )
  2794. {
  2795. // we must unselect the old current item as well or we
  2796. // might end up with more than one selected item in a
  2797. // single selection control
  2798. HighlightLine(oldCurrent, false);
  2799. }
  2800. RefreshLine( oldCurrent );
  2801. }
  2802. }
  2803. }
  2804. // do we need to change the selection state?
  2805. if ( stateMask & wxLIST_STATE_SELECTED )
  2806. {
  2807. bool on = (state & wxLIST_STATE_SELECTED) != 0;
  2808. if ( IsSingleSel() )
  2809. {
  2810. if ( on )
  2811. {
  2812. // selecting the item also makes it the focused one in the
  2813. // single sel mode
  2814. if ( m_current != item )
  2815. {
  2816. ChangeCurrent(item);
  2817. if ( oldCurrent != (size_t)-1 )
  2818. {
  2819. HighlightLine( oldCurrent, false );
  2820. RefreshLine( oldCurrent );
  2821. }
  2822. }
  2823. }
  2824. else // off
  2825. {
  2826. // only the current item may be selected anyhow
  2827. if ( item != m_current )
  2828. return;
  2829. }
  2830. }
  2831. if ( HighlightLine(item, on) )
  2832. {
  2833. RefreshLine(item);
  2834. }
  2835. }
  2836. }
  2837. int wxListMainWindow::GetItemState( long item, long stateMask ) const
  2838. {
  2839. wxCHECK_MSG( item >= 0 && (size_t)item < GetItemCount(), 0,
  2840. wxT("invalid list ctrl item index in GetItemState()") );
  2841. int ret = wxLIST_STATE_DONTCARE;
  2842. if ( stateMask & wxLIST_STATE_FOCUSED )
  2843. {
  2844. if ( (size_t)item == m_current )
  2845. ret |= wxLIST_STATE_FOCUSED;
  2846. }
  2847. if ( stateMask & wxLIST_STATE_SELECTED )
  2848. {
  2849. if ( IsHighlighted(item) )
  2850. ret |= wxLIST_STATE_SELECTED;
  2851. }
  2852. return ret;
  2853. }
  2854. void wxListMainWindow::GetItem( wxListItem &item ) const
  2855. {
  2856. wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId < GetItemCount(),
  2857. wxT("invalid item index in GetItem") );
  2858. wxListLineData *line = GetLine((size_t)item.m_itemId);
  2859. line->GetItem( item.m_col, item );
  2860. // Get item state if user wants it
  2861. if ( item.m_mask & wxLIST_MASK_STATE )
  2862. item.m_state = GetItemState( item.m_itemId, wxLIST_STATE_SELECTED |
  2863. wxLIST_STATE_FOCUSED );
  2864. }
  2865. // ----------------------------------------------------------------------------
  2866. // item count
  2867. // ----------------------------------------------------------------------------
  2868. size_t wxListMainWindow::GetItemCount() const
  2869. {
  2870. return IsVirtual() ? m_countVirt : m_lines.GetCount();
  2871. }
  2872. void wxListMainWindow::SetItemCount(long count)
  2873. {
  2874. // Update the current item if it's not valid any longer (notice that this
  2875. // invalidates it completely if the control is becoming empty, which is the
  2876. // right thing to do).
  2877. if ( HasCurrent() && m_current >= (size_t)count )
  2878. ChangeCurrent(count - 1);
  2879. m_selStore.SetItemCount(count);
  2880. m_countVirt = count;
  2881. ResetVisibleLinesRange();
  2882. // scrollbars must be reset
  2883. m_dirty = true;
  2884. }
  2885. int wxListMainWindow::GetSelectedItemCount() const
  2886. {
  2887. // deal with the quick case first
  2888. if ( IsSingleSel() )
  2889. return HasCurrent() ? IsHighlighted(m_current) : false;
  2890. // virtual controls remmebers all its selections itself
  2891. if ( IsVirtual() )
  2892. return m_selStore.GetSelectedCount();
  2893. // TODO: we probably should maintain the number of items selected even for
  2894. // non virtual controls as enumerating all lines is really slow...
  2895. size_t countSel = 0;
  2896. size_t count = GetItemCount();
  2897. for ( size_t line = 0; line < count; line++ )
  2898. {
  2899. if ( GetLine(line)->IsHighlighted() )
  2900. countSel++;
  2901. }
  2902. return countSel;
  2903. }
  2904. // ----------------------------------------------------------------------------
  2905. // item position/size
  2906. // ----------------------------------------------------------------------------
  2907. wxRect wxListMainWindow::GetViewRect() const
  2908. {
  2909. wxASSERT_MSG( !HasFlag(wxLC_LIST), "not implemented for list view" );
  2910. // we need to find the longest/tallest label
  2911. wxCoord xMax = 0, yMax = 0;
  2912. const int count = GetItemCount();
  2913. if ( count )
  2914. {
  2915. for ( int i = 0; i < count; i++ )
  2916. {
  2917. // we need logical, not physical, coordinates here, so use
  2918. // GetLineRect() instead of GetItemRect()
  2919. wxRect r = GetLineRect(i);
  2920. wxCoord x = r.GetRight(),
  2921. y = r.GetBottom();
  2922. if ( x > xMax )
  2923. xMax = x;
  2924. if ( y > yMax )
  2925. yMax = y;
  2926. }
  2927. }
  2928. // some fudge needed to make it look prettier
  2929. xMax += 2 * EXTRA_BORDER_X;
  2930. yMax += 2 * EXTRA_BORDER_Y;
  2931. // account for the scrollbars if necessary
  2932. const wxSize sizeAll = GetClientSize();
  2933. if ( xMax > sizeAll.x )
  2934. yMax += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
  2935. if ( yMax > sizeAll.y )
  2936. xMax += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
  2937. return wxRect(0, 0, xMax, yMax);
  2938. }
  2939. bool
  2940. wxListMainWindow::GetSubItemRect(long item, long subItem, wxRect& rect) const
  2941. {
  2942. wxCHECK_MSG( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM || InReportView(),
  2943. false,
  2944. wxT("GetSubItemRect only meaningful in report view") );
  2945. wxCHECK_MSG( item >= 0 && (size_t)item < GetItemCount(), false,
  2946. wxT("invalid item in GetSubItemRect") );
  2947. // ensure that we're laid out, otherwise we could return nonsense
  2948. if ( m_dirty )
  2949. {
  2950. wxConstCast(this, wxListMainWindow)->
  2951. RecalculatePositions(true /* no refresh */);
  2952. }
  2953. rect = GetLineRect((size_t)item);
  2954. // Adjust rect to specified column
  2955. if ( subItem != wxLIST_GETSUBITEMRECT_WHOLEITEM )
  2956. {
  2957. wxCHECK_MSG( subItem >= 0 && subItem < GetColumnCount(), false,
  2958. wxT("invalid subItem in GetSubItemRect") );
  2959. for (int i = 0; i < subItem; i++)
  2960. {
  2961. rect.x += GetColumnWidth(i);
  2962. }
  2963. rect.width = GetColumnWidth(subItem);
  2964. }
  2965. GetListCtrl()->CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
  2966. return true;
  2967. }
  2968. bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos) const
  2969. {
  2970. wxRect rect;
  2971. GetItemRect(item, rect);
  2972. pos.x = rect.x;
  2973. pos.y = rect.y;
  2974. return true;
  2975. }
  2976. // ----------------------------------------------------------------------------
  2977. // geometry calculation
  2978. // ----------------------------------------------------------------------------
  2979. void wxListMainWindow::RecalculatePositions(bool noRefresh)
  2980. {
  2981. const int lineHeight = GetLineHeight();
  2982. wxClientDC dc( this );
  2983. dc.SetFont( GetFont() );
  2984. const size_t count = GetItemCount();
  2985. int iconSpacing;
  2986. if ( HasFlag(wxLC_ICON) && m_normal_image_list )
  2987. iconSpacing = m_normal_spacing;
  2988. else if ( HasFlag(wxLC_SMALL_ICON) && m_small_image_list )
  2989. iconSpacing = m_small_spacing;
  2990. else
  2991. iconSpacing = 0;
  2992. // Note that we do not call GetClientSize() here but
  2993. // GetSize() and subtract the border size for sunken
  2994. // borders manually. This is technically incorrect,
  2995. // but we need to know the client area's size WITHOUT
  2996. // scrollbars here. Since we don't know if there are
  2997. // any scrollbars, we use GetSize() instead. Another
  2998. // solution would be to call SetScrollbars() here to
  2999. // remove the scrollbars and call GetClientSize() then,
  3000. // but this might result in flicker and - worse - will
  3001. // reset the scrollbars to 0 which is not good at all
  3002. // if you resize a dialog/window, but don't want to
  3003. // reset the window scrolling. RR.
  3004. // Furthermore, we actually do NOT subtract the border
  3005. // width as 2 pixels is just the extra space which we
  3006. // need around the actual content in the window. Other-
  3007. // wise the text would e.g. touch the upper border. RR.
  3008. int clientWidth,
  3009. clientHeight;
  3010. GetSize( &clientWidth, &clientHeight );
  3011. if ( InReportView() )
  3012. {
  3013. // all lines have the same height and we scroll one line per step
  3014. int entireHeight = count * lineHeight + LINE_SPACING;
  3015. m_linesPerPage = clientHeight / lineHeight;
  3016. ResetVisibleLinesRange();
  3017. GetListCtrl()->SetScrollbars( SCROLL_UNIT_X, lineHeight,
  3018. GetHeaderWidth() / SCROLL_UNIT_X,
  3019. (entireHeight + lineHeight - 1) / lineHeight,
  3020. GetListCtrl()->GetScrollPos(wxHORIZONTAL),
  3021. GetListCtrl()->GetScrollPos(wxVERTICAL),
  3022. true );
  3023. }
  3024. else // !report
  3025. {
  3026. // we have 3 different layout strategies: either layout all items
  3027. // horizontally/vertically (wxLC_ALIGN_XXX styles explicitly given) or
  3028. // to arrange them in top to bottom, left to right (don't ask me why
  3029. // not the other way round...) order
  3030. if ( HasFlag(wxLC_ALIGN_LEFT | wxLC_ALIGN_TOP) )
  3031. {
  3032. int x = EXTRA_BORDER_X;
  3033. int y = EXTRA_BORDER_Y;
  3034. wxCoord widthMax = 0;
  3035. size_t i;
  3036. for ( i = 0; i < count; i++ )
  3037. {
  3038. wxListLineData *line = GetLine(i);
  3039. line->CalculateSize( &dc, iconSpacing );
  3040. line->SetPosition( x, y, iconSpacing );
  3041. wxSize sizeLine = GetLineSize(i);
  3042. if ( HasFlag(wxLC_ALIGN_TOP) )
  3043. {
  3044. if ( sizeLine.x > widthMax )
  3045. widthMax = sizeLine.x;
  3046. y += sizeLine.y;
  3047. }
  3048. else // wxLC_ALIGN_LEFT
  3049. {
  3050. x += sizeLine.x + MARGIN_BETWEEN_ROWS;
  3051. }
  3052. }
  3053. if ( HasFlag(wxLC_ALIGN_TOP) )
  3054. {
  3055. // traverse the items again and tweak their sizes so that they are
  3056. // all the same in a row
  3057. for ( i = 0; i < count; i++ )
  3058. {
  3059. wxListLineData *line = GetLine(i);
  3060. line->m_gi->ExtendWidth(widthMax);
  3061. }
  3062. }
  3063. GetListCtrl()->SetScrollbars
  3064. (
  3065. SCROLL_UNIT_X,
  3066. lineHeight,
  3067. (x + SCROLL_UNIT_X) / SCROLL_UNIT_X,
  3068. (y + lineHeight) / lineHeight,
  3069. GetListCtrl()->GetScrollPos( wxHORIZONTAL ),
  3070. GetListCtrl()->GetScrollPos( wxVERTICAL ),
  3071. true
  3072. );
  3073. }
  3074. else // "flowed" arrangement, the most complicated case
  3075. {
  3076. // at first we try without any scrollbars, if the items don't fit into
  3077. // the window, we recalculate after subtracting the space taken by the
  3078. // scrollbar
  3079. int entireWidth = 0;
  3080. for (int tries = 0; tries < 2; tries++)
  3081. {
  3082. entireWidth = 2 * EXTRA_BORDER_X;
  3083. if (tries == 1)
  3084. {
  3085. // Now we have decided that the items do not fit into the
  3086. // client area, so we need a scrollbar
  3087. entireWidth += SCROLL_UNIT_X;
  3088. }
  3089. int x = EXTRA_BORDER_X;
  3090. int y = EXTRA_BORDER_Y;
  3091. // Note that "row" here is vertical, i.e. what is called
  3092. // "column" in many other places in wxWidgets.
  3093. int maxWidthInThisRow = 0;
  3094. m_linesPerPage = 0;
  3095. int currentlyVisibleLines = 0;
  3096. for (size_t i = 0; i < count; i++)
  3097. {
  3098. currentlyVisibleLines++;
  3099. wxListLineData *line = GetLine( i );
  3100. line->CalculateSize( &dc, iconSpacing );
  3101. line->SetPosition( x, y, iconSpacing );
  3102. wxSize sizeLine = GetLineSize( i );
  3103. if ( maxWidthInThisRow < sizeLine.x )
  3104. maxWidthInThisRow = sizeLine.x;
  3105. y += sizeLine.y;
  3106. if (currentlyVisibleLines > m_linesPerPage)
  3107. m_linesPerPage = currentlyVisibleLines;
  3108. // Have we reached the end of the row either because no
  3109. // more items would fit or because there are simply no more
  3110. // items?
  3111. if ( y + sizeLine.y >= clientHeight
  3112. || i == count - 1)
  3113. {
  3114. // Adjust all items in this row to have the same
  3115. // width to ensure that they all align horizontally in
  3116. // icon view.
  3117. if ( HasFlag(wxLC_ICON) || HasFlag(wxLC_SMALL_ICON) )
  3118. {
  3119. size_t firstRowLine = i - currentlyVisibleLines + 1;
  3120. for (size_t j = firstRowLine; j <= i; j++)
  3121. {
  3122. GetLine(j)->m_gi->ExtendWidth(maxWidthInThisRow);
  3123. }
  3124. }
  3125. currentlyVisibleLines = 0;
  3126. y = EXTRA_BORDER_Y;
  3127. maxWidthInThisRow += MARGIN_BETWEEN_ROWS;
  3128. x += maxWidthInThisRow;
  3129. entireWidth += maxWidthInThisRow;
  3130. maxWidthInThisRow = 0;
  3131. }
  3132. if ( (tries == 0) &&
  3133. (entireWidth + SCROLL_UNIT_X > clientWidth) )
  3134. {
  3135. clientHeight -= wxSystemSettings::
  3136. GetMetric(wxSYS_HSCROLL_Y);
  3137. m_linesPerPage = 0;
  3138. break;
  3139. }
  3140. if ( i == count - 1 )
  3141. tries = 1; // Everything fits, no second try required.
  3142. }
  3143. }
  3144. GetListCtrl()->SetScrollbars
  3145. (
  3146. SCROLL_UNIT_X,
  3147. lineHeight,
  3148. (entireWidth + SCROLL_UNIT_X) / SCROLL_UNIT_X,
  3149. 0,
  3150. GetListCtrl()->GetScrollPos( wxHORIZONTAL ),
  3151. 0,
  3152. true
  3153. );
  3154. }
  3155. }
  3156. if ( !noRefresh )
  3157. {
  3158. // FIXME: why should we call it from here?
  3159. UpdateCurrent();
  3160. RefreshAll();
  3161. }
  3162. }
  3163. void wxListMainWindow::RefreshAll()
  3164. {
  3165. m_dirty = false;
  3166. Refresh();
  3167. wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
  3168. if ( headerWin && headerWin->m_dirty )
  3169. {
  3170. headerWin->m_dirty = false;
  3171. headerWin->Refresh();
  3172. }
  3173. }
  3174. void wxListMainWindow::UpdateCurrent()
  3175. {
  3176. if ( !HasCurrent() && !IsEmpty() )
  3177. ChangeCurrent(0);
  3178. }
  3179. long wxListMainWindow::GetNextItem( long item,
  3180. int WXUNUSED(geometry),
  3181. int state ) const
  3182. {
  3183. long ret = item,
  3184. max = GetItemCount();
  3185. wxCHECK_MSG( (ret == -1) || (ret < max), -1,
  3186. wxT("invalid listctrl index in GetNextItem()") );
  3187. // notice that we start with the next item (or the first one if item == -1)
  3188. // and this is intentional to allow writing a simple loop to iterate over
  3189. // all selected items
  3190. ret++;
  3191. if ( ret == max )
  3192. // this is not an error because the index was OK initially,
  3193. // just no such item
  3194. return -1;
  3195. if ( !state )
  3196. // any will do
  3197. return (size_t)ret;
  3198. size_t count = GetItemCount();
  3199. for ( size_t line = (size_t)ret; line < count; line++ )
  3200. {
  3201. if ( (state & wxLIST_STATE_FOCUSED) && (line == m_current) )
  3202. return line;
  3203. if ( (state & wxLIST_STATE_SELECTED) && IsHighlighted(line) )
  3204. return line;
  3205. }
  3206. return -1;
  3207. }
  3208. // ----------------------------------------------------------------------------
  3209. // deleting stuff
  3210. // ----------------------------------------------------------------------------
  3211. void wxListMainWindow::DeleteItem( long lindex )
  3212. {
  3213. size_t count = GetItemCount();
  3214. wxCHECK_RET( (lindex >= 0) && ((size_t)lindex < count),
  3215. wxT("invalid item index in DeleteItem") );
  3216. size_t index = (size_t)lindex;
  3217. // we don't need to adjust the index for the previous items
  3218. if ( HasCurrent() && m_current >= index )
  3219. {
  3220. // if the current item is being deleted, we want the next one to
  3221. // become selected - unless there is no next one - so don't adjust
  3222. // m_current in this case
  3223. if ( m_current != index || m_current == count - 1 )
  3224. m_current--;
  3225. }
  3226. if ( InReportView() )
  3227. {
  3228. // mark the Column Max Width cache as dirty if the items in the line
  3229. // we're deleting contain the Max Column Width
  3230. wxListLineData * const line = GetLine(index);
  3231. wxListItemDataList::compatibility_iterator n;
  3232. wxListItemData *itemData;
  3233. wxListItem item;
  3234. int itemWidth;
  3235. for (size_t i = 0; i < m_columns.GetCount(); i++)
  3236. {
  3237. n = line->m_items.Item( i );
  3238. itemData = n->GetData();
  3239. itemData->GetItem(item);
  3240. itemWidth = GetItemWidthWithImage(&item);
  3241. if (itemWidth >= m_aColWidths.Item(i)->nMaxWidth)
  3242. m_aColWidths.Item(i)->bNeedsUpdate = true;
  3243. }
  3244. ResetVisibleLinesRange();
  3245. }
  3246. SendNotify( index, wxEVT_LIST_DELETE_ITEM, wxDefaultPosition );
  3247. if ( IsVirtual() )
  3248. {
  3249. m_countVirt--;
  3250. m_selStore.OnItemDelete(index);
  3251. }
  3252. else
  3253. {
  3254. m_lines.RemoveAt( index );
  3255. }
  3256. // we need to refresh the (vert) scrollbar as the number of items changed
  3257. m_dirty = true;
  3258. RefreshAfter(index);
  3259. }
  3260. void wxListMainWindow::DeleteColumn( int col )
  3261. {
  3262. wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
  3263. wxCHECK_RET( node, wxT("invalid column index in DeleteColumn()") );
  3264. m_dirty = true;
  3265. delete node->GetData();
  3266. m_columns.Erase( node );
  3267. if ( !IsVirtual() )
  3268. {
  3269. // update all the items
  3270. for ( size_t i = 0; i < m_lines.GetCount(); i++ )
  3271. {
  3272. wxListLineData * const line = GetLine(i);
  3273. // In the following atypical but possible scenario it can be
  3274. // legal to call DeleteColumn() but the items may not have any
  3275. // values for it:
  3276. // 1. In report view, insert a second column.
  3277. // 2. Still in report view, add an item with 2 values.
  3278. // 3. Switch to an icon (or list) view.
  3279. // 4. Add an item -- necessarily with 1 value only.
  3280. // 5. Switch back to report view.
  3281. // 6. Call DeleteColumn().
  3282. // So we need to check for this as otherwise we would simply crash
  3283. // if this happens.
  3284. if ( line->m_items.GetCount() <= static_cast<unsigned>(col) )
  3285. continue;
  3286. wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
  3287. delete n->GetData();
  3288. line->m_items.Erase(n);
  3289. }
  3290. }
  3291. if ( InReportView() ) // we only cache max widths when in Report View
  3292. {
  3293. delete m_aColWidths.Item(col);
  3294. m_aColWidths.RemoveAt(col);
  3295. }
  3296. // invalidate it as it has to be recalculated
  3297. m_headerWidth = 0;
  3298. }
  3299. void wxListMainWindow::DoDeleteAllItems()
  3300. {
  3301. if ( IsEmpty() )
  3302. // nothing to do - in particular, don't send the event
  3303. return;
  3304. ResetCurrent();
  3305. // to make the deletion of all items faster, we don't send the
  3306. // notifications for each item deletion in this case but only one event
  3307. // for all of them: this is compatible with wxMSW and documented in
  3308. // DeleteAllItems() description
  3309. wxListEvent event( wxEVT_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() );
  3310. event.SetEventObject( GetParent() );
  3311. GetParent()->GetEventHandler()->ProcessEvent( event );
  3312. if ( IsVirtual() )
  3313. {
  3314. m_countVirt = 0;
  3315. m_selStore.Clear();
  3316. }
  3317. if ( InReportView() )
  3318. {
  3319. ResetVisibleLinesRange();
  3320. for (size_t i = 0; i < m_aColWidths.GetCount(); i++)
  3321. {
  3322. m_aColWidths.Item(i)->bNeedsUpdate = true;
  3323. }
  3324. }
  3325. m_lines.Clear();
  3326. }
  3327. void wxListMainWindow::DeleteAllItems()
  3328. {
  3329. DoDeleteAllItems();
  3330. RecalculatePositions();
  3331. }
  3332. void wxListMainWindow::DeleteEverything()
  3333. {
  3334. WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
  3335. WX_CLEAR_ARRAY(m_aColWidths);
  3336. DeleteAllItems();
  3337. }
  3338. // ----------------------------------------------------------------------------
  3339. // scanning for an item
  3340. // ----------------------------------------------------------------------------
  3341. void wxListMainWindow::EnsureVisible( long index )
  3342. {
  3343. wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(),
  3344. wxT("invalid index in EnsureVisible") );
  3345. // We have to call this here because the label in question might just have
  3346. // been added and its position is not known yet
  3347. if ( m_dirty )
  3348. RecalculatePositions(true /* no refresh */);
  3349. MoveToItem((size_t)index);
  3350. }
  3351. long wxListMainWindow::FindItem(long start, const wxString& str, bool partial )
  3352. {
  3353. if (str.empty())
  3354. return wxNOT_FOUND;
  3355. long pos = start;
  3356. wxString str_upper = str.Upper();
  3357. if (pos < 0)
  3358. pos = 0;
  3359. size_t count = GetItemCount();
  3360. for ( size_t i = (size_t)pos; i < count; i++ )
  3361. {
  3362. wxListLineData *line = GetLine(i);
  3363. wxString line_upper = line->GetText(0).Upper();
  3364. if (!partial)
  3365. {
  3366. if (line_upper == str_upper )
  3367. return i;
  3368. }
  3369. else
  3370. {
  3371. if (line_upper.find(str_upper) == 0)
  3372. return i;
  3373. }
  3374. }
  3375. return wxNOT_FOUND;
  3376. }
  3377. long wxListMainWindow::FindItem(long start, wxUIntPtr data)
  3378. {
  3379. long pos = start;
  3380. if (pos < 0)
  3381. pos = 0;
  3382. size_t count = GetItemCount();
  3383. for (size_t i = (size_t)pos; i < count; i++)
  3384. {
  3385. wxListLineData *line = GetLine(i);
  3386. wxListItem item;
  3387. line->GetItem( 0, item );
  3388. if (item.m_data == data)
  3389. return i;
  3390. }
  3391. return wxNOT_FOUND;
  3392. }
  3393. long wxListMainWindow::FindItem( const wxPoint& pt )
  3394. {
  3395. size_t topItem;
  3396. GetVisibleLinesRange( &topItem, NULL );
  3397. wxPoint p;
  3398. GetItemPosition( GetItemCount() - 1, p );
  3399. if ( p.y == 0 )
  3400. return topItem;
  3401. long id = (long)floor( pt.y * double(GetItemCount() - topItem - 1) / p.y + topItem );
  3402. if ( id >= 0 && id < (long)GetItemCount() )
  3403. return id;
  3404. return wxNOT_FOUND;
  3405. }
  3406. long wxListMainWindow::HitTest( int x, int y, int &flags ) const
  3407. {
  3408. GetListCtrl()->CalcUnscrolledPosition( x, y, &x, &y );
  3409. size_t count = GetItemCount();
  3410. if ( InReportView() )
  3411. {
  3412. size_t current = y / GetLineHeight();
  3413. if ( current < count )
  3414. {
  3415. flags = HitTestLine(current, x, y);
  3416. if ( flags )
  3417. return current;
  3418. }
  3419. }
  3420. else // !report
  3421. {
  3422. // TODO: optimize it too! this is less simple than for report view but
  3423. // enumerating all items is still not a way to do it!!
  3424. for ( size_t current = 0; current < count; current++ )
  3425. {
  3426. flags = HitTestLine(current, x, y);
  3427. if ( flags )
  3428. return current;
  3429. }
  3430. }
  3431. return wxNOT_FOUND;
  3432. }
  3433. // ----------------------------------------------------------------------------
  3434. // adding stuff
  3435. // ----------------------------------------------------------------------------
  3436. void wxListMainWindow::InsertItem( wxListItem &item )
  3437. {
  3438. wxASSERT_MSG( !IsVirtual(), wxT("can't be used with virtual control") );
  3439. int count = GetItemCount();
  3440. wxCHECK_RET( item.m_itemId >= 0, wxT("invalid item index") );
  3441. if (item.m_itemId > count)
  3442. item.m_itemId = count;
  3443. size_t id = item.m_itemId;
  3444. m_dirty = true;
  3445. if ( InReportView() )
  3446. {
  3447. ResetVisibleLinesRange();
  3448. const unsigned col = item.GetColumn();
  3449. wxCHECK_RET( col < m_aColWidths.size(), "invalid item column" );
  3450. // calculate the width of the item and adjust the max column width
  3451. wxColWidthInfo *pWidthInfo = m_aColWidths.Item(col);
  3452. int width = GetItemWidthWithImage(&item);
  3453. item.SetWidth(width);
  3454. if (width > pWidthInfo->nMaxWidth)
  3455. pWidthInfo->nMaxWidth = width;
  3456. }
  3457. wxListLineData *line = new wxListLineData(this);
  3458. line->SetItem( item.m_col, item );
  3459. if ( item.m_mask & wxLIST_MASK_IMAGE )
  3460. {
  3461. // Reset the buffered height if it's not big enough for the new image.
  3462. int image = item.GetImage();
  3463. if ( m_small_image_list && image != -1 && InReportView() )
  3464. {
  3465. int imageWidth, imageHeight;
  3466. m_small_image_list->GetSize(image, imageWidth, imageHeight);
  3467. if ( imageHeight > m_lineHeight )
  3468. m_lineHeight = 0;
  3469. }
  3470. }
  3471. m_lines.Insert( line, id );
  3472. m_dirty = true;
  3473. // If an item is selected at or below the point of insertion, we need to
  3474. // increment the member variables because the current row's index has gone
  3475. // up by one
  3476. if ( HasCurrent() && m_current >= id )
  3477. m_current++;
  3478. SendNotify(id, wxEVT_LIST_INSERT_ITEM);
  3479. RefreshLines(id, GetItemCount() - 1);
  3480. }
  3481. long wxListMainWindow::InsertColumn( long col, const wxListItem &item )
  3482. {
  3483. long idx = -1;
  3484. m_dirty = true;
  3485. if ( InReportView() )
  3486. {
  3487. wxListHeaderData *column = new wxListHeaderData( item );
  3488. if (item.m_width == wxLIST_AUTOSIZE_USEHEADER)
  3489. column->SetWidth(ComputeMinHeaderWidth(column));
  3490. wxColWidthInfo *colWidthInfo = new wxColWidthInfo();
  3491. bool insert = (col >= 0) && ((size_t)col < m_columns.GetCount());
  3492. if ( insert )
  3493. {
  3494. wxListHeaderDataList::compatibility_iterator
  3495. node = m_columns.Item( col );
  3496. m_columns.Insert( node, column );
  3497. m_aColWidths.Insert( colWidthInfo, col );
  3498. idx = col;
  3499. }
  3500. else
  3501. {
  3502. idx = m_aColWidths.GetCount();
  3503. m_columns.Append( column );
  3504. m_aColWidths.Add( colWidthInfo );
  3505. }
  3506. if ( !IsVirtual() )
  3507. {
  3508. // update all the items
  3509. for ( size_t i = 0; i < m_lines.GetCount(); i++ )
  3510. {
  3511. wxListLineData * const line = GetLine(i);
  3512. wxListItemData * const data = new wxListItemData(this);
  3513. if ( insert )
  3514. line->m_items.Insert(col, data);
  3515. else
  3516. line->m_items.Append(data);
  3517. }
  3518. }
  3519. // invalidate it as it has to be recalculated
  3520. m_headerWidth = 0;
  3521. }
  3522. return idx;
  3523. }
  3524. int wxListMainWindow::GetItemWidthWithImage(wxListItem * item)
  3525. {
  3526. int width = 0;
  3527. wxClientDC dc(this);
  3528. dc.SetFont( GetFont() );
  3529. if (item->GetImage() != -1)
  3530. {
  3531. int ix, iy;
  3532. GetImageSize( item->GetImage(), ix, iy );
  3533. width += ix + 5;
  3534. }
  3535. if (!item->GetText().empty())
  3536. {
  3537. wxCoord w;
  3538. dc.GetTextExtent( item->GetText(), &w, NULL );
  3539. width += w;
  3540. }
  3541. return width;
  3542. }
  3543. // ----------------------------------------------------------------------------
  3544. // sorting
  3545. // ----------------------------------------------------------------------------
  3546. static wxListCtrlCompare list_ctrl_compare_func_2;
  3547. static wxIntPtr list_ctrl_compare_data;
  3548. int LINKAGEMODE list_ctrl_compare_func_1( wxListLineData **arg1, wxListLineData **arg2 )
  3549. {
  3550. wxListLineData *line1 = *arg1;
  3551. wxListLineData *line2 = *arg2;
  3552. wxListItem item;
  3553. line1->GetItem( 0, item );
  3554. wxUIntPtr data1 = item.m_data;
  3555. line2->GetItem( 0, item );
  3556. wxUIntPtr data2 = item.m_data;
  3557. return list_ctrl_compare_func_2( data1, data2, list_ctrl_compare_data );
  3558. }
  3559. void wxListMainWindow::SortItems( wxListCtrlCompare fn, wxIntPtr data )
  3560. {
  3561. // selections won't make sense any more after sorting the items so reset
  3562. // them
  3563. HighlightAll(false);
  3564. ResetCurrent();
  3565. list_ctrl_compare_func_2 = fn;
  3566. list_ctrl_compare_data = data;
  3567. m_lines.Sort( list_ctrl_compare_func_1 );
  3568. m_dirty = true;
  3569. }
  3570. // ----------------------------------------------------------------------------
  3571. // scrolling
  3572. // ----------------------------------------------------------------------------
  3573. void wxListMainWindow::OnScroll(wxScrollWinEvent& event)
  3574. {
  3575. // update our idea of which lines are shown when we redraw the window the
  3576. // next time
  3577. ResetVisibleLinesRange();
  3578. if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
  3579. {
  3580. wxGenericListCtrl* lc = GetListCtrl();
  3581. wxCHECK_RET( lc, wxT("no listctrl window?") );
  3582. if (lc->m_headerWin) // when we use wxLC_NO_HEADER, m_headerWin==NULL
  3583. {
  3584. lc->m_headerWin->Refresh();
  3585. lc->m_headerWin->Update();
  3586. }
  3587. }
  3588. }
  3589. int wxListMainWindow::GetCountPerPage() const
  3590. {
  3591. if ( !m_linesPerPage )
  3592. {
  3593. wxConstCast(this, wxListMainWindow)->
  3594. m_linesPerPage = GetClientSize().y / GetLineHeight();
  3595. }
  3596. return m_linesPerPage;
  3597. }
  3598. void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to)
  3599. {
  3600. wxASSERT_MSG( InReportView(), wxT("this is for report mode only") );
  3601. if ( m_lineFrom == (size_t)-1 )
  3602. {
  3603. size_t count = GetItemCount();
  3604. if ( count )
  3605. {
  3606. m_lineFrom = GetListCtrl()->GetScrollPos(wxVERTICAL);
  3607. // this may happen if SetScrollbars() hadn't been called yet
  3608. if ( m_lineFrom >= count )
  3609. m_lineFrom = count - 1;
  3610. // we redraw one extra line but this is needed to make the redrawing
  3611. // logic work when there is a fractional number of lines on screen
  3612. m_lineTo = m_lineFrom + m_linesPerPage;
  3613. if ( m_lineTo >= count )
  3614. m_lineTo = count - 1;
  3615. }
  3616. else // empty control
  3617. {
  3618. m_lineFrom = 0;
  3619. m_lineTo = (size_t)-1;
  3620. }
  3621. }
  3622. wxASSERT_MSG( IsEmpty() ||
  3623. (m_lineFrom <= m_lineTo && m_lineTo < GetItemCount()),
  3624. wxT("GetVisibleLinesRange() returns incorrect result") );
  3625. if ( from )
  3626. *from = m_lineFrom;
  3627. if ( to )
  3628. *to = m_lineTo;
  3629. }
  3630. size_t
  3631. wxListMainWindow::PrefixFindItem(size_t idParent,
  3632. const wxString& prefixOrig) const
  3633. {
  3634. // if no items then just return
  3635. if ( idParent == (size_t)-1 )
  3636. return idParent;
  3637. // match is case insensitive as this is more convenient to the user: having
  3638. // to press Shift-letter to go to the item starting with a capital letter
  3639. // would be too bothersome
  3640. wxString prefix = prefixOrig.Lower();
  3641. // determine the starting point: we shouldn't take the current item (this
  3642. // allows to switch between two items starting with the same letter just by
  3643. // pressing it) but we shouldn't jump to the next one if the user is
  3644. // continuing to type as otherwise he might easily skip the item he wanted
  3645. size_t itemid = idParent;
  3646. if ( prefix.length() == 1 )
  3647. {
  3648. itemid += 1;
  3649. }
  3650. // look for the item starting with the given prefix after it
  3651. while ( ( itemid < (size_t)GetItemCount() ) &&
  3652. !GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) )
  3653. {
  3654. itemid += 1;
  3655. }
  3656. // if we haven't found anything...
  3657. if ( !( itemid < (size_t)GetItemCount() ) )
  3658. {
  3659. // ... wrap to the beginning
  3660. itemid = 0;
  3661. // and try all the items (stop when we get to the one we started from)
  3662. while ( ( itemid < (size_t)GetItemCount() ) && itemid != idParent &&
  3663. !GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) )
  3664. {
  3665. itemid += 1;
  3666. }
  3667. // If we haven't found the item, id will be (size_t)-1, as per
  3668. // documentation
  3669. if ( !( itemid < (size_t)GetItemCount() ) ||
  3670. ( ( itemid == idParent ) &&
  3671. !GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) ) )
  3672. {
  3673. itemid = (size_t)-1;
  3674. }
  3675. }
  3676. return itemid;
  3677. }
  3678. // -------------------------------------------------------------------------------------
  3679. // wxGenericListCtrl
  3680. // -------------------------------------------------------------------------------------
  3681. IMPLEMENT_DYNAMIC_CLASS(wxGenericListCtrl, wxControl)
  3682. BEGIN_EVENT_TABLE(wxGenericListCtrl,wxListCtrlBase)
  3683. EVT_SIZE(wxGenericListCtrl::OnSize)
  3684. EVT_SCROLLWIN(wxGenericListCtrl::OnScroll)
  3685. END_EVENT_TABLE()
  3686. void wxGenericListCtrl::Init()
  3687. {
  3688. m_imageListNormal = NULL;
  3689. m_imageListSmall = NULL;
  3690. m_imageListState = NULL;
  3691. m_ownsImageListNormal =
  3692. m_ownsImageListSmall =
  3693. m_ownsImageListState = false;
  3694. m_mainWin = NULL;
  3695. m_headerWin = NULL;
  3696. }
  3697. wxGenericListCtrl::~wxGenericListCtrl()
  3698. {
  3699. if (m_ownsImageListNormal)
  3700. delete m_imageListNormal;
  3701. if (m_ownsImageListSmall)
  3702. delete m_imageListSmall;
  3703. if (m_ownsImageListState)
  3704. delete m_imageListState;
  3705. }
  3706. void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded()
  3707. {
  3708. bool needs_header = HasHeader();
  3709. bool has_header = (m_headerWin != NULL);
  3710. if (needs_header == has_header)
  3711. return;
  3712. if (needs_header)
  3713. {
  3714. // Notice that we must initialize m_headerWin first, and create the
  3715. // real window only later, so that the test in the beginning of the
  3716. // function blocks repeated creation of the header as it could happen
  3717. // before via wxNavigationEnabled::AddChild() -> ToggleWindowStyle() ->
  3718. // SetWindowStyleFlag().
  3719. m_headerWin = new wxListHeaderWindow();
  3720. m_headerWin->Create
  3721. (
  3722. this, wxID_ANY, m_mainWin,
  3723. wxPoint(0,0),
  3724. wxSize
  3725. (
  3726. GetClientSize().x,
  3727. wxRendererNative::Get().GetHeaderButtonHeight(this)
  3728. ),
  3729. wxTAB_TRAVERSAL
  3730. );
  3731. #if defined( __WXMAC__ )
  3732. static wxFont font( wxOSX_SYSTEM_FONT_SMALL );
  3733. m_headerWin->SetFont( font );
  3734. #endif
  3735. GetSizer()->Prepend( m_headerWin, 0, wxGROW );
  3736. }
  3737. else
  3738. {
  3739. GetSizer()->Detach( m_headerWin );
  3740. wxDELETE(m_headerWin);
  3741. }
  3742. }
  3743. bool wxGenericListCtrl::Create(wxWindow *parent,
  3744. wxWindowID id,
  3745. const wxPoint &pos,
  3746. const wxSize &size,
  3747. long style,
  3748. const wxValidator &validator,
  3749. const wxString &name)
  3750. {
  3751. Init();
  3752. // just like in other ports, an assert will fail if the user doesn't give any type style:
  3753. wxASSERT_MSG( (style & wxLC_MASK_TYPE),
  3754. wxT("wxListCtrl style should have exactly one mode bit set") );
  3755. if ( !wxListCtrlBase::Create( parent, id, pos, size,
  3756. style | wxVSCROLL | wxHSCROLL,
  3757. validator, name ) )
  3758. return false;
  3759. m_mainWin = new wxListMainWindow(this, wxID_ANY, wxPoint(0, 0), size);
  3760. SetTargetWindow( m_mainWin );
  3761. // We use the cursor keys for moving the selection, not scrolling, so call
  3762. // this method to ensure wxScrollHelperEvtHandler doesn't catch all
  3763. // keyboard events forwarded to us from wxListMainWindow.
  3764. DisableKeyboardScrolling();
  3765. wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
  3766. sizer->Add( m_mainWin, 1, wxGROW );
  3767. SetSizer( sizer );
  3768. CreateOrDestroyHeaderWindowAsNeeded();
  3769. SetInitialSize(size);
  3770. return true;
  3771. }
  3772. wxBorder wxGenericListCtrl::GetDefaultBorder() const
  3773. {
  3774. return wxBORDER_THEME;
  3775. }
  3776. #if defined(__WXMSW__) && !defined(__WXWINCE__) && !defined(__WXUNIVERSAL__)
  3777. WXLRESULT wxGenericListCtrl::MSWWindowProc(WXUINT nMsg,
  3778. WXWPARAM wParam,
  3779. WXLPARAM lParam)
  3780. {
  3781. WXLRESULT rc = wxListCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
  3782. // we need to process arrows ourselves for scrolling
  3783. if ( nMsg == WM_GETDLGCODE )
  3784. {
  3785. rc |= DLGC_WANTARROWS;
  3786. }
  3787. return rc;
  3788. }
  3789. #endif // __WXMSW__
  3790. wxSize wxGenericListCtrl::GetSizeAvailableForScrollTarget(const wxSize& size)
  3791. {
  3792. wxSize newsize = size;
  3793. if (m_headerWin)
  3794. newsize.y -= m_headerWin->GetSize().y;
  3795. return newsize;
  3796. }
  3797. void wxGenericListCtrl::OnScroll(wxScrollWinEvent& event)
  3798. {
  3799. // update our idea of which lines are shown when we redraw
  3800. // the window the next time
  3801. m_mainWin->ResetVisibleLinesRange();
  3802. if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
  3803. {
  3804. m_headerWin->Refresh();
  3805. m_headerWin->Update();
  3806. }
  3807. // Let the window be scrolled as usual by the default handler.
  3808. event.Skip();
  3809. }
  3810. void wxGenericListCtrl::SetSingleStyle( long style, bool add )
  3811. {
  3812. wxASSERT_MSG( !(style & wxLC_VIRTUAL),
  3813. wxT("wxLC_VIRTUAL can't be [un]set") );
  3814. long flag = GetWindowStyle();
  3815. if (add)
  3816. {
  3817. if (style & wxLC_MASK_TYPE)
  3818. flag &= ~(wxLC_MASK_TYPE | wxLC_VIRTUAL);
  3819. if (style & wxLC_MASK_ALIGN)
  3820. flag &= ~wxLC_MASK_ALIGN;
  3821. if (style & wxLC_MASK_SORT)
  3822. flag &= ~wxLC_MASK_SORT;
  3823. }
  3824. if (add)
  3825. flag |= style;
  3826. else
  3827. flag &= ~style;
  3828. // some styles can be set without recreating everything (as happens in
  3829. // SetWindowStyleFlag() which calls wxListMainWindow::DeleteEverything())
  3830. if ( !(style & ~(wxLC_HRULES | wxLC_VRULES)) )
  3831. {
  3832. Refresh();
  3833. wxWindow::SetWindowStyleFlag(flag);
  3834. }
  3835. else
  3836. {
  3837. SetWindowStyleFlag( flag );
  3838. }
  3839. }
  3840. void wxGenericListCtrl::SetWindowStyleFlag( long flag )
  3841. {
  3842. // we add wxHSCROLL and wxVSCROLL in ctor unconditionally and it never
  3843. // makes sense to remove them as we'll always add scrollbars anyhow when
  3844. // needed
  3845. flag |= wxHSCROLL | wxVSCROLL;
  3846. const bool wasInReportView = HasFlag(wxLC_REPORT);
  3847. // update the window style first so that the header is created or destroyed
  3848. // corresponding to the new style
  3849. wxWindow::SetWindowStyleFlag( flag );
  3850. if (m_mainWin)
  3851. {
  3852. const bool inReportView = (flag & wxLC_REPORT) != 0;
  3853. if ( inReportView != wasInReportView )
  3854. {
  3855. // we need to notify the main window about this change as it must
  3856. // update its data structures
  3857. m_mainWin->SetReportView(inReportView);
  3858. }
  3859. // m_mainWin->DeleteEverything(); wxMSW doesn't do that
  3860. CreateOrDestroyHeaderWindowAsNeeded();
  3861. GetSizer()->Layout();
  3862. }
  3863. }
  3864. bool wxGenericListCtrl::GetColumn(int col, wxListItem &item) const
  3865. {
  3866. m_mainWin->GetColumn( col, item );
  3867. return true;
  3868. }
  3869. bool wxGenericListCtrl::SetColumn( int col, const wxListItem& item )
  3870. {
  3871. m_mainWin->SetColumn( col, item );
  3872. return true;
  3873. }
  3874. int wxGenericListCtrl::GetColumnWidth( int col ) const
  3875. {
  3876. return m_mainWin->GetColumnWidth( col );
  3877. }
  3878. bool wxGenericListCtrl::SetColumnWidth( int col, int width )
  3879. {
  3880. m_mainWin->SetColumnWidth( col, width );
  3881. return true;
  3882. }
  3883. int wxGenericListCtrl::GetCountPerPage() const
  3884. {
  3885. return m_mainWin->GetCountPerPage(); // different from Windows ?
  3886. }
  3887. bool wxGenericListCtrl::GetItem( wxListItem &info ) const
  3888. {
  3889. m_mainWin->GetItem( info );
  3890. return true;
  3891. }
  3892. bool wxGenericListCtrl::SetItem( wxListItem &info )
  3893. {
  3894. m_mainWin->SetItem( info );
  3895. return true;
  3896. }
  3897. long wxGenericListCtrl::SetItem( long index, int col, const wxString& label, int imageId )
  3898. {
  3899. wxListItem info;
  3900. info.m_text = label;
  3901. info.m_mask = wxLIST_MASK_TEXT;
  3902. info.m_itemId = index;
  3903. info.m_col = col;
  3904. if ( imageId > -1 )
  3905. {
  3906. info.m_image = imageId;
  3907. info.m_mask |= wxLIST_MASK_IMAGE;
  3908. }
  3909. m_mainWin->SetItem(info);
  3910. return true;
  3911. }
  3912. int wxGenericListCtrl::GetItemState( long item, long stateMask ) const
  3913. {
  3914. return m_mainWin->GetItemState( item, stateMask );
  3915. }
  3916. bool wxGenericListCtrl::SetItemState( long item, long state, long stateMask )
  3917. {
  3918. m_mainWin->SetItemState( item, state, stateMask );
  3919. return true;
  3920. }
  3921. bool
  3922. wxGenericListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) )
  3923. {
  3924. return SetItemColumnImage(item, 0, image);
  3925. }
  3926. bool
  3927. wxGenericListCtrl::SetItemColumnImage( long item, long column, int image )
  3928. {
  3929. wxListItem info;
  3930. info.m_image = image;
  3931. info.m_mask = wxLIST_MASK_IMAGE;
  3932. info.m_itemId = item;
  3933. info.m_col = column;
  3934. m_mainWin->SetItem( info );
  3935. return true;
  3936. }
  3937. wxString wxGenericListCtrl::GetItemText( long item, int col ) const
  3938. {
  3939. return m_mainWin->GetItemText(item, col);
  3940. }
  3941. void wxGenericListCtrl::SetItemText( long item, const wxString& str )
  3942. {
  3943. m_mainWin->SetItemText(item, str);
  3944. }
  3945. wxUIntPtr wxGenericListCtrl::GetItemData( long item ) const
  3946. {
  3947. wxListItem info;
  3948. info.m_mask = wxLIST_MASK_DATA;
  3949. info.m_itemId = item;
  3950. m_mainWin->GetItem( info );
  3951. return info.m_data;
  3952. }
  3953. bool wxGenericListCtrl::SetItemPtrData( long item, wxUIntPtr data )
  3954. {
  3955. wxListItem info;
  3956. info.m_mask = wxLIST_MASK_DATA;
  3957. info.m_itemId = item;
  3958. info.m_data = data;
  3959. m_mainWin->SetItem( info );
  3960. return true;
  3961. }
  3962. wxRect wxGenericListCtrl::GetViewRect() const
  3963. {
  3964. return m_mainWin->GetViewRect();
  3965. }
  3966. bool wxGenericListCtrl::GetItemRect(long item, wxRect& rect, int code) const
  3967. {
  3968. return GetSubItemRect(item, wxLIST_GETSUBITEMRECT_WHOLEITEM, rect, code);
  3969. }
  3970. bool wxGenericListCtrl::GetSubItemRect(long item,
  3971. long subItem,
  3972. wxRect& rect,
  3973. int WXUNUSED(code)) const
  3974. {
  3975. if ( !m_mainWin->GetSubItemRect( item, subItem, rect ) )
  3976. return false;
  3977. if ( m_mainWin->HasHeader() )
  3978. rect.y += m_headerWin->GetSize().y + 1;
  3979. return true;
  3980. }
  3981. bool wxGenericListCtrl::GetItemPosition( long item, wxPoint& pos ) const
  3982. {
  3983. m_mainWin->GetItemPosition( item, pos );
  3984. return true;
  3985. }
  3986. bool wxGenericListCtrl::SetItemPosition( long WXUNUSED(item), const wxPoint& WXUNUSED(pos) )
  3987. {
  3988. return false;
  3989. }
  3990. int wxGenericListCtrl::GetItemCount() const
  3991. {
  3992. return m_mainWin->GetItemCount();
  3993. }
  3994. int wxGenericListCtrl::GetColumnCount() const
  3995. {
  3996. return m_mainWin->GetColumnCount();
  3997. }
  3998. void wxGenericListCtrl::SetItemSpacing( int spacing, bool isSmall )
  3999. {
  4000. m_mainWin->SetItemSpacing( spacing, isSmall );
  4001. }
  4002. wxSize wxGenericListCtrl::GetItemSpacing() const
  4003. {
  4004. const int spacing = m_mainWin->GetItemSpacing(HasFlag(wxLC_SMALL_ICON));
  4005. return wxSize(spacing, spacing);
  4006. }
  4007. #if WXWIN_COMPATIBILITY_2_6
  4008. int wxGenericListCtrl::GetItemSpacing( bool isSmall ) const
  4009. {
  4010. return m_mainWin->GetItemSpacing( isSmall );
  4011. }
  4012. #endif // WXWIN_COMPATIBILITY_2_6
  4013. void wxGenericListCtrl::SetItemTextColour( long item, const wxColour &col )
  4014. {
  4015. wxListItem info;
  4016. info.m_itemId = item;
  4017. info.SetTextColour( col );
  4018. m_mainWin->SetItem( info );
  4019. }
  4020. wxColour wxGenericListCtrl::GetItemTextColour( long item ) const
  4021. {
  4022. wxListItem info;
  4023. info.m_itemId = item;
  4024. m_mainWin->GetItem( info );
  4025. return info.GetTextColour();
  4026. }
  4027. void wxGenericListCtrl::SetItemBackgroundColour( long item, const wxColour &col )
  4028. {
  4029. wxListItem info;
  4030. info.m_itemId = item;
  4031. info.SetBackgroundColour( col );
  4032. m_mainWin->SetItem( info );
  4033. }
  4034. wxColour wxGenericListCtrl::GetItemBackgroundColour( long item ) const
  4035. {
  4036. wxListItem info;
  4037. info.m_itemId = item;
  4038. m_mainWin->GetItem( info );
  4039. return info.GetBackgroundColour();
  4040. }
  4041. void wxGenericListCtrl::SetItemFont( long item, const wxFont &f )
  4042. {
  4043. wxListItem info;
  4044. info.m_itemId = item;
  4045. info.SetFont( f );
  4046. m_mainWin->SetItem( info );
  4047. }
  4048. wxFont wxGenericListCtrl::GetItemFont( long item ) const
  4049. {
  4050. wxListItem info;
  4051. info.m_itemId = item;
  4052. m_mainWin->GetItem( info );
  4053. return info.GetFont();
  4054. }
  4055. int wxGenericListCtrl::GetSelectedItemCount() const
  4056. {
  4057. return m_mainWin->GetSelectedItemCount();
  4058. }
  4059. wxColour wxGenericListCtrl::GetTextColour() const
  4060. {
  4061. return GetForegroundColour();
  4062. }
  4063. void wxGenericListCtrl::SetTextColour(const wxColour& col)
  4064. {
  4065. SetForegroundColour(col);
  4066. }
  4067. long wxGenericListCtrl::GetTopItem() const
  4068. {
  4069. size_t top;
  4070. m_mainWin->GetVisibleLinesRange(&top, NULL);
  4071. return (long)top;
  4072. }
  4073. long wxGenericListCtrl::GetNextItem( long item, int geom, int state ) const
  4074. {
  4075. return m_mainWin->GetNextItem( item, geom, state );
  4076. }
  4077. wxImageList *wxGenericListCtrl::GetImageList(int which) const
  4078. {
  4079. if (which == wxIMAGE_LIST_NORMAL)
  4080. return m_imageListNormal;
  4081. else if (which == wxIMAGE_LIST_SMALL)
  4082. return m_imageListSmall;
  4083. else if (which == wxIMAGE_LIST_STATE)
  4084. return m_imageListState;
  4085. return NULL;
  4086. }
  4087. void wxGenericListCtrl::SetImageList( wxImageList *imageList, int which )
  4088. {
  4089. if ( which == wxIMAGE_LIST_NORMAL )
  4090. {
  4091. if (m_ownsImageListNormal)
  4092. delete m_imageListNormal;
  4093. m_imageListNormal = imageList;
  4094. m_ownsImageListNormal = false;
  4095. }
  4096. else if ( which == wxIMAGE_LIST_SMALL )
  4097. {
  4098. if (m_ownsImageListSmall)
  4099. delete m_imageListSmall;
  4100. m_imageListSmall = imageList;
  4101. m_ownsImageListSmall = false;
  4102. }
  4103. else if ( which == wxIMAGE_LIST_STATE )
  4104. {
  4105. if (m_ownsImageListState)
  4106. delete m_imageListState;
  4107. m_imageListState = imageList;
  4108. m_ownsImageListState = false;
  4109. }
  4110. m_mainWin->SetImageList( imageList, which );
  4111. }
  4112. void wxGenericListCtrl::AssignImageList(wxImageList *imageList, int which)
  4113. {
  4114. SetImageList(imageList, which);
  4115. if ( which == wxIMAGE_LIST_NORMAL )
  4116. m_ownsImageListNormal = true;
  4117. else if ( which == wxIMAGE_LIST_SMALL )
  4118. m_ownsImageListSmall = true;
  4119. else if ( which == wxIMAGE_LIST_STATE )
  4120. m_ownsImageListState = true;
  4121. }
  4122. bool wxGenericListCtrl::Arrange( int WXUNUSED(flag) )
  4123. {
  4124. return 0;
  4125. }
  4126. bool wxGenericListCtrl::DeleteItem( long item )
  4127. {
  4128. m_mainWin->DeleteItem( item );
  4129. return true;
  4130. }
  4131. bool wxGenericListCtrl::DeleteAllItems()
  4132. {
  4133. m_mainWin->DeleteAllItems();
  4134. return true;
  4135. }
  4136. bool wxGenericListCtrl::DeleteAllColumns()
  4137. {
  4138. size_t count = m_mainWin->m_columns.GetCount();
  4139. for ( size_t n = 0; n < count; n++ )
  4140. DeleteColumn( 0 );
  4141. return true;
  4142. }
  4143. void wxGenericListCtrl::ClearAll()
  4144. {
  4145. m_mainWin->DeleteEverything();
  4146. }
  4147. bool wxGenericListCtrl::DeleteColumn( int col )
  4148. {
  4149. m_mainWin->DeleteColumn( col );
  4150. // if we don't have the header any longer, we need to relayout the window
  4151. // if ( !GetColumnCount() )
  4152. // Ensure that the non-existent columns are really removed from display.
  4153. Refresh();
  4154. return true;
  4155. }
  4156. wxTextCtrl *wxGenericListCtrl::EditLabel(long item,
  4157. wxClassInfo* textControlClass)
  4158. {
  4159. return m_mainWin->EditLabel( item, textControlClass );
  4160. }
  4161. wxTextCtrl *wxGenericListCtrl::GetEditControl() const
  4162. {
  4163. return m_mainWin->GetEditControl();
  4164. }
  4165. bool wxGenericListCtrl::EnsureVisible( long item )
  4166. {
  4167. m_mainWin->EnsureVisible( item );
  4168. return true;
  4169. }
  4170. long wxGenericListCtrl::FindItem( long start, const wxString& str, bool partial )
  4171. {
  4172. return m_mainWin->FindItem( start, str, partial );
  4173. }
  4174. long wxGenericListCtrl::FindItem( long start, wxUIntPtr data )
  4175. {
  4176. return m_mainWin->FindItem( start, data );
  4177. }
  4178. long wxGenericListCtrl::FindItem( long WXUNUSED(start), const wxPoint& pt,
  4179. int WXUNUSED(direction))
  4180. {
  4181. return m_mainWin->FindItem( pt );
  4182. }
  4183. // TODO: sub item hit testing
  4184. long wxGenericListCtrl::HitTest(const wxPoint& point, int& flags, long *) const
  4185. {
  4186. return m_mainWin->HitTest( (int)point.x, (int)point.y, flags );
  4187. }
  4188. long wxGenericListCtrl::InsertItem( wxListItem& info )
  4189. {
  4190. m_mainWin->InsertItem( info );
  4191. return info.m_itemId;
  4192. }
  4193. long wxGenericListCtrl::InsertItem( long index, const wxString &label )
  4194. {
  4195. wxListItem info;
  4196. info.m_text = label;
  4197. info.m_mask = wxLIST_MASK_TEXT;
  4198. info.m_itemId = index;
  4199. return InsertItem( info );
  4200. }
  4201. long wxGenericListCtrl::InsertItem( long index, int imageIndex )
  4202. {
  4203. wxListItem info;
  4204. info.m_mask = wxLIST_MASK_IMAGE;
  4205. info.m_image = imageIndex;
  4206. info.m_itemId = index;
  4207. return InsertItem( info );
  4208. }
  4209. long wxGenericListCtrl::InsertItem( long index, const wxString &label, int imageIndex )
  4210. {
  4211. wxListItem info;
  4212. info.m_text = label;
  4213. info.m_image = imageIndex;
  4214. info.m_mask = wxLIST_MASK_TEXT;
  4215. if (imageIndex > -1)
  4216. info.m_mask |= wxLIST_MASK_IMAGE;
  4217. info.m_itemId = index;
  4218. return InsertItem( info );
  4219. }
  4220. long wxGenericListCtrl::DoInsertColumn( long col, const wxListItem &item )
  4221. {
  4222. wxCHECK_MSG( InReportView(), -1, wxT("can't add column in non report mode") );
  4223. long idx = m_mainWin->InsertColumn( col, item );
  4224. // NOTE: if wxLC_NO_HEADER was given, then we are in report view mode but
  4225. // still have m_headerWin==NULL
  4226. if (m_headerWin)
  4227. m_headerWin->Refresh();
  4228. return idx;
  4229. }
  4230. bool wxGenericListCtrl::ScrollList( int dx, int dy )
  4231. {
  4232. return m_mainWin->ScrollList(dx, dy);
  4233. }
  4234. // Sort items.
  4235. // fn is a function which takes 3 long arguments: item1, item2, data.
  4236. // item1 is the long data associated with a first item (NOT the index).
  4237. // item2 is the long data associated with a second item (NOT the index).
  4238. // data is the same value as passed to SortItems.
  4239. // The return value is a negative number if the first item should precede the second
  4240. // item, a positive number of the second item should precede the first,
  4241. // or zero if the two items are equivalent.
  4242. // data is arbitrary data to be passed to the sort function.
  4243. bool wxGenericListCtrl::SortItems( wxListCtrlCompare fn, wxIntPtr data )
  4244. {
  4245. m_mainWin->SortItems( fn, data );
  4246. return true;
  4247. }
  4248. // ----------------------------------------------------------------------------
  4249. // event handlers
  4250. // ----------------------------------------------------------------------------
  4251. void wxGenericListCtrl::OnSize(wxSizeEvent& WXUNUSED(event))
  4252. {
  4253. if (!m_mainWin) return;
  4254. // We need to override OnSize so that our scrolled
  4255. // window a) does call Layout() to use sizers for
  4256. // positioning the controls but b) does not query
  4257. // the sizer for their size and use that for setting
  4258. // the scrollable area as set that ourselves by
  4259. // calling SetScrollbar() further down.
  4260. Layout();
  4261. m_mainWin->RecalculatePositions();
  4262. AdjustScrollbars();
  4263. }
  4264. void wxGenericListCtrl::OnInternalIdle()
  4265. {
  4266. wxWindow::OnInternalIdle();
  4267. if (m_mainWin->m_dirty)
  4268. m_mainWin->RecalculatePositions();
  4269. }
  4270. // ----------------------------------------------------------------------------
  4271. // font/colours
  4272. // ----------------------------------------------------------------------------
  4273. bool wxGenericListCtrl::SetBackgroundColour( const wxColour &colour )
  4274. {
  4275. if (m_mainWin)
  4276. {
  4277. m_mainWin->SetBackgroundColour( colour );
  4278. m_mainWin->m_dirty = true;
  4279. }
  4280. return true;
  4281. }
  4282. bool wxGenericListCtrl::SetForegroundColour( const wxColour &colour )
  4283. {
  4284. if ( !wxWindow::SetForegroundColour( colour ) )
  4285. return false;
  4286. if (m_mainWin)
  4287. {
  4288. m_mainWin->SetForegroundColour( colour );
  4289. m_mainWin->m_dirty = true;
  4290. }
  4291. return true;
  4292. }
  4293. bool wxGenericListCtrl::SetFont( const wxFont &font )
  4294. {
  4295. if ( !wxWindow::SetFont( font ) )
  4296. return false;
  4297. if (m_mainWin)
  4298. {
  4299. m_mainWin->SetFont( font );
  4300. m_mainWin->m_dirty = true;
  4301. }
  4302. if (m_headerWin)
  4303. {
  4304. m_headerWin->SetFont( font );
  4305. // CalculateAndSetHeaderHeight();
  4306. }
  4307. Refresh();
  4308. return true;
  4309. }
  4310. // static
  4311. wxVisualAttributes
  4312. wxGenericListCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
  4313. {
  4314. #if _USE_VISATTR
  4315. // Use the same color scheme as wxListBox
  4316. return wxListBox::GetClassDefaultAttributes(variant);
  4317. #else
  4318. wxUnusedVar(variant);
  4319. wxVisualAttributes attr;
  4320. attr.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT);
  4321. attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX);
  4322. attr.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
  4323. return attr;
  4324. #endif
  4325. }
  4326. // ----------------------------------------------------------------------------
  4327. // methods forwarded to m_mainWin
  4328. // ----------------------------------------------------------------------------
  4329. #if wxUSE_DRAG_AND_DROP
  4330. void wxGenericListCtrl::SetDropTarget( wxDropTarget *dropTarget )
  4331. {
  4332. m_mainWin->SetDropTarget( dropTarget );
  4333. }
  4334. wxDropTarget *wxGenericListCtrl::GetDropTarget() const
  4335. {
  4336. return m_mainWin->GetDropTarget();
  4337. }
  4338. #endif
  4339. bool wxGenericListCtrl::SetCursor( const wxCursor &cursor )
  4340. {
  4341. return m_mainWin ? m_mainWin->wxWindow::SetCursor(cursor) : false;
  4342. }
  4343. wxColour wxGenericListCtrl::GetBackgroundColour() const
  4344. {
  4345. return m_mainWin ? m_mainWin->GetBackgroundColour() : wxColour();
  4346. }
  4347. wxColour wxGenericListCtrl::GetForegroundColour() const
  4348. {
  4349. return m_mainWin ? m_mainWin->GetForegroundColour() : wxColour();
  4350. }
  4351. bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y )
  4352. {
  4353. #if wxUSE_MENUS
  4354. return m_mainWin->PopupMenu( menu, x, y );
  4355. #else
  4356. return false;
  4357. #endif
  4358. }
  4359. wxSize wxGenericListCtrl::DoGetBestClientSize() const
  4360. {
  4361. // The base class version can compute the best size in report view only.
  4362. wxSize sizeBest = wxListCtrlBase::DoGetBestClientSize();
  4363. if ( !InReportView() )
  4364. {
  4365. // Ensure that our minimal width is at least big enough to show all our
  4366. // items. This is important for wxListbook to size itself correctly.
  4367. // Remember the offset of the first item: this corresponds to the
  4368. // margins around the item so we will add it to the minimal size below
  4369. // to ensure that we have equal margins on all sides.
  4370. wxPoint ofs;
  4371. // We can iterate over all items as there shouldn't be too many of them
  4372. // in non-report view. If it ever becomes a problem, we could examine
  4373. // just the first few items probably, the determination of the best
  4374. // size is less important if we will need scrollbars anyhow.
  4375. for ( int n = 0; n < GetItemCount(); n++ )
  4376. {
  4377. const wxRect itemRect = m_mainWin->GetLineRect(n);
  4378. if ( !n )
  4379. {
  4380. // Remember the position of the first item as all the rest are
  4381. // offset by at least this number of pixels too.
  4382. ofs = itemRect.GetPosition();
  4383. }
  4384. sizeBest.IncTo(itemRect.GetSize());
  4385. }
  4386. sizeBest.IncBy(2*ofs);
  4387. // If we have the scrollbars we need to account for them too. And to
  4388. // make sure the scrollbars status is up to date we need to call this
  4389. // function to set them.
  4390. m_mainWin->RecalculatePositions(true /* no refresh */);
  4391. // Unfortunately we can't use wxWindow::HasScrollbar() here as we need
  4392. // to use m_mainWin client/virtual size for determination of whether we
  4393. // use scrollbars and not the size of this window itself. Maybe that
  4394. // function should be extended to work correctly in the case when our
  4395. // scrollbars manage a different window from this one but currently it
  4396. // doesn't work.
  4397. const wxSize sizeClient = m_mainWin->GetClientSize();
  4398. const wxSize sizeVirt = m_mainWin->GetVirtualSize();
  4399. if ( sizeVirt.x > sizeClient.x /* HasScrollbar(wxHORIZONTAL) */ )
  4400. sizeBest.y += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
  4401. if ( sizeVirt.y > sizeClient.y /* HasScrollbar(wxVERTICAL) */ )
  4402. sizeBest.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
  4403. }
  4404. return sizeBest;
  4405. }
  4406. // ----------------------------------------------------------------------------
  4407. // virtual list control support
  4408. // ----------------------------------------------------------------------------
  4409. wxString wxGenericListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) const
  4410. {
  4411. // this is a pure virtual function, in fact - which is not really pure
  4412. // because the controls which are not virtual don't need to implement it
  4413. wxFAIL_MSG( wxT("wxGenericListCtrl::OnGetItemText not supposed to be called") );
  4414. return wxEmptyString;
  4415. }
  4416. int wxGenericListCtrl::OnGetItemImage(long WXUNUSED(item)) const
  4417. {
  4418. wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL),
  4419. -1,
  4420. wxT("List control has an image list, OnGetItemImage or OnGetItemColumnImage should be overridden."));
  4421. return -1;
  4422. }
  4423. int wxGenericListCtrl::OnGetItemColumnImage(long item, long column) const
  4424. {
  4425. if (!column)
  4426. return OnGetItemImage(item);
  4427. return -1;
  4428. }
  4429. void wxGenericListCtrl::SetItemCount(long count)
  4430. {
  4431. wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") );
  4432. m_mainWin->SetItemCount(count);
  4433. }
  4434. void wxGenericListCtrl::RefreshItem(long item)
  4435. {
  4436. m_mainWin->RefreshLine(item);
  4437. }
  4438. void wxGenericListCtrl::RefreshItems(long itemFrom, long itemTo)
  4439. {
  4440. m_mainWin->RefreshLines(itemFrom, itemTo);
  4441. }
  4442. void wxGenericListCtrl::EnableBellOnNoMatch( bool on )
  4443. {
  4444. m_mainWin->EnableBellOnNoMatch(on);
  4445. }
  4446. // Generic wxListCtrl is more or less a container for two other
  4447. // windows which drawings are done upon. These are namely
  4448. // 'm_headerWin' and 'm_mainWin'.
  4449. // Here we override 'virtual wxWindow::Refresh()' to mimic the
  4450. // behaviour wxListCtrl has under wxMSW.
  4451. //
  4452. void wxGenericListCtrl::Refresh(bool eraseBackground, const wxRect *rect)
  4453. {
  4454. if (!rect)
  4455. {
  4456. // The easy case, no rectangle specified.
  4457. if (m_headerWin)
  4458. m_headerWin->Refresh(eraseBackground);
  4459. if (m_mainWin)
  4460. m_mainWin->Refresh(eraseBackground);
  4461. }
  4462. else
  4463. {
  4464. // Refresh the header window
  4465. if (m_headerWin)
  4466. {
  4467. wxRect rectHeader = m_headerWin->GetRect();
  4468. rectHeader.Intersect(*rect);
  4469. if (rectHeader.GetWidth() && rectHeader.GetHeight())
  4470. {
  4471. int x, y;
  4472. m_headerWin->GetPosition(&x, &y);
  4473. rectHeader.Offset(-x, -y);
  4474. m_headerWin->Refresh(eraseBackground, &rectHeader);
  4475. }
  4476. }
  4477. // Refresh the main window
  4478. if (m_mainWin)
  4479. {
  4480. wxRect rectMain = m_mainWin->GetRect();
  4481. rectMain.Intersect(*rect);
  4482. if (rectMain.GetWidth() && rectMain.GetHeight())
  4483. {
  4484. int x, y;
  4485. m_mainWin->GetPosition(&x, &y);
  4486. rectMain.Offset(-x, -y);
  4487. m_mainWin->Refresh(eraseBackground, &rectMain);
  4488. }
  4489. }
  4490. }
  4491. }
  4492. void wxGenericListCtrl::Update()
  4493. {
  4494. if ( m_mainWin )
  4495. {
  4496. if ( m_mainWin->m_dirty )
  4497. m_mainWin->RecalculatePositions();
  4498. m_mainWin->Update();
  4499. }
  4500. if ( m_headerWin )
  4501. m_headerWin->Update();
  4502. }
  4503. #endif // wxUSE_LISTCTRL