Esempio n. 1
0
 def __init__( self, name, path = '', export_type = HC.EXPORT_FOLDER_TYPE_REGULAR, delete_from_client_after_export = False, file_search_context = None, run_regularly = True, period = 3600, phrase = None, last_checked = 0, paused = False, run_now = False, last_error = '' ):
     
     HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
     
     if export_type == HC.EXPORT_FOLDER_TYPE_SYNCHRONISE:
         
         delete_from_client_after_export = False
         
     
     if file_search_context is None:
         
         default_local_file_service_key = HG.client_controller.services_manager.GetDefaultLocalFileServiceKey()
         
         location_search_context = ClientSearch.LocationSearchContext( current_service_keys = [ default_local_file_service_key ] )
         
         file_search_context = ClientSearch.FileSearchContext( location_search_context = location_search_context )
         
     
     if phrase is None:
         
         phrase = HG.client_controller.new_options.GetString( 'export_phrase' )
         
     
     self._path = path
     self._export_type = export_type
     self._delete_from_client_after_export = delete_from_client_after_export
     self._file_search_context = file_search_context
     self._run_regularly = run_regularly
     self._period = period
     self._phrase = phrase
     self._last_checked = last_checked
     self._paused = paused and not run_now
     self._run_now = run_now
     self._last_error = last_error
    def __init__(self, parent: QW.QWidget, predicate: ClientSearch.Predicate):

        QW.QWidget.__init__(self, parent)

        from hydrus.client.gui.search import ClientGUIACDropdown

        if predicate.GetType() != ClientSearch.PREDICATE_TYPE_OR_CONTAINER:

            raise Exception(
                'Launched an ORPredicateControl without an OR Pred!')

        predicates = predicate.GetValue()

        page_key = HydrusData.GenerateKey()

        location_search_context = ClientSearch.LocationSearchContext(
            current_service_keys=[CC.LOCAL_FILE_SERVICE_KEY])

        file_search_context = ClientSearch.FileSearchContext(
            location_search_context=location_search_context,
            predicates=predicates)

        self._search_control = ClientGUIACDropdown.AutoCompleteDropdownTagsRead(
            self,
            page_key,
            file_search_context,
            hide_favourites_edit_actions=True)

        vbox = QP.VBoxLayout()

        QP.AddToLayout(vbox, self._search_control, CC.FLAGS_EXPAND_BOTH_WAYS)

        self.setLayout(vbox)
Esempio n. 3
0
 def ExpandPredicate( self, service_key: bytes, predicate: ClientSearch.Predicate, service_strict: bool = False ):
     
     if not service_strict and self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
         
         service_key = CC.COMBINED_TAG_SERVICE_KEY
         
     
     ideal_sibling_predicate = predicate
     other_sibling_predicates = []
     
     if predicate.GetType() == ClientSearch.PREDICATE_TYPE_TAG:
         
         tag = predicate.GetValue()
         
         ideal_sibling = self.CollapseTag( service_key, tag, service_strict = service_strict )
         
         ideal_sibling_predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, ideal_sibling, predicate.IsInclusive() )
         
         other_siblings = set( self.GetAllSiblings( service_key, tag, service_strict = service_strict ) )
         
         other_siblings.discard( tag )
         other_siblings.discard( ideal_sibling )
         
         other_sibling_predicates = [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, sibling, predicate.IsInclusive() ) for sibling in other_siblings ]
         
         
     return ( ideal_sibling_predicate, other_sibling_predicates )
Esempio n. 4
0
 def _AddFolder( self ):
     
     new_options = HG.client_controller.new_options
     
     phrase = new_options.GetString( 'export_phrase' )
     
     name = 'export folder'
     path = ''
     export_type = HC.EXPORT_FOLDER_TYPE_REGULAR
     delete_from_client_after_export = False
     
     default_local_file_service_key = HG.client_controller.services_manager.GetDefaultLocalFileServiceKey()
     location_search_context = ClientSearch.LocationSearchContext( current_service_keys = [ default_local_file_service_key ] )
     
     file_search_context = ClientSearch.FileSearchContext( location_search_context = location_search_context )
     
     period = 15 * 60
     
     export_folder = ClientExporting.ExportFolder( name, path, export_type = export_type, delete_from_client_after_export = delete_from_client_after_export, file_search_context = file_search_context, period = period, phrase = phrase )
     
     with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit export folder' ) as dlg:
         
         panel = EditExportFolderPanel( dlg, export_folder )
         
         dlg.SetPanel( panel )
         
         if dlg.exec() == QW.QDialog.Accepted:
             
             export_folder = panel.GetValue()
             
             export_folder.SetNonDupeName( self._GetExistingNames() )
             
             self._export_folders.AddDatas( ( export_folder, ) )
    def test_basics(self):
        def test(obj, dupe_obj):

            self.assertEqual(len(list(obj.items())),
                             len(list(dupe_obj.items())))

            for (key, value) in list(obj.items()):

                self.assertEqual(value, dupe_obj[key])

        #

        d = HydrusSerialisable.SerialisableDictionary()

        d[1] = 2
        d[3] = 'test1'

        d['test2'] = 4
        d['test3'] = 5

        d[6] = HydrusSerialisable.SerialisableDictionary(
            {i: 'test' + str(i)
             for i in range(20)})
        d[ClientSearch.Predicate(ClientSearch.PREDICATE_TYPE_TAG,
                                 'test pred 1')] = 56

        d[ClientSearch.Predicate(
            ClientSearch.PREDICATE_TYPE_TAG,
            'test pred 2')] = HydrusSerialisable.SerialisableList([
                ClientSearch.Predicate(ClientSearch.PREDICATE_TYPE_TAG,
                                       'test' + str(i)) for i in range(10)
            ])

        self.assertEqual(len(list(d.keys())), 7)

        for (key, value) in list(d.items()):

            self.assertEqual(d[key], value)

        self._dump_and_load_and_test(d, test)

        #

        db = HydrusSerialisable.SerialisableBytesDictionary()

        db[HydrusData.GenerateKey()] = HydrusData.GenerateKey()
        db[HydrusData.GenerateKey()] = [
            HydrusData.GenerateKey() for i in range(10)
        ]
        db[1] = HydrusData.GenerateKey()
        db[2] = [HydrusData.GenerateKey() for i in range(10)]

        self.assertEqual(len(list(db.keys())), 4)

        for (key, value) in list(db.items()):

            self.assertEqual(db[key], value)

        self._dump_and_load_and_test(db, test)
Esempio n. 6
0
    def _AddNewFavouriteSearch(self, search_row=None):

        existing_folders_to_names = self._GetExistingFoldersToNames()

        existing_names = {
            name
            for name in itertools.chain.from_iterable(
                existing_folders_to_names.values())
        }

        if search_row is None:

            foldername = None
            name = 'new favourite search'

            default_local_file_service_key = HG.client_controller.services_manager.GetDefaultLocalFileServiceKey(
            )

            location_search_context = ClientSearch.LocationSearchContext(
                current_service_keys=[default_local_file_service_key])

            file_search_context = ClientSearch.FileSearchContext(
                location_search_context=location_search_context)

            synchronised = True
            media_sort = None
            media_collect = None

        else:

            (foldername, name, file_search_context, synchronised, media_sort,
             media_collect) = search_row

        name = HydrusData.GetNonDupeName(name, existing_names)

        with ClientGUITopLevelWindowsPanels.DialogEdit(
                self, 'edit favourite search') as dlg:

            panel = EditFavouriteSearchPanel(dlg, existing_folders_to_names,
                                             foldername, name,
                                             file_search_context, synchronised,
                                             media_sort, media_collect)

            dlg.SetPanel(panel)

            if dlg.exec() == QW.QDialog.Accepted:

                row = panel.GetValue()

                self._DeleteRow(row[0], row[1])

                self._favourite_searches.AddDatas((row, ))

                self._favourite_searches.Sort()
Esempio n. 7
0
    def ExpandPredicates(self, service_key, predicates, service_strict=False):

        if not service_strict and self._controller.new_options.GetBoolean(
                'apply_all_parents_to_all_services'):

            service_key = CC.COMBINED_TAG_SERVICE_KEY

        results = []

        with self._lock:

            for predicate in predicates:

                results.append(predicate)

                if predicate.GetType() == ClientSearch.PREDICATE_TYPE_TAG:

                    tag = predicate.GetValue()

                    parents = self._service_keys_to_children_to_parents[
                        service_key][tag]

                    for parent in parents:

                        parent_predicate = ClientSearch.Predicate(
                            ClientSearch.PREDICATE_TYPE_PARENT, parent)

                        results.append(parent_predicate)

            return results
Esempio n. 8
0
 def __init__( self, name, path = '', export_type = HC.EXPORT_FOLDER_TYPE_REGULAR, delete_from_client_after_export = False, file_search_context = None, run_regularly = True, period = 3600, phrase = None, last_checked = 0, paused = False, run_now = False ):
     
     HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
     
     if export_type == HC.EXPORT_FOLDER_TYPE_SYNCHRONISE:
         
         delete_from_client_after_export = False
         
     
     if file_search_context is None:
         
         file_search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY )
         
     
     if phrase is None:
         
         phrase = HG.client_controller.new_options.GetString( 'export_phrase' )
         
     
     self._path = path
     self._export_type = export_type
     self._delete_from_client_after_export = delete_from_client_after_export
     self._file_search_context = file_search_context
     self._run_regularly = run_regularly
     self._period = period
     self._phrase = phrase
     self._last_checked = last_checked
     self._paused = paused and not run_now
     self._run_now = run_now
    def GetSearchPredicates(self) -> typing.List[ClientSearch.Predicate]:

        # with counts? or just merge this into texttag???

        return [
            ClientSearch.Predicate(ClientSearch.PREDICATE_TYPE_TAG, self._tag)
        ]
def filetype_pred_generator( v ):
    
    # v is a list of non-hydrus-standard filetype strings
    
    mimes = ( 1, )
    
    return ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_MIME, mimes )
 def GetPredicates( self ):
     
     rating = None
     
     if self._rated_checkbox.isChecked():
         
         operator = '='
         rating = 'rated'
         
     elif self._not_rated_checkbox.isChecked():
         
         operator = '='
         rating = 'not rated'
         
     elif self._rating_control.GetRatingState() != ClientRatings.NULL:
         
         operator = self._operator.GetStringSelection()
         
         rating = self._rating_control.GetRating()
         
     
     if rating is None:
         
         return []
         
     else:
         
         predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_RATING, ( operator, rating, self._service_key ) )
         
         return [ predicate ]
def FilterAndConvertLabelPredicates(
    predicates: typing.Collection[ClientSearch.Predicate]
) -> typing.List[ClientSearch.Predicate]:

    good_predicates = []

    for predicate in predicates:

        predicate = predicate.GetCountlessCopy()

        predicate_type = predicate.GetType()

        if predicate_type in (ClientSearch.PREDICATE_TYPE_LABEL,
                              ClientSearch.PREDICATE_TYPE_PARENT):

            continue

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_UNTAGGED:

            predicate = ClientSearch.Predicate(
                ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS, (None, '=', 0))

        good_predicates.append(predicate)

    return good_predicates
 def GetPredicates( self ):
     
     rating = None
     
     if self._rated_checkbox.isChecked():
         
         rating = 'rated'
         
     elif self._not_rated_checkbox.isChecked():
         
         rating = 'not rated'
         
     else:
         
         rating_state = self._rating_control.GetRatingState()
         
         if rating_state == ClientRatings.LIKE:
             
             rating = 1
             
         elif rating_state == ClientRatings.DISLIKE:
             
             rating = 0
             
         
     
     if rating is None:
         
         return []
         
     else:
         
         predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_RATING, ( '=', rating, self._service_key ) )
         
         return [ predicate ]
    def ExpandPredicates(self, service_key, predicates, service_strict=False):

        results = []

        with self._lock:

            for predicate in predicates:

                results.append(predicate)

                if predicate.GetType() == ClientSearch.PREDICATE_TYPE_TAG:

                    tag = predicate.GetValue()

                    parents = self._service_keys_to_children_to_parents[
                        service_key][tag]

                    for parent in parents:

                        parent_predicate = ClientSearch.Predicate(
                            ClientSearch.PREDICATE_TYPE_PARENT, parent)

                        results.append(parent_predicate)

            return results
Esempio n. 15
0
def file_service_pred_generator(o, v, u):

    o_dict = {
        'is not currently in': (False, HC.CONTENT_STATUS_CURRENT),
        'is currently in': (True, HC.CONTENT_STATUS_CURRENT),
        'is not pending to': (False, HC.CONTENT_STATUS_PENDING),
        'is pending to': (True, HC.CONTENT_STATUS_PENDING)
    }

    (is_in, status) = o_dict[o]

    try:

        service_name = v

        service_key = HG.client_controller.services_manager.GetServiceKeyFromName(
            HC.FILE_SERVICES, service_name)

    except:

        raise HydrusExceptions.BadRequestException(
            'Could not find the service "{}"!'.format(service_name))

    return ClientSearch.Predicate(
        ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_SERVICE,
        (is_in, status, service_key))
Esempio n. 16
0
    def RegenerateSearchableSubtagMap(self,
                                      file_service_id,
                                      tag_service_id,
                                      status_hook=None):

        subtags_fts4_table_name = self.GetSubtagsFTS4TableName(
            file_service_id, tag_service_id)
        subtags_searchable_map_table_name = self.GetSubtagsSearchableMapTableName(
            file_service_id, tag_service_id)

        self._Execute(
            'DELETE FROM {};'.format(subtags_searchable_map_table_name))

        query = 'SELECT docid FROM {};'.format(subtags_fts4_table_name)

        BLOCK_SIZE = 10000

        for (group_of_subtag_ids, num_done,
             num_to_do) in HydrusDB.ReadLargeIdQueryInSeparateChunks(
                 self._c, query, BLOCK_SIZE):

            for subtag_id in group_of_subtag_ids:

                result = self._Execute(
                    'SELECT subtag FROM subtags WHERE subtag_id = ?;',
                    (subtag_id, )).fetchone()

                if result is None:

                    continue

                (subtag, ) = result

                searchable_subtag = ClientSearch.ConvertSubtagToSearchable(
                    subtag)

                if searchable_subtag != subtag:

                    searchable_subtag_id = self.modules_tags.GetSubtagId(
                        searchable_subtag)

                    self._Execute(
                        'INSERT OR IGNORE INTO {} ( subtag_id, searchable_subtag_id ) VALUES ( ?, ? );'
                        .format(subtags_searchable_map_table_name),
                        (subtag_id, searchable_subtag_id))

            message = HydrusData.ConvertValueRangeToPrettyString(
                num_done, num_to_do)

            HG.client_controller.frame_splash_status.SetSubtext(message)

            if status_hook is not None:

                status_hook(message)
Esempio n. 17
0
 def CollapsePredicates( self, service_key, predicates, service_strict = False ):
     
     if not service_strict and self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
         
         service_key = CC.COMBINED_TAG_SERVICE_KEY
         
     
     with self._lock:
         
         siblings = self._service_keys_to_siblings[ service_key ]
         
         results = [ predicate for predicate in predicates if predicate.GetType() != ClientSearch.PREDICATE_TYPE_TAG ]
         
         tag_predicates = [ predicate for predicate in predicates if predicate.GetType() == ClientSearch.PREDICATE_TYPE_TAG ]
         
         tags_to_predicates = {predicate.GetValue() : predicate for predicate in predicates if predicate.GetType() == ClientSearch.PREDICATE_TYPE_TAG}
         
         tags = list( tags_to_predicates.keys() )
         
         tags_to_include_in_results = set()
         
         for tag in tags:
             
             if tag in siblings:
                 
                 old_tag = tag
                 old_predicate = tags_to_predicates[ old_tag ]
                 
                 new_tag = siblings[ old_tag ]
                 
                 if new_tag not in tags_to_predicates:
                     
                     ( old_pred_type, old_value, old_inclusive ) = old_predicate.GetInfo()
                     
                     new_predicate = ClientSearch.Predicate( old_pred_type, new_tag, old_inclusive )
                     
                     tags_to_predicates[ new_tag ] = new_predicate
                     
                     tags_to_include_in_results.add( new_tag )
                     
                 
                 new_predicate = tags_to_predicates[ new_tag ]
                 
                 new_predicate.AddCounts( old_predicate )
                 
             else:
                 
                 tags_to_include_in_results.add( tag )
                 
             
         
         results.extend( [ tags_to_predicates[ tag ] for tag in tags_to_include_in_results ] )
         
         return results
def num_file_relationships_pred_generator( o, v, u ):
    
    u_dict = {
        'not related/false positive' : HC.DUPLICATE_FALSE_POSITIVE,
        'duplicates' : HC.DUPLICATE_MEMBER,
        'alternates' : HC.DUPLICATE_ALTERNATE,
        'potential duplicates' : HC.DUPLICATE_POTENTIAL
    }
    
    dupe_type = u_dict[ u ]
    
    return ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS_COUNT, ( o, v, dupe_type ) )
Esempio n. 19
0
def date_pred_generator(pred_type, o, v):

    #Either a tuple of 4 non-negative integers: (years, months, days, hours) where the latter is < 24 OR
    #a datetime.date object. For the latter, only the YYYY-MM-DD format is accepted.

    date_type = 'delta'

    if isinstance(v, datetime.date):

        date_type = 'date'
        v = (v.year, v.month, v.day)

    return ClientSearch.Predicate(pred_type, (o, date_type, tuple(v)))
Esempio n. 20
0
 def ExpandPredicate( self, service_key: bytes, predicate: ClientSearch.Predicate ):
     
     ideal_sibling_predicate = predicate
     other_sibling_predicates = []
     
     if predicate.GetType() == ClientSearch.PREDICATE_TYPE_TAG:
         
         tag = predicate.GetValue()
         
         ideal_sibling = self.GetSibling( service_key, tag )
         
         ideal_sibling_predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, ideal_sibling, predicate.IsInclusive() )
         
         other_siblings = set( self.GetAllSiblings( service_key, tag ) )
         
         other_siblings.discard( tag )
         other_siblings.discard( ideal_sibling )
         
         other_sibling_predicates = [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, sibling, predicate.IsInclusive() ) for sibling in other_siblings ]
         
     
     return ( ideal_sibling_predicate, other_sibling_predicates )
def url_class_pred_generator( include, url_class_name ):
    
    description = ( 'has {} url' if include else 'does not have {} url' ).format( url_class_name )
    
    try:
        
        url_class = HG.client_controller.network_engine.domain_manager.GetURLClassFromName( url_class_name )
        
    except HydrusExceptions.DataMissing as e:
        
        raise ValueError( str( e ) )
        
    
    return ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS, ( include, 'url_class', url_class, description ) )
Esempio n. 22
0
    def GetPredicates(self):

        or_sub_predicates = self._search_control.GetPredicates()

        if len(or_sub_predicates) == 0:

            return []

        elif len(or_sub_predicates) == 1:

            return or_sub_predicates

        or_predicate = ClientSearch.Predicate(
            ClientSearch.PREDICATE_TYPE_OR_CONTAINER, or_sub_predicates)

        return [or_predicate]
def file_service_pred_generator( o, v, u ):
    
    if o.startswith( 'is not' ):
        
        is_in = False
        
    else:
        
        is_in = True
        
    
    o_dict = {
        'currently in' : HC.CONTENT_STATUS_CURRENT,
        'deleted from' : HC.CONTENT_STATUS_DELETED,
        'pending to' : HC.CONTENT_STATUS_PENDING,
        'petitioned from' : HC.CONTENT_STATUS_PETITIONED
    }
    
    status = None
    
    for ( phrase, possible_status ) in o_dict.items():
        
        if phrase in o:
            
            status = possible_status
            
            break
            
        
    
    if status is None:
        
        raise HydrusExceptions.BadRequestException( 'Did not understand the file service status!' )
        
    
    try:
        
        service_name = v
        
        service_key = HG.client_controller.services_manager.GetServiceKeyFromName( HC.FILE_SERVICES, service_name )
        
    except:
        
        raise HydrusExceptions.BadRequestException( 'Could not find the service "{}"!'.format( service_name ) )
        
    
    return ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_SERVICE, ( is_in, status, service_key ) )
Esempio n. 24
0
    def _AddNewFavouriteSearch(self, search_row=None):

        existing_folders_to_names = self._GetExistingFoldersToNames()

        existing_names = {
            name
            for name in itertools.chain.from_iterable(
                existing_folders_to_names.values())
        }

        if search_row is None:

            foldername = None
            name = 'new favourite search'
            file_search_context = ClientSearch.FileSearchContext(
                file_service_key=CC.LOCAL_FILE_SERVICE_KEY)
            synchronised = True
            media_sort = None
            media_collect = None

        else:

            (foldername, name, file_search_context, synchronised, media_sort,
             media_collect) = search_row

        name = HydrusData.GetNonDupeName(name, existing_names)

        with ClientGUITopLevelWindowsPanels.DialogEdit(
                self, 'edit favourite search') as dlg:

            panel = EditFavouriteSearchPanel(dlg, existing_folders_to_names,
                                             foldername, name,
                                             file_search_context, synchronised,
                                             media_sort, media_collect)

            dlg.SetPanel(panel)

            if dlg.exec() == QW.QDialog.Accepted:

                row = panel.GetValue()

                self._DeleteRow(row[0], row[1])

                self._favourite_searches.AddDatas((row, ))

                self._favourite_searches.Sort()
Esempio n. 25
0
 def do_it( service_key, hash, search_tags, max_results, max_time_to_take ):
     
     def qt_code( predicates ):
         
         if not self or not QP.isValid( self ):
             
             return
             
         
         self._last_fetched_predicates = predicates
         
         self._UpdateTagDisplay()
         
         self._have_fetched = True
         
     
     predicates = HG.client_controller.Read( 'related_tags', service_key, hash, search_tags, max_results, max_time_to_take )
     
     predicates = ClientSearch.SortPredicates( predicates )
     
     QP.CallAfter( qt_code, predicates )
Esempio n. 26
0
    def __init__(self, parent: QW.QWidget, predicate: ClientSearch.Predicate):

        QW.QWidget.__init__(self, parent)

        from hydrus.client.gui.search import ClientGUIACDropdown

        if predicate.GetType() != ClientSearch.PREDICATE_TYPE_OR_CONTAINER:

            raise Exception(
                'Launched an ORPredicateControl without an OR Pred!')

        predicates = predicate.GetValue()

        page_key = HydrusData.GenerateKey()

        location_context = HG.client_controller.new_options.GetDefaultLocalLocationContext(
        )

        file_search_context = ClientSearch.FileSearchContext(
            location_context=location_context, predicates=predicates)

        self._search_control = ClientGUIACDropdown.AutoCompleteDropdownTagsRead(
            self,
            page_key,
            file_search_context,
            hide_favourites_edit_actions=True)

        self._search_control.setMinimumWidth(
            ClientGUIFunctions.ConvertTextToPixelWidth(self._search_control,
                                                       64))

        vbox = QP.VBoxLayout()

        QP.AddToLayout(vbox, self._search_control, CC.FLAGS_EXPAND_BOTH_WAYS)

        self.setLayout(vbox)

        ClientGUIFunctions.SetFocusLater(self._search_control)
Esempio n. 27
0
    def GetSubtagIdsFromWildcardIntoTable(self,
                                          file_service_id: int,
                                          tag_service_id: int,
                                          subtag_wildcard,
                                          subtag_id_table_name,
                                          job_key=None):

        if tag_service_id == self.modules_services.combined_tag_service_id:

            search_tag_service_ids = self.modules_services.GetServiceIds(
                HC.REAL_TAG_SERVICES)

        else:

            search_tag_service_ids = (tag_service_id, )

        for search_tag_service_id in search_tag_service_ids:

            if '*' in subtag_wildcard:

                subtags_fts4_table_name = self.GetSubtagsFTS4TableName(
                    file_service_id, search_tag_service_id)

                wildcard_has_fts4_searchable_characters = WildcardHasFTS4SearchableCharacters(
                    subtag_wildcard)

                if subtag_wildcard == '*':

                    # hellmode, but shouldn't be called normally
                    cursor = self._Execute('SELECT docid FROM {};'.format(
                        subtags_fts4_table_name))

                elif ClientSearch.IsComplexWildcard(
                        subtag_wildcard
                ) or not wildcard_has_fts4_searchable_characters:

                    # FTS4 does not support complex wildcards, so instead we'll search our raw subtags
                    # however, since we want to search 'searchable' text, we use the 'searchable subtags map' to cross between real and searchable

                    like_param = ConvertWildcardToSQLiteLikeParameter(
                        subtag_wildcard)

                    if subtag_wildcard.startswith(
                            '*'
                    ) or not wildcard_has_fts4_searchable_characters:

                        # this is a SCAN, but there we go
                        # a potential optimisation here, in future, is to store fts4 of subtags reversed, then for '*amus', we can just search that reverse cache for 'suma*'
                        # and this would only double the size of the fts4 cache, the largest cache in the whole db! a steal!
                        # it also would not fix '*amu*', but with some cleverness could speed up '*amus ar*'

                        query = 'SELECT docid FROM {} WHERE subtag LIKE ?;'.format(
                            subtags_fts4_table_name)

                        cursor = self._Execute(query, (like_param, ))

                    else:

                        # we have an optimisation here--rather than searching all subtags for bl*ah, let's search all the bl* subtags for bl*ah!

                        prefix_fts4_wildcard = subtag_wildcard.split('*')[0]

                        prefix_fts4_wildcard_param = '"{}*"'.format(
                            prefix_fts4_wildcard)

                        query = 'SELECT docid FROM {} WHERE subtag MATCH ? AND subtag LIKE ?;'.format(
                            subtags_fts4_table_name)

                        cursor = self._Execute(
                            query, (prefix_fts4_wildcard_param, like_param))

                else:

                    # we want the " " wrapping our search text to keep whitespace words connected and in order
                    # "samus ar*" should not match "around samus"

                    # simple 'sam*' style subtag, so we can search fts4 no prob

                    subtags_fts4_param = '"{}"'.format(subtag_wildcard)

                    cursor = self._Execute(
                        'SELECT docid FROM {} WHERE subtag MATCH ?;'.format(
                            subtags_fts4_table_name), (subtags_fts4_param, ))

                cancelled_hook = None

                if job_key is not None:

                    cancelled_hook = job_key.IsCancelled

                loop_of_subtag_id_tuples = HydrusDB.ReadFromCancellableCursor(
                    cursor, 1024, cancelled_hook=cancelled_hook)

                self._ExecuteMany(
                    'INSERT OR IGNORE INTO {} ( subtag_id ) VALUES ( ? );'.
                    format(subtag_id_table_name), loop_of_subtag_id_tuples)

            else:

                # old notes from before we had searchable subtag map. I deleted that map once, albeit in an older and less efficient form. *don't delete it again, it has use*
                #
                # NOTE: doing a subtag = 'blah' lookup on subtags_fts4 tables is ultra slow, lmao!
                # attempts to match '/a/' to 'a' with clever FTS4 MATCHing (i.e. a MATCH on a*\b, then an '= a') proved not super successful
                # in testing, it was still a bit slow. my guess is it is still iterating through all the nodes for ^a*, the \b just makes it a bit more efficient sometimes
                # in tests '^a\b' was about twice as fast as 'a*', so the \b might not even be helping at all
                # so, I decided to move back to a lean and upgraded searchable subtag map, and here we are

                searchable_subtag = subtag_wildcard

                if self.modules_tags.SubtagExists(searchable_subtag):

                    searchable_subtag_id = self.modules_tags.GetSubtagId(
                        searchable_subtag)

                    self._Execute(
                        'INSERT OR IGNORE INTO {} ( subtag_id ) VALUES ( ? );'.
                        format(subtag_id_table_name), (searchable_subtag_id, ))

                    subtags_searchable_map_table_name = self.GetSubtagsSearchableMapTableName(
                        file_service_id, search_tag_service_id)

                    self._Execute(
                        'INSERT OR IGNORE INTO {} ( subtag_id ) SELECT subtag_id FROM {} WHERE searchable_subtag_id = ?;'
                        .format(subtag_id_table_name,
                                subtags_searchable_map_table_name),
                        (searchable_subtag_id, ))

            if job_key is not None and job_key.IsCancelled():

                self._Execute('DELETE FROM {};'.format(subtag_id_table_name))

                return
    def GetSearchPredicates(self) -> typing.List[ClientSearch.Predicate]:

        return [
            ClientSearch.Predicate(ClientSearch.PREDICATE_TYPE_TAG, self._tag)
        ]
    def __init__(self, parent, predicate: ClientSearch.Predicate):

        ClientGUIScrolledPanels.EditPanel.__init__(self, parent)

        predicate_type = predicate.GetType()

        self._predicates = []

        label = None
        editable_pred_panels = []
        static_pred_buttons = []

        recent_predicate_types = [predicate_type]

        if predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_AGE:

            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_AGE,
                        ('<', 'delta', (0, 0, 1, 0))), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_AGE,
                        ('<', 'delta', (0, 0, 7, 0))), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_AGE,
                        ('<', 'delta', (0, 1, 0, 0))), )))

            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemAgeDelta,
                    predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemAgeDate,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_MODIFIED_TIME:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.
                    PanelPredicateSystemModifiedDelta, predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemModifiedDate,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_DIMENSIONS:

            recent_predicate_types = [
                ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT,
                ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH,
                ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO,
                ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_PIXELS
            ]

            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO,
                        ('=', 16, 9)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO,
                        ('=', 9, 16)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO,
                        ('=', 4, 3)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO,
                        ('=', 1, 1)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self,
                    self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH, ('=', 1920)),
                           ClientSearch.Predicate(
                               ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT,
                               ('=', 1080))),
                    forced_label='1080p'))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self,
                    self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH, ('=', 1280)),
                           ClientSearch.Predicate(
                               ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT,
                               ('=', 720))),
                    forced_label='720p'))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self,
                    self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH, ('=', 3840)),
                           ClientSearch.Predicate(
                               ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT,
                               ('=', 2160))),
                    forced_label='4k'))

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.PanelPredicateSystemHeight,
                    predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.PanelPredicateSystemWidth,
                    predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.PanelPredicateSystemRatio,
                    predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumPixels,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION:

            recent_predicate_types = [
                ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION,
                ClientSearch.PREDICATE_TYPE_SYSTEM_FRAMERATE,
                ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_FRAMES
            ]

            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION,
                        ('>', 0)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION,
                        ('=', 0)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_FRAMERATE,
                        ('=', 30)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_FRAMERATE,
                        ('=', 60)), )))

            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemDuration,
                    predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemFramerate,
                    predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumFrames,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_SERVICE:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemFileService,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.
                    PanelPredicateSystemKnownURLsExactURL, predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.
                    PanelPredicateSystemKnownURLsDomain, predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.
                    PanelPredicateSystemKnownURLsRegex, predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.
                    PanelPredicateSystemKnownURLsURLClass, predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_AUDIO:

            recent_predicate_types = []

            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_AUDIO,
                        True), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_AUDIO,
                        False), )))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HASH:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.PanelPredicateSystemHash,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT:

            label = 'system:limit clips a large search result down to the given number of files. It is very useful for processing in smaller batches.'
            label += os.linesep * 2
            label += 'For all the simpler sorts (filesize, duration, etc...), it will select the n largest/smallest in the result set appropriate for that sort. For complicated sorts like tags, it will sample randomly.'

            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT, 64), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT, 256), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT, 1024), )))

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.PanelPredicateSystemLimit,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_MIME:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.PanelPredicateSystemMime,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS:

            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS,
                        (None, '>', 0)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS,
                        (None, '=', 0)), )))

            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumTags,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NOTES:

            recent_predicate_types = [
                ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_NOTES,
                ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_NOTE_NAME
            ]

            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_NOTES,
                        ('>', 0)), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_NOTES,
                        ('=', 0)), )))

            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumNotes,
                    predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemHasNoteName,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_WORDS:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumWords,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_RATING:

            services_manager = HG.client_controller.services_manager

            ratings_services = services_manager.GetServices(
                (HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL))

            if len(ratings_services) > 0:

                editable_pred_panels.append(
                    self._PredOKPanel(
                        self,
                        ClientGUIPredicatesMultiple.PanelPredicateSystemRating,
                        (predicate, )))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_SIMILAR_TO:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemSimilarTo,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_SIZE:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.PanelPredicateSystemSize,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_TAG_AS_NUMBER:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self,
                    ClientGUIPredicatesSingle.PanelPredicateSystemTagAsNumber,
                    predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS:

            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.
                        PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS_KING,
                        False), )))
            static_pred_buttons.append(
                ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                    self, self, (ClientSearch.Predicate(
                        ClientSearch.
                        PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS_KING,
                        True), )))

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.
                    PanelPredicateSystemDuplicateRelationships, predicate))

        elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_VIEWING_STATS:

            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.
                    PanelPredicateSystemFileViewingStatsViews, predicate))
            editable_pred_panels.append(
                self._PredOKPanel(
                    self, ClientGUIPredicatesSingle.
                    PanelPredicateSystemFileViewingStatsViewtime, predicate))

        vbox = QP.VBoxLayout()

        if label is not None:

            st = ClientGUICommon.BetterStaticText(self, label=label)

            st.setWordWrap(True)

            QP.AddToLayout(vbox, st, CC.FLAGS_EXPAND_PERPENDICULAR)

        recent_predicates = []

        if len(recent_predicate_types) > 0:

            recent_predicates = HG.client_controller.new_options.GetRecentPredicates(
                recent_predicate_types)

            if len(recent_predicates) > 0:

                recent_predicates_box = ClientGUICommon.StaticBox(
                    self, 'recent')

                for recent_predicate in recent_predicates:

                    button = ClientGUIPredicatesSingle.StaticSystemPredicateButton(
                        recent_predicates_box, self, (recent_predicate, ))

                    recent_predicates_box.Add(button,
                                              CC.FLAGS_EXPAND_PERPENDICULAR)

                QP.AddToLayout(vbox, recent_predicates_box,
                               CC.FLAGS_EXPAND_PERPENDICULAR)

        for button in static_pred_buttons:

            QP.AddToLayout(vbox, button, CC.FLAGS_EXPAND_PERPENDICULAR)

        for panel in editable_pred_panels:

            QP.AddToLayout(vbox, panel, CC.FLAGS_EXPAND_PERPENDICULAR)

        if len(static_pred_buttons) > 0 and len(editable_pred_panels) == 0:

            HG.client_controller.CallAfterQtSafe(
                static_pred_buttons[0], static_pred_buttons[0].setFocus,
                QC.Qt.OtherFocusReason)

        self.widget().setLayout(vbox)
    def __init__(self, parent,
                 predicates: typing.Collection[ClientSearch.Predicate]):

        ClientGUIScrolledPanels.EditPanel.__init__(self, parent)

        predicates = list(predicates)

        predicates.sort(key=lambda p: p.ToString(with_count=False))

        self._uneditable_predicates = []

        self._invertible_pred_buttons = []
        self._editable_pred_panels = []

        rating_preds = []

        # I hate this pred comparison stuff, but let's hang in there until we split this stuff up by type mate
        # then we can just have a dict type->panel_class lookup or whatever
        # also it would be nice to have proper rating editing here, think about it

        AGE_DELTA_PRED = ClientSearch.Predicate(
            ClientSearch.PREDICATE_TYPE_SYSTEM_AGE,
            ('>', 'delta', (2000, 1, 1, 1)))
        MODIFIED_DELTA_PRED = ClientSearch.Predicate(
            ClientSearch.PREDICATE_TYPE_SYSTEM_MODIFIED_TIME,
            ('>', 'delta', (2000, 1, 1, 1)))
        KNOWN_URL_EXACT = ClientSearch.Predicate(
            ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS,
            (True, 'exact_match', '', ''))
        KNOWN_URL_DOMAIN = ClientSearch.Predicate(
            ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS,
            (True, 'domain', '', ''))
        KNOWN_URL_REGEX = ClientSearch.Predicate(
            ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS,
            (True, 'regex', '', ''))
        FILE_VIEWS_PRED = ClientSearch.Predicate(
            ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_VIEWING_STATS,
            ('views', ('media', ), '>', 0))

        for predicate in predicates:

            predicate_type = predicate.GetType()

            if predicate_type == ClientSearch.PREDICATE_TYPE_OR_CONTAINER:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesOR.ORPredicateControl(self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_AGE:

                if predicate.IsUIEditable(AGE_DELTA_PRED):

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.PanelPredicateSystemAgeDelta(
                            self, predicate))

                else:

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.PanelPredicateSystemAgeDate(
                            self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_MODIFIED_TIME:

                if predicate.IsUIEditable(MODIFIED_DELTA_PRED):

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.
                        PanelPredicateSystemModifiedDelta(self, predicate))

                else:

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.
                        PanelPredicateSystemModifiedDate(self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemHeight(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemWidth(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemRatio(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_PIXELS:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumPixels(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemDuration(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FRAMERATE:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemFramerate(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_FRAMES:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumFrames(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_SERVICE:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemFileService(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS:

                if predicate.IsUIEditable(KNOWN_URL_EXACT):

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.
                        PanelPredicateSystemKnownURLsExactURL(self, predicate))

                elif predicate.IsUIEditable(KNOWN_URL_DOMAIN):

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.
                        PanelPredicateSystemKnownURLsDomain(self, predicate))

                elif predicate.IsUIEditable(KNOWN_URL_REGEX):

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.
                        PanelPredicateSystemKnownURLsRegex(self, predicate))

                else:

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.
                        PanelPredicateSystemKnownURLsURLClass(self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HASH:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemHash(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemLimit(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_MIME:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemMime(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumTags(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_NOTES:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumNotes(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_NOTE_NAME:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemHasNoteName(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_WORDS:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemNumWords(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_SIMILAR_TO:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemSimilarTo(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_SIZE:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemSize(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_TAG_AS_NUMBER:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.PanelPredicateSystemTagAsNumber(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS_COUNT:

                self._editable_pred_panels.append(
                    ClientGUIPredicatesSingle.
                    PanelPredicateSystemDuplicateRelationships(
                        self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_VIEWING_STATS:

                if predicate.IsUIEditable(FILE_VIEWS_PRED):

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.
                        PanelPredicateSystemFileViewingStatsViews(
                            self, predicate))

                else:

                    self._editable_pred_panels.append(
                        ClientGUIPredicatesSingle.
                        PanelPredicateSystemFileViewingStatsViewtime(
                            self, predicate))

            elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_RATING:

                rating_preds.append(predicate)

            elif predicate.IsInvertible():

                self._invertible_pred_buttons.append(
                    ClientGUIPredicatesSingle.InvertiblePredicateButton(
                        self, predicate))

            else:

                self._uneditable_predicates.append(predicate)

        if len(rating_preds) > 0:

            self._editable_pred_panels.append(
                ClientGUIPredicatesMultiple.PanelPredicateSystemRating(
                    self, rating_preds))

        vbox = QP.VBoxLayout()

        for button in self._invertible_pred_buttons:

            QP.AddToLayout(vbox, button, CC.FLAGS_EXPAND_PERPENDICULAR)

        for panel in self._editable_pred_panels:

            if isinstance(panel, ClientGUIPredicatesOR.ORPredicateControl):

                flags = CC.FLAGS_EXPAND_BOTH_WAYS

            else:

                flags = CC.FLAGS_EXPAND_PERPENDICULAR

            QP.AddToLayout(vbox, panel, flags)

        self.widget().setLayout(vbox)