hydrus/include/ClientGUIListCtrl.py

1204 lines
33 KiB
Python
Raw Normal View History

2017-08-02 21:32:54 +00:00
import ClientConstants as CC
import ClientData
import ClientGUICommon
2017-11-29 21:48:23 +00:00
import ClientSerialisable
import HydrusData
2017-08-02 21:32:54 +00:00
import HydrusExceptions
2017-11-29 21:48:23 +00:00
import HydrusGlobals as HG
import HydrusSerialisable
import os
2017-08-02 21:32:54 +00:00
import wx
from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
2017-10-25 21:45:15 +00:00
def SetNonDupeName( obj, disallowed_names ):
i = 1
original_name = obj.GetName()
new_name = original_name
while new_name in disallowed_names:
new_name = original_name + ' (' + str( i ) + ')'
i += 1
obj.SetName( new_name )
2017-12-06 22:06:56 +00:00
# This used to be ColumnSorterMixin, but it was crashing on sort-click on clients with many pages open
# I've disabled it for now because it was still catching people. The transition to BetterListCtrl will nuke the whole thing eventually.
class SaneListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin ):
2017-08-02 21:32:54 +00:00
def __init__( self, parent, height, columns, delete_key_callback = None, activation_callback = None ):
num_columns = len( columns )
wx.ListCtrl.__init__( self, parent, style = wx.LC_REPORT )
ListCtrlAutoWidthMixin.__init__( self )
self.itemDataMap = {}
self._data_indices_to_sort_indices = {}
self._data_indices_to_sort_indices_dirty = False
self._next_data_index = 0
resize_column = 1
for ( i, ( name, width ) ) in enumerate( columns ):
self.InsertColumn( i, name, width = width )
if width == -1:
resize_column = i + 1
self.setResizeColumn( resize_column )
self.SetMinSize( ( -1, height ) )
self._delete_key_callback = delete_key_callback
self._activation_callback = activation_callback
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.EventItemActivated )
self.Bind( wx.EVT_LIST_COL_BEGIN_DRAG, self.EventBeginColDrag )
_GetDataIndex = wx.ListCtrl.GetItemData
def _GetIndexFromDataIndex( self, data_index ):
if self._data_indices_to_sort_indices_dirty:
self._data_indices_to_sort_indices = { self._GetDataIndex( index ) : index for index in range( self.GetItemCount() ) }
self._data_indices_to_sort_indices_dirty = False
try:
return self._data_indices_to_sort_indices[ data_index ]
except KeyError:
raise HydrusExceptions.DataMissing( 'Data not found!' )
def Append( self, display_tuple, sort_tuple ):
index = wx.ListCtrl.Append( self, display_tuple )
data_index = self._next_data_index
self.SetItemData( index, data_index )
self.itemDataMap[ data_index ] = list( sort_tuple )
self._data_indices_to_sort_indices[ data_index ] = index
self._next_data_index += 1
def DeleteItem( self, *args, **kwargs ):
wx.ListCtrl.DeleteItem( self, *args, **kwargs )
self._data_indices_to_sort_indices_dirty = True
def EventBeginColDrag( self, event ):
# resizeCol is not zero-indexed
if event.GetColumn() == self._resizeCol - 1:
last_column = self.GetColumnCount()
if self._resizeCol != last_column:
self.setResizeColumn( last_column )
else:
event.Veto()
return
event.Skip()
def EventItemActivated( self, event ):
if self._activation_callback is not None:
self._activation_callback()
else:
event.Skip()
def EventKeyDown( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
if key in CC.DELETE_KEYS:
if self._delete_key_callback is not None:
self._delete_key_callback()
elif key in ( ord( 'A' ), ord( 'a' ) ) and modifier == wx.ACCEL_CTRL:
self.SelectAll()
else:
event.Skip()
def GetAllSelected( self ):
indices = []
i = self.GetFirstSelected()
while i != -1:
indices.append( i )
i = self.GetNextSelected( i )
return indices
def GetClientData( self, index = None ):
if index is None:
data_indicies = [ self._GetDataIndex( index ) for index in range( self.GetItemCount() ) ]
datas = [ tuple( self.itemDataMap[ data_index ] ) for data_index in data_indicies ]
return datas
else:
data_index = self._GetDataIndex( index )
return tuple( self.itemDataMap[ data_index ] )
def GetIndexFromClientData( self, data, column_index = None ):
for index in range( self.GetItemCount() ):
client_data = self.GetClientData( index )
if column_index is None:
comparison_data = client_data
else:
comparison_data = client_data[ column_index ]
if comparison_data == data:
return index
raise HydrusExceptions.DataMissing( 'Data not found!' )
def GetSecondarySortValues( self, col, key1, key2 ):
# This overrides the ColumnSortedMixin. Just spam the whole tuple back.
return ( self.itemDataMap[ key1 ], self.itemDataMap[ key2 ] )
def GetListCtrl( self ):
return self
def GetSelectedClientData( self ):
indices = self.GetAllSelected()
results = []
for index in indices:
results.append( self.GetClientData( index ) )
return results
def HasClientData( self, data, column_index = None ):
try:
index = self.GetIndexFromClientData( data, column_index )
return True
except HydrusExceptions.DataMissing:
return False
def OnSortOrderChanged( self ):
self._data_indices_to_sort_indices_dirty = True
def RemoveAllSelected( self ):
indices = self.GetAllSelected()
self.RemoveIndices( indices )
def RemoveIndices( self, indices ):
indices.sort()
indices.reverse() # so we don't screw with the indices of deletees below
for index in indices:
self.DeleteItem( index )
def SelectAll( self ):
currently_selected = set( self.GetAllSelected() )
currently_not_selected = [ index for index in range( self.GetItemCount() ) if index not in currently_selected ]
for index in currently_not_selected:
self.Select( index )
def UpdateRow( self, index, display_tuple, sort_tuple ):
column = 0
for value in display_tuple:
self.SetStringItem( index, column, value )
column += 1
data_index = self._GetDataIndex( index )
self.itemDataMap[ data_index ] = list( sort_tuple )
class SaneListCtrlForSingleObject( SaneListCtrl ):
def __init__( self, *args, **kwargs ):
# this could one day just take column parameters that the user can pick
# it could just take obj in append or whatever and generate column tuples off that
self._data_indices_to_objects = {}
self._objects_to_data_indices = {}
SaneListCtrl.__init__( self, *args, **kwargs )
def Append( self, display_tuple, sort_tuple, obj ):
self._data_indices_to_objects[ self._next_data_index ] = obj
self._objects_to_data_indices[ obj ] = self._next_data_index
SaneListCtrl.Append( self, display_tuple, sort_tuple )
def GetIndexFromObject( self, obj ):
try:
data_index = self._objects_to_data_indices[ obj ]
index = self._GetIndexFromDataIndex( data_index )
return index
except KeyError:
raise HydrusExceptions.DataMissing( 'Data not found!' )
def GetObject( self, index ):
data_index = self._GetDataIndex( index )
return self._data_indices_to_objects[ data_index ]
def GetObjects( self, only_selected = False ):
if only_selected:
indicies = self.GetAllSelected()
else:
indicies = range( self.GetItemCount() )
data_indicies = [ self._GetDataIndex( index ) for index in indicies ]
datas = [ self._data_indices_to_objects[ data_index ] for data_index in data_indicies ]
return datas
def HasObject( self, obj ):
try:
index = self.GetIndexFromObject( obj )
return True
except HydrusExceptions.DataMissing:
return False
def SetNonDupeName( self, obj ):
# when column population is handled here, we can tuck this into normal append/update calls internally
name = obj.GetName()
2017-09-27 21:52:54 +00:00
current_names = { o.GetName() for o in self.GetObjects() if o is not obj }
2017-08-02 21:32:54 +00:00
if name in current_names:
i = 1
original_name = name
while name in current_names:
name = original_name + ' (' + str( i ) + ')'
i += 1
obj.SetName( name )
def UpdateRow( self, index, display_tuple, sort_tuple, obj ):
SaneListCtrl.UpdateRow( self, index, display_tuple, sort_tuple )
data_index = self._GetDataIndex( index )
self._data_indices_to_objects[ data_index ] = obj
self._objects_to_data_indices[ obj ] = data_index
class SaneListCtrlPanel( wx.Panel ):
def __init__( self, parent ):
wx.Panel.__init__( self, parent )
self._vbox = wx.BoxSizer( wx.VERTICAL )
self._buttonbox = wx.BoxSizer( wx.HORIZONTAL )
self._listctrl = None
self._button_infos = []
def _SomeSelected( self ):
return self._listctrl.GetSelectedItemCount() > 0
def _UpdateButtons( self ):
for ( button, enabled_check_func ) in self._button_infos:
if enabled_check_func():
button.Enable()
else:
button.Disable()
def AddButton( self, label, clicked_func, enabled_only_on_selection = False, enabled_check_func = None ):
button = ClientGUICommon.BetterButton( self, label, clicked_func )
self._buttonbox.AddF( button, CC.FLAGS_VCENTER )
if enabled_only_on_selection:
enabled_check_func = self._SomeSelected
if enabled_check_func is not None:
self._button_infos.append( ( button, enabled_check_func ) )
def AddWindow( self, window ):
self._buttonbox.AddF( window, CC.FLAGS_VCENTER )
def EventContentChanged( self, event ):
self._UpdateButtons()
event.Skip()
def EventSelectionChanged( self, event ):
self._UpdateButtons()
event.Skip()
def SetListCtrl( self, listctrl ):
self._listctrl = listctrl
self._vbox.AddF( self._listctrl, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self._vbox.AddF( self._buttonbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( self._vbox )
self._listctrl.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventSelectionChanged )
self._listctrl.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventSelectionChanged )
self._listctrl.Bind( wx.EVT_LIST_INSERT_ITEM, self.EventContentChanged )
self._listctrl.Bind( wx.EVT_LIST_DELETE_ITEM, self.EventContentChanged )
self._listctrl.Bind( wx.EVT_LIST_DELETE_ALL_ITEMS, self.EventContentChanged )
2017-09-27 21:52:54 +00:00
2017-08-02 21:32:54 +00:00
class BetterListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin ):
def __init__( self, parent, name, height_num_chars, sizing_column_initial_width_num_chars, columns, data_to_tuples_func, delete_key_callback = None, activation_callback = None ):
wx.ListCtrl.__init__( self, parent, style = wx.LC_REPORT )
ListCtrlAutoWidthMixin.__init__( self )
self._data_to_tuples_func = data_to_tuples_func
self._sort_column = 0
self._sort_asc = True
# eventually have it look up 'name' in some options somewhere and see previous height, width, and column selection
# this thing should deal with missing entries but also have some filtered defaults for subs listctrl, which will have a bunch of possible columns
self._indices_to_data_info = {}
self._data_to_indices = {}
( total_width, height ) = ClientData.ConvertTextToPixels( self, ( sizing_column_initial_width_num_chars, height_num_chars ) )
resize_column = 1
for ( i, ( name, width_num_chars ) ) in enumerate( columns ):
if width_num_chars == -1:
width = -1
resize_column = i + 1
else:
width = ClientData.ConvertTextToPixelWidth( self, width_num_chars )
total_width += width
self.InsertColumn( i, name, width = width )
self.setResizeColumn( resize_column )
self.SetInitialSize( ( total_width, height ) )
self._delete_key_callback = delete_key_callback
self._activation_callback = activation_callback
self.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.EventItemActivated )
self.Bind( wx.EVT_LIST_COL_BEGIN_DRAG, self.EventBeginColDrag )
self.Bind( wx.EVT_LIST_COL_CLICK, self.EventColumnClick )
2017-08-30 20:27:47 +00:00
def _AddDataInfo( self, data_info ):
2017-08-02 21:32:54 +00:00
( data, display_tuple, sort_tuple ) = data_info
2017-08-09 21:33:51 +00:00
index = self.Append( display_tuple )
2017-08-02 21:32:54 +00:00
self._indices_to_data_info[ index ] = data_info
self._data_to_indices[ data ] = index
def _GetSelected( self ):
indices = []
i = self.GetFirstSelected()
while i != -1:
indices.append( i )
i = self.GetNextSelected( i )
return indices
2017-08-09 21:33:51 +00:00
def _RecalculateIndicesAfterDelete( self ):
2017-08-30 20:27:47 +00:00
indices_and_data_info = list( self._indices_to_data_info.items() )
indices_and_data_info.sort()
2017-08-09 21:33:51 +00:00
self._indices_to_data_info = {}
self._data_to_indices = {}
2017-08-30 20:27:47 +00:00
for ( index, ( old_index, data_info ) ) in enumerate( indices_and_data_info ):
2017-08-09 21:33:51 +00:00
( data, display_tuple, sort_tuple ) = data_info
self._data_to_indices[ data ] = index
self._indices_to_data_info[ index ] = data_info
2017-08-02 21:32:54 +00:00
def _SortDataInfo( self ):
data_infos = list( self._indices_to_data_info.values() )
def sort_key( data_info ):
( data, display_tuple, sort_tuple ) = data_info
2017-08-09 21:33:51 +00:00
return ( sort_tuple[ self._sort_column ], sort_tuple ) # add the sort tuple to get secondary sorting
2017-08-02 21:32:54 +00:00
data_infos.sort( key = sort_key, reverse = not self._sort_asc )
return data_infos
2017-08-09 21:33:51 +00:00
def _SortAndRefreshRows( self ):
2017-08-02 21:32:54 +00:00
2017-09-20 19:47:31 +00:00
selected_data_quick = set( self.GetData( only_selected = True ) )
2017-08-02 21:32:54 +00:00
2017-08-30 20:27:47 +00:00
selected_indices = self._GetSelected()
for selected_index in selected_indices:
self.Select( selected_index, False )
2017-08-02 21:32:54 +00:00
sorted_data_info = self._SortDataInfo()
self._indices_to_data_info = {}
self._data_to_indices = {}
2017-08-30 20:27:47 +00:00
for ( index, data_info ) in enumerate( sorted_data_info ):
self._indices_to_data_info[ index ] = data_info
2017-08-02 21:32:54 +00:00
( data, display_tuple, sort_tuple ) = data_info
2017-08-30 20:27:47 +00:00
self._data_to_indices[ data ] = index
2017-08-02 21:32:54 +00:00
2017-08-30 20:27:47 +00:00
self._UpdateRow( index, display_tuple )
2017-09-20 19:47:31 +00:00
if data in selected_data_quick:
2017-08-30 20:27:47 +00:00
self.Select( index )
2017-08-02 21:32:54 +00:00
2017-08-30 20:27:47 +00:00
def _UpdateRow( self, index, display_tuple ):
2017-08-02 21:32:54 +00:00
2017-08-30 20:27:47 +00:00
for ( column_index, value ) in enumerate( display_tuple ):
self.SetStringItem( index, column_index, value )
2017-08-02 21:32:54 +00:00
2017-08-30 20:27:47 +00:00
def AddDatas( self, datas ):
for data in datas:
( display_tuple, sort_tuple ) = self._data_to_tuples_func( data )
self._AddDataInfo( ( data, display_tuple, sort_tuple ) )
2017-08-02 21:32:54 +00:00
def DeleteDatas( self, datas ):
deletees = [ ( self._data_to_indices[ data ], data ) for data in datas ]
deletees.sort( reverse = True )
for ( index, data ) in deletees:
self.DeleteItem( index )
del self._data_to_indices[ data ]
del self._indices_to_data_info[ index ]
2017-08-09 21:33:51 +00:00
self._RecalculateIndicesAfterDelete()
2017-08-02 21:32:54 +00:00
2017-08-30 20:27:47 +00:00
2017-08-02 21:32:54 +00:00
def DeleteSelected( self ):
indices = self._GetSelected()
indices.sort( reverse = True )
for index in indices:
( data, display_tuple, sort_tuple ) = self._indices_to_data_info[ index ]
self.DeleteItem( index )
del self._data_to_indices[ data ]
del self._indices_to_data_info[ index ]
2017-08-09 21:33:51 +00:00
self._RecalculateIndicesAfterDelete()
2017-08-02 21:32:54 +00:00
def EventBeginColDrag( self, event ):
# resizeCol is not zero-indexed
if event.GetColumn() == self._resizeCol - 1:
last_column = self.GetColumnCount()
if self._resizeCol != last_column:
self.setResizeColumn( last_column )
else:
event.Veto()
return
event.Skip()
def EventColumnClick( self, event ):
col = event.GetColumn()
if col == self._sort_column:
self._sort_asc = not self._sort_asc
else:
self._sort_column = col
self._sort_asc = True
2017-08-09 21:33:51 +00:00
self._SortAndRefreshRows()
2017-08-02 21:32:54 +00:00
def EventItemActivated( self, event ):
if self._activation_callback is not None:
self._activation_callback()
else:
event.Skip()
def EventKeyDown( self, event ):
( modifier, key ) = ClientData.ConvertKeyEventToSimpleTuple( event )
if key in CC.DELETE_KEYS:
if self._delete_key_callback is not None:
self._delete_key_callback()
elif key in ( ord( 'A' ), ord( 'a' ) ) and modifier == wx.ACCEL_CTRL:
self.SelectAll()
else:
event.Skip()
def GetData( self, only_selected = False ):
if only_selected:
indices = self._GetSelected()
else:
indices = self._indices_to_data_info.keys()
2017-09-20 19:47:31 +00:00
result = []
2017-08-02 21:32:54 +00:00
for index in indices:
( data, display_tuple, sort_tuple ) = self._indices_to_data_info[ index ]
2017-09-20 19:47:31 +00:00
result.append( data )
2017-08-02 21:32:54 +00:00
return result
def HasData( self, data ):
return data in self._data_to_indices
def HasSelected( self ):
return self.GetSelectedItemCount() > 0
def SelectAll( self ):
currently_selected = set( self._GetSelected() )
currently_not_selected = [ index for index in range( self.GetItemCount() ) if index not in currently_selected ]
for index in currently_not_selected:
self.Select( index )
2017-11-15 22:35:49 +00:00
def SelectDatas( self, datas ):
for data in datas:
if data in self._data_to_indices:
index = self._data_to_indices[ data ]
self.Select( index )
2017-08-02 21:32:54 +00:00
def SetData( self, datas ):
2017-08-09 21:33:51 +00:00
datas = set( datas )
existing_datas = set( self._data_to_indices.keys() )
2017-08-02 21:32:54 +00:00
2017-08-09 21:33:51 +00:00
datas_to_add = datas.difference( existing_datas )
datas_to_update = datas.intersection( existing_datas )
datas_to_delete = existing_datas.difference( datas )
2017-08-02 21:32:54 +00:00
2017-08-09 21:33:51 +00:00
if len( datas_to_delete ) > 0:
2017-08-02 21:32:54 +00:00
2017-08-09 21:33:51 +00:00
self.DeleteDatas( datas_to_delete )
2017-08-02 21:32:54 +00:00
2017-08-09 21:33:51 +00:00
if len( datas_to_update ) > 0:
self.UpdateDatas( datas_to_update )
2017-08-02 21:32:54 +00:00
2017-08-09 21:33:51 +00:00
if len( datas_to_add ) > 0:
2017-08-30 20:27:47 +00:00
self.AddDatas( datas_to_add )
2017-08-09 21:33:51 +00:00
2017-12-13 22:33:07 +00:00
self._SortAndRefreshRows()
2017-08-02 21:32:54 +00:00
def Sort( self, col = None, asc = None ):
if col is not None:
self._sort_column = col
if asc is not None:
self._sort_asc = asc
2017-08-09 21:33:51 +00:00
self._SortAndRefreshRows()
2017-08-02 21:32:54 +00:00
2017-09-06 20:18:20 +00:00
def UpdateDatas( self, datas = None ):
if datas is None:
datas = list( self._data_to_indices.keys() )
2017-08-09 21:33:51 +00:00
for data in datas:
( display_tuple, sort_tuple ) = self._data_to_tuples_func( data )
data_info = ( data, display_tuple, sort_tuple )
index = self._data_to_indices[ data ]
if data_info != self._indices_to_data_info[ index ]:
self._indices_to_data_info[ index ] = data_info
2017-08-30 20:27:47 +00:00
self._UpdateRow( index, display_tuple )
2017-08-09 21:33:51 +00:00
2017-09-27 21:52:54 +00:00
class BetterListCtrlPanel( wx.Panel ):
def __init__( self, parent ):
wx.Panel.__init__( self, parent )
self._vbox = wx.BoxSizer( wx.VERTICAL )
self._buttonbox = wx.BoxSizer( wx.HORIZONTAL )
self._listctrl = None
2017-11-29 21:48:23 +00:00
self._permitted_object_types = []
self._import_add_callable = lambda x: None
2017-09-27 21:52:54 +00:00
self._button_infos = []
2017-11-08 22:07:12 +00:00
def _AddButton( self, button, enabled_only_on_selection = False, enabled_check_func = None ):
self._buttonbox.AddF( button, CC.FLAGS_VCENTER )
if enabled_only_on_selection:
enabled_check_func = self._HasSelected
if enabled_check_func is not None:
self._button_infos.append( ( button, enabled_check_func ) )
2017-11-29 21:48:23 +00:00
def _Duplicate( self ):
dupe_data = self._GetExportObject()
if dupe_data is not None:
dupe_data = dupe_data.Duplicate()
self._ImportObject( dupe_data )
def _ExportToClipboard( self ):
export_object = self._GetExportObject()
if export_object is not None:
json = export_object.DumpToString()
HG.client_controller.pub( 'clipboard', 'text', json )
def _ExportToPng( self ):
export_object = self._GetExportObject()
if export_object is not None:
import ClientGUITopLevelWindows
import ClientGUISerialisable
with ClientGUITopLevelWindows.DialogNullipotent( self, 'export to png' ) as dlg:
panel = ClientGUISerialisable.PngExportPanel( dlg, export_object )
dlg.SetPanel( panel )
dlg.ShowModal()
def _GetExportObject( self ):
to_export = HydrusSerialisable.SerialisableList()
for obj in self._listctrl.GetData( only_selected = True ):
to_export.append( obj )
if len( to_export ) == 0:
return None
elif len( to_export ) == 1:
return to_export[0]
else:
return to_export
2017-09-27 21:52:54 +00:00
def _HasSelected( self ):
return self._listctrl.HasSelected()
2017-11-29 21:48:23 +00:00
def _ImportFromClipboard( self ):
2017-12-13 22:33:07 +00:00
raw_text = HG.client_controller.GetClipboardText()
try:
2017-11-29 21:48:23 +00:00
2017-12-13 22:33:07 +00:00
obj = HydrusSerialisable.CreateFromString( raw_text )
2017-11-29 21:48:23 +00:00
2017-12-13 22:33:07 +00:00
self._ImportObject( obj )
2017-11-29 21:48:23 +00:00
2017-12-13 22:33:07 +00:00
except Exception as e:
2017-11-29 21:48:23 +00:00
2017-12-13 22:33:07 +00:00
wx.MessageBox( 'I could not understand what was in the clipboard' )
2017-11-29 21:48:23 +00:00
def _ImportFromPng( self ):
with wx.FileDialog( self, 'select the png with the encoded script', wildcard = 'PNG (*.png)|*.png' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
path = HydrusData.ToUnicode( dlg.GetPath() )
try:
payload = ClientSerialisable.LoadFromPng( path )
except Exception as e:
wx.MessageBox( HydrusData.ToUnicode( e ) )
return
try:
obj = HydrusSerialisable.CreateFromNetworkString( payload )
self._ImportObject( obj )
except:
wx.MessageBox( 'I could not understand what was encoded in the png!' )
def _ImportObject( self, obj ):
bad_object_types = set()
if isinstance( obj, HydrusSerialisable.SerialisableList ):
for sub_obj in obj:
self._ImportObject( sub_obj )
else:
if isinstance( obj, self._permitted_object_types ):
self._import_add_callable( obj )
else:
bad_object_types.add( type( obj ).__name__ )
if len( bad_object_types ) > 0:
message = 'The imported objects included these types:'
message += os.linesep * 2
message += os.linesep.join( bad_object_types )
message += os.linesep * 2
message += 'Whereas this control only allows:'
message += os.linesep * 2
message += os.linesep.join( ( o.__name__ for o in self._permitted_object_types ) )
wx.MessageBox( message )
2017-09-27 21:52:54 +00:00
def _UpdateButtons( self ):
for ( button, enabled_check_func ) in self._button_infos:
if enabled_check_func():
button.Enable()
else:
button.Disable()
def AddButton( self, label, clicked_func, enabled_only_on_selection = False, enabled_check_func = None ):
button = ClientGUICommon.BetterButton( self, label, clicked_func )
2017-11-08 22:07:12 +00:00
self._AddButton( button, enabled_only_on_selection = enabled_only_on_selection, enabled_check_func = enabled_check_func )
2017-09-27 21:52:54 +00:00
2017-11-15 22:35:49 +00:00
self._UpdateButtons()
2017-11-08 22:07:12 +00:00
2017-11-29 21:48:23 +00:00
def AddImportExportButtons( self, permitted_object_types, import_add_callable ):
self._permitted_object_types = permitted_object_types
self._import_add_callable = import_add_callable
export_menu_items = []
export_menu_items.append( ( 'normal', 'to clipboard', 'Serialise the selected data and put it on your clipboard.', self._ExportToClipboard ) )
export_menu_items.append( ( 'normal', 'to png', 'Serialise the selected data and encode it to an image file you can easily share with other hydrus users.', self._ExportToPng ) )
import_menu_items = []
import_menu_items.append( ( 'normal', 'from clipboard', 'Load a data from text in your clipboard.', self._ImportFromClipboard ) )
import_menu_items.append( ( 'normal', 'from png', 'Load a data from an encoded png.', self._ImportFromPng ) )
self.AddMenuButton( 'export', export_menu_items, enabled_only_on_selection = True )
self.AddMenuButton( 'import', import_menu_items )
self.AddButton( 'duplicate', self._Duplicate, enabled_only_on_selection = True )
2017-11-08 22:07:12 +00:00
def AddMenuButton( self, label, menu_items, enabled_only_on_selection = False, enabled_check_func = None ):
2017-09-27 21:52:54 +00:00
2017-11-08 22:07:12 +00:00
button = ClientGUICommon.MenuButton( self, label, menu_items )
self._AddButton( button, enabled_only_on_selection = enabled_only_on_selection, enabled_check_func = enabled_check_func )
2017-11-15 22:35:49 +00:00
self._UpdateButtons()
2017-11-08 22:07:12 +00:00
def AddSeparator( self ):
self._buttonbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_PERPENDICULAR )
2017-09-27 21:52:54 +00:00
def AddWindow( self, window ):
self._buttonbox.AddF( window, CC.FLAGS_VCENTER )
def EventContentChanged( self, event ):
self._UpdateButtons()
event.Skip()
def EventSelectionChanged( self, event ):
self._UpdateButtons()
event.Skip()
def SetListCtrl( self, listctrl ):
self._listctrl = listctrl
self._vbox.AddF( self._listctrl, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self._vbox.AddF( self._buttonbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( self._vbox )
self._listctrl.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventSelectionChanged )
self._listctrl.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventSelectionChanged )
self._listctrl.Bind( wx.EVT_LIST_INSERT_ITEM, self.EventContentChanged )
self._listctrl.Bind( wx.EVT_LIST_DELETE_ITEM, self.EventContentChanged )
self._listctrl.Bind( wx.EVT_LIST_DELETE_ALL_ITEMS, self.EventContentChanged )