You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

310 lines
9.1 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include "widgets/listbox_tricks.h"
  24. #include <wx/clipbrd.h>
  25. #include <wx/listbox.h>
  26. #include <wx/menu.h>
  27. #include <wx/window.h>
  28. #include <bitmaps.h>
  29. #include <bitmaps/bitmaps_list.h>
  30. #include <widgets/ui_common.h>
  31. wxDEFINE_EVENT( EDA_EVT_LISTBOX_COPY, wxCommandEvent );
  32. wxDEFINE_EVENT( EDA_EVT_LISTBOX_CUT, wxCommandEvent );
  33. wxDEFINE_EVENT( EDA_EVT_LISTBOX_PASTE, wxCommandEvent );
  34. wxDEFINE_EVENT( EDA_EVT_LISTBOX_DELETE, wxCommandEvent );
  35. wxDEFINE_EVENT( EDA_EVT_LISTBOX_DUPLICATE, wxCommandEvent );
  36. wxDEFINE_EVENT( EDA_EVT_LISTBOX_CHANGED, wxCommandEvent );
  37. LISTBOX_TRICKS::LISTBOX_TRICKS( wxWindow& aParent, wxListBox& aListBox ) :
  38. m_parent( aParent ), m_listBox( aListBox )
  39. {
  40. // Init default menu labels
  41. m_menuStrings = { {
  42. { ID_COPY, _( "Copy" ) },
  43. { ID_PASTE, _( "Paste" ) },
  44. { ID_CUT, _( "Cut" ) },
  45. { ID_DUPLICATE, _( "Duplicate" ) },
  46. { ID_DELETE, _( "Delete" ) },
  47. } };
  48. m_listBox.Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( LISTBOX_TRICKS::OnListBoxRDown ),
  49. nullptr, this );
  50. m_listBox.Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( LISTBOX_TRICKS::OnListBoxKeyDown ),
  51. nullptr, this );
  52. Connect( EDA_EVT_LISTBOX_DELETE, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxDelete ) );
  53. Connect( EDA_EVT_LISTBOX_COPY, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxCopy ) );
  54. Connect( EDA_EVT_LISTBOX_CUT, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxCut ) );
  55. Connect( EDA_EVT_LISTBOX_PASTE, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxPaste ) );
  56. Connect( EDA_EVT_LISTBOX_DUPLICATE, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxDuplicate ) );
  57. }
  58. LISTBOX_TRICKS::~LISTBOX_TRICKS()
  59. {
  60. m_listBox.Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( LISTBOX_TRICKS::OnListBoxRDown ),
  61. nullptr, this );
  62. m_listBox.Disconnect( wxEVT_KEY_DOWN, wxKeyEventHandler( LISTBOX_TRICKS::OnListBoxKeyDown ),
  63. nullptr, this );
  64. Disconnect( EDA_EVT_LISTBOX_DELETE, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxDelete ) );
  65. Disconnect( EDA_EVT_LISTBOX_COPY, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxCopy ) );
  66. Disconnect( EDA_EVT_LISTBOX_CUT, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxCut ) );
  67. Disconnect( EDA_EVT_LISTBOX_PASTE, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxPaste ) );
  68. Disconnect( EDA_EVT_LISTBOX_DUPLICATE, wxCommandEventHandler( LISTBOX_TRICKS::OnListBoxDuplicate ) );
  69. }
  70. void LISTBOX_TRICKS::SetMenuLabels( const std::map<MENU_ID, wxString>& aItems )
  71. {
  72. for( const auto& [id, string] : aItems )
  73. {
  74. m_menuStrings[id] = string;
  75. }
  76. }
  77. wxArrayInt LISTBOX_TRICKS::listBoxDeleteSelected()
  78. {
  79. wxArrayInt selections;
  80. m_listBox.GetSelections( selections );
  81. std::sort( selections.begin(), selections.end() );
  82. for( int ii = selections.GetCount() - 1; ii >= 0; ii-- )
  83. m_listBox.Delete( selections[ii] );
  84. m_listBox.SetSelection( wxNOT_FOUND );
  85. if( m_listBox.GetCount() > 0 )
  86. m_listBox.SetSelection( std::max( 0, selections[0] - 1 ) );
  87. wxPostEvent( &m_listBox, wxCommandEvent( EDA_EVT_LISTBOX_CHANGED ) );
  88. return selections;
  89. }
  90. wxArrayString LISTBOX_TRICKS::listBoxGetSelected() const
  91. {
  92. wxArrayInt selections;
  93. m_listBox.GetSelections( selections );
  94. wxArrayString result;
  95. for( size_t ii = 0; ii < selections.GetCount(); ii++ )
  96. result.Add( m_listBox.GetString( selections[ii] ) );
  97. return result;
  98. }
  99. void LISTBOX_TRICKS::listBoxDuplicateSelected()
  100. {
  101. wxArrayInt selections;
  102. m_listBox.GetSelections( selections );
  103. int insertAt = selections.GetCount() > 0 ? selections.back() + 1 : m_listBox.GetCount();
  104. m_listBox.SetSelection( wxNOT_FOUND );
  105. for( size_t ii = 0; ii < selections.GetCount(); ii++ )
  106. {
  107. wxString filter = m_listBox.GetString( selections[ii] );
  108. m_listBox.Insert( filter, insertAt );
  109. m_listBox.SetSelection( insertAt );
  110. insertAt++;
  111. }
  112. wxPostEvent( &m_listBox, wxCommandEvent( EDA_EVT_LISTBOX_CHANGED ) );
  113. }
  114. void LISTBOX_TRICKS::listBoxCopy()
  115. {
  116. wxArrayString filters = listBoxGetSelected();
  117. wxString result;
  118. for( const wxString& filter : filters )
  119. {
  120. result += filter + wxT( "\n" );
  121. }
  122. if( wxTheClipboard->Open() )
  123. {
  124. wxTheClipboard->SetData( new wxTextDataObject( result ) );
  125. wxTheClipboard->Close();
  126. }
  127. }
  128. void LISTBOX_TRICKS::listBoxPaste()
  129. {
  130. wxArrayString lines;
  131. if( wxTheClipboard->Open() )
  132. {
  133. wxTextDataObject data;
  134. wxTheClipboard->GetData( data );
  135. wxString text = data.GetText();
  136. text.Trim( false );
  137. text.Trim( true );
  138. lines = wxSplit( text, '\n' );
  139. wxTheClipboard->Close();
  140. }
  141. wxArrayInt selections;
  142. m_listBox.GetSelections( selections );
  143. int insertAt = selections.GetCount() > 0 ? selections.back() + 1 : m_listBox.GetCount();
  144. for( wxString& line : lines )
  145. {
  146. line.Trim( false );
  147. line.Trim( true );
  148. }
  149. m_listBox.InsertItems( lines, insertAt );
  150. m_listBox.SetSelection( wxNOT_FOUND );
  151. for( size_t ii = insertAt; ii < insertAt + lines.GetCount(); ii++ )
  152. m_listBox.SetSelection( ii );
  153. wxPostEvent( &m_listBox, wxCommandEvent( EDA_EVT_LISTBOX_CHANGED ) );
  154. }
  155. void LISTBOX_TRICKS::listBoxCut()
  156. {
  157. listBoxCopy();
  158. wxArrayInt deleted = listBoxDeleteSelected();
  159. size_t select = deleted.GetCount() > 0 ? deleted[0] : m_listBox.GetCount();
  160. m_listBox.SetSelection( wxNOT_FOUND );
  161. m_listBox.SetSelection( std::min( select, (size_t) m_listBox.GetCount() - 1 ) );
  162. }
  163. void LISTBOX_TRICKS::OnListBoxRDown( wxMouseEvent& aEvent )
  164. {
  165. wxMenu menu;
  166. const auto mstr = [&]( const MENU_ID& id )
  167. {
  168. return m_menuStrings[id];
  169. };
  170. // clang-format off
  171. KIUI::AddMenuItem( &menu, ID_COPY,
  172. mstr( ID_COPY ) + "\tCtrl+C",
  173. KiBitmap( BITMAPS::copy ) );
  174. KIUI::AddMenuItem( &menu, ID_CUT,
  175. mstr( ID_CUT ) + "\tCtrl+X",
  176. KiBitmap( BITMAPS::cut ) );
  177. KIUI::AddMenuItem( &menu, ID_PASTE,
  178. mstr( ID_PASTE ) + "\tCtrl+V",
  179. KiBitmap( BITMAPS::paste ) );
  180. KIUI::AddMenuItem( &menu, ID_DUPLICATE,
  181. mstr( ID_DUPLICATE ) + "\tCtrl+D",
  182. KiBitmap( BITMAPS::duplicate ) );
  183. KIUI::AddMenuItem( &menu, ID_DELETE,
  184. mstr( ID_DELETE ) + "\tDel",
  185. KiBitmap( BITMAPS::trash ) );
  186. // clang-format on
  187. menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
  188. [&]( wxCommandEvent& aCmd )
  189. {
  190. switch( aEvent.GetId() )
  191. {
  192. case ID_COPY: listBoxCopy(); break;
  193. case ID_PASTE: listBoxPaste(); break;
  194. case ID_CUT: listBoxCut(); break;
  195. case ID_DELETE: listBoxDeleteSelected(); break;
  196. case ID_DUPLICATE: listBoxDuplicateSelected(); break;
  197. default: aEvent.Skip();
  198. }
  199. } );
  200. m_parent.PopupMenu( &menu );
  201. }
  202. void LISTBOX_TRICKS::OnListBoxKeyDown( wxKeyEvent& aEvent )
  203. {
  204. if( aEvent.GetKeyCode() == WXK_DELETE )
  205. {
  206. listBoxDeleteSelected();
  207. }
  208. else
  209. {
  210. if( aEvent.ControlDown() )
  211. {
  212. switch( aEvent.GetKeyCode() )
  213. {
  214. case 'C': listBoxCopy(); break;
  215. case 'V': listBoxPaste(); break;
  216. case 'X': listBoxCut(); break;
  217. case 'D': listBoxDuplicateSelected(); break;
  218. default: aEvent.Skip();
  219. }
  220. }
  221. else
  222. aEvent.Skip();
  223. }
  224. }
  225. void LISTBOX_TRICKS::OnListBoxDelete( wxCommandEvent& aEvent )
  226. {
  227. listBoxDeleteSelected();
  228. }
  229. void LISTBOX_TRICKS::OnListBoxCopy( wxCommandEvent& aEvent )
  230. {
  231. listBoxCopy();
  232. }
  233. void LISTBOX_TRICKS::OnListBoxCut( wxCommandEvent& aEvent )
  234. {
  235. listBoxCut();
  236. }
  237. void LISTBOX_TRICKS::OnListBoxPaste( wxCommandEvent& aEvent )
  238. {
  239. listBoxPaste();
  240. }
  241. void LISTBOX_TRICKS::OnListBoxDuplicate( wxCommandEvent& aEvent )
  242. {
  243. listBoxDuplicateSelected();
  244. }