+
    Ki$  c                   s  a  R& t-0 t ^ RIt^ RIt^ RIHt ^ RIHt ^ RIHtH	t	H
t
HtHt ^ RIHt ^ RIHt ^ RIHt ^ RIHt ^ R	IHt ]! 4       t ! R
 R]P0                  4      t ! R R]4      t]! 4       .t] ^ k R tR tR R ltR tR t R t!R'R R llt"R(R lt#R R lt$R R lt%R t&R'R R llt'R'R R  llt(R'R! R" llt)R# t*R$ t+R% t,R# ))i    N)defaultdict)repeat)AnyDictListSetUnion)settings)games)sql)NotificationSource)get_natural_sort_keyc                   s   a  ] tR t^t o Rt]P                  V 3R lR l4       tV 3R lR lt]P                  V 3R lR l4       t	Rt
V tR	# )
_SmartCategoryzfAbstract class to define smart categories. Smart categories are automatically defined based on a rule.c                    < V ^8  d   QhRS[ /#    returnstrformat__classdict__   "?/usr/lib/python3.14/site-packages/lutris/database/categories.py__annotate___SmartCategory.__annotate__   s      #     c                    R # N Zself   &r   get_nameZ_SmartCategory.get_name       r   c                0   < V ^8  d   QhRS[ S[,          /# r   r   r   r   r   r   r   r      s     < <c#h <r   c                sB    \        R  V P                  4        4       4      # )c              3   2   "   T F  qR ,          x  K  	  R# 5iidNr   .0Zgame   & r   	<genexpr>Z._SmartCategory.get_game_ids.<locals>.<genexpr>   s     ;*:$::*:   )set	get_gamesr   r   r   get_game_idsZ_SmartCategory.get_game_ids   s    ;$..*:;;;r   c                r"   r   r   r   r   r   r   r   r      s      49 r   c                r   r   r   r   r   r   r-   Z_SmartCategory.get_games   r!   r   r   N)__name__
__module____qualname____firstlineno____doc__abcZabstractmethodr    r.   r-   __static_attributes____classdictcell__r      @r   r   r      sD     p < < 	 r   r   c                   sZ   a  ] tR t^t o RtV 3R lR ltV 3R lR ltV 3R lR ltRtV t	R	# )
_SmartUncategorizedCategoryz9A SmartCategory that resolves to all uncategorized games.c                r   r   r   r   r   r   r   (_SmartUncategorizedCategory.__annotate__"   s        #  r   c                r   )z.uncategorizedr   r   r   r   r    Z$_SmartUncategorizedCategory.get_name"   s    r   c                r"   r   r#   r   r   r   r   r;   %   s     , ,c#h ,r   c                    \        4       # r   )get_uncategorized_game_idsr   r   r   r.   Z(_SmartUncategorizedCategory.get_game_ids%   s    )++r   c                r"   r   r/   r   r   r   r   r;   (   s     ) )49 )r   c                r<   r   )get_uncategorized_gamesr   r   r   r-   Z%_SmartUncategorizedCategory.get_games(   s    &((r   r   N)
r0   r1   r2   r3   r4   r    r.   r-   r6   r7   r8   r9   r   r:   r:      s&     C   , ,) )r   r:   c                s    T ;'       g    RP                  4       p \        V 4      '       g   \        P                  ! RRV 4      p V # )zH"This strips the name given, and also removes extra internal whitespace. z +Z )Zstripis_reserved_categoryreZsubnamer   r   strip_category_namerD   1   s8    JJBD%%vvdC&Kr   c                sR    V '       * ;'       g    V ^ ,          R8H  ;'       g    V R9   # )ziTrue if name is None, blank or is a name Lutris uses internally, or
starts with '.' for future expansion.Z.)Zallfavoriter   rB   r   r   r@   r@   9   s*     8DDtAw#~DD1D)DDr   c                s~    V ^8  d   QhR\         \        \        \        \        \        3,          3,          ,          /# r   )r   r   r   r   intr      "r   r   r   ?   s*     9 9T#uS#X"678 9r   c                 sL    \         P                  ! \        P                  R4      # )z+Get the list of every category in database.
categoriesr	   	db_selectr   DB_PATHr   r   r   get_categoriesrM   ?   s     ==))<88r   c                  s    \        \        4      p \        P                  ! \        P
                  R 4       F(  pWR,          ,          P                  VR,          4       K*  	  V # )games_categoriesgame_idcategory_id)r    listr	   rK   r   rL   append)rN   row     r   get_all_games_categoriesrU   E   sJ    "4(}}X--/ABY(//M0BC Cr   c                z    \         P                  ! \        P                  RRV 3R7      pV'       d
   V^ ,          # R# )Return a category by namerI   rC   Z	conditionNrJ   )rC   rI   r)   r   get_category_by_namerY   L   s1    x//&RVXJ!} r   c                rV   )rW   rI   r&   rX   NrJ   )rP   rI   r)   r   get_category_by_idrZ   S   s2    x//$P[I\]J!} r   c                sR    V ^8  d   QhR\         R\        R\        \         ,          /# )r   rC   subname_allowedr   )r   boolr   rG   rH   r   r   r   Z   s&      C $ 4PS9 r   c                sT   RpV 3p\         P                  ! \        P                  W#4       Uu. uF  qDR,          NK  	  ppV'       gL   V'       dD   RpRV  R23p\         P                  ! \        P                  W#4       Uu. uF  qDR,          NK  	  ppT;'       g    V .# u upi u upi )a2  Searches for a category name case-insensitively and returns all matching names;
if none match, it just returns 'name' as is.

If subname_allowed is true but name is not a match for any category, we'll look for
any category that contains the name as a substring instead before falling back to
'name' itself.z7SELECT name FROM categories WHERE name=? COLLATE NOCASErC   z<SELECT name FROM categories WHERE name LIKE ? COLLATE NOCASEZ%r	   db_queryr   rL   )rC   r[   query
parameterscatZnamess   &&    r   normalized_category_namesrb   Z   s     FEJ$'LL1A1A5$UV$US[[$UEV_N$qk^
(+X5E5Eu(YZ(YV(YZ??TF W
 [s   B  B%c                s
   . p. pV '       dO   RpVP                  RRP                  \        R\        V 4      4      4      ,          4       VP	                  V 4       MRpV'       dN   RRP                  \        R\        V4      4      4      ,          pVP                  V4       VP	                  V4       V'       d    VRRP                  V4      ,          ,          p\        R	 \        P                  ! \        P                  V\        V4      4       4       4      p\         FO  pVe   VP                  4       V9   d   K  V e   VP                  4       V 9  d   K9  WgP                  4       ,          pKQ  	  \        \        V4      4      # )
z!Get the ids of games in database.zSELECT games.id FROM games INNER JOIN games_categories ON games.id = games_categories.game_id INNER JOIN categories ON categories.id = games_categories.category_idzcategories.name IN (%s)z, Z?zSELECT games.id FROM gameszNOT EXISTS(SELECT * FROM games_categories AS gc INNER JOIN categories AS c ON gc.category_id = c.id WHERE gc.game_id = games.id AND c.name IN (%s))z	 WHERE %sz AND c              3   r$   r%   r   r'   r)   r   r*   Z.get_game_ids_for_categories.<locals>.<genexpr>   s     a(`d(`r+   )rR   Zjoinr   ZlenZextendr,   r	   r^   r   rL   Ztuple_SMART_CATEGORIESr    r.   rQ   Zsorted)Zincluded_category_namesZexcluded_category_namesZfiltersr`   r_   Zexclude_filterZresultZ	smart_cats   &&      r   get_game_ids_for_categoriesrd   m   sI   GJT 	
 	0499VCMdIe=f3ggh12 -" %)IIfS#>U:V.W$XY 	 	~&12w||G444aX5E5EueT^N_(`aaF&	".93E3E3GKb3b".93E3E3GOf3f((** ' vr   c                :    V ^8  d   QhR\         \        ,          /# r   r#   rG   rH   r   r   r      s     3 3CH 3r   c                 st    Rp \         P                  ! \        P                  V 4      p\	        R V 4       4      # )zReturns the ids of games that are in no categories. We do not count
the 'favorites' category, but we do count '.hidden'- hidden games are hidden
from this too.zSELECT games.id FROM games WHERE NOT EXISTS(SELECT * FROM games_categories INNER JOIN categories ON categories.id = games_categories.category_id AND categories.name NOT IN ('all', 'favorite') WHERE games.id = games_categories.game_id)c              3   r$   r%   r   )r(   rS   r)   r   r*   Z-get_uncategorized_game_ids.<locals>.<genexpr>   s     2MS4yyMr+   )r	   r^   r   rL   r,   )r_   ZuncategorizedrT   r   r=   r=      s5    
	5 
 LL!1!159M2M222r   c                re   r   r/   rG   rH   r   r   r      s      c r   c                 sr    \         P                  ! \        4       4      p R R lpV P                  VR7       V # )z(Return a list of currently running gamesc                sF    V ^8  d   QhR\         \        \        3,          /# )r   g)r   r   r   rG   rH   r   r   Z-get_uncategorized_games.<locals>.__annotate__   s     9 94S> 9r   c                s    \        V P                  R4      ;'       g    R4      p\        V P                  R4      4      pV'       * \        V4      3# )zJSort in the default order for Lutris- installed games first, then by name.rC   r?   	installed)r   Zgetr\   r   )rf   rC   rg      &  r   get_keyZ(get_uncategorized_games.<locals>.get_key   sB    155=&&B'{+,	}24888r   )Zkey)games_dbZget_games_by_idsr=   Zsort)r   ri   rT   r   r>   r>      s0    %%&@&BCE9 
JJ7JLr   c                s    Rp\         P                  ! \        P                  W34       Uu. uF  q"R,          NK  	  up# u upi )z)Get the categories of a game in database.zSELECT categories.name FROM categories JOIN games_categories ON categories.id = games_categories.category_id JOIN games ON games.id = games_categories.game_id WHERE games.id=?rC   r]   )rO   r_   categoryrh   r   get_categories_in_gamerl      sB    	 
 .1\\(:J:JES]-^_-^V-^___s   ?c                s$    V ^8  d   QhR\         /# )r   	no_signal)r\   rG   rH   r   r   r      s      4 r   c                s    \         P                  ! \        P                  RRV /4      pV'       g   \        P                  4        V# )zAdd a category to the databaserI   rC   )r	   	db_insertr   rL   CATEGORIES_UPDATEDfire)Zcategory_namerm   ra   s   && r   add_categoryrq      s3    
--((,8O
PC!Jr   c                s@    V ^8  d   QhR\         R\        R\        RR/# )r   rP   new_namerm   r   N)rF   r   r\   rG   rH   r   r   r      s)     " "3 "# "$ "SW "r   c                 s   R p\         P                  ! \        P                  4      ;_uu_ 4       p\         P                  ! WCW34       RRR4       V'       g   \
        P                  4        R# R#   + '       g   i     L0; i)z'UPDATE categories SET name=? WHERE id=?Nr	   	db_cursorr   rL   cursor_executero   rp   )rP   rr   rm   r_   cursors   &&&  r   redefine_categoryrw      sS    5E	x''	(	(F68*AB 
)!  
)	(s   A00B 	c                s4    V ^8  d   QhR\         R\        RR/# )r   rP   rm   r   N)rF   r\   rG   rH   r   r   r      s!     " " " "$ "r   c                 s   R R.pV FP  p\         P                  ! \        P                  4      ;_uu_ 4       p\         P                  ! WCV 34       RRR4       KR  	  V'       g   \
        P                  4        R# R#   + '       g   i     K  ; i)z0DELETE FROM games_categories WHERE category_id=?z!DELETE FROM categories WHERE id=?Nrs   )rP   rm   Zqueriesr_   rv   s   &&   r   remove_categoryrx      sj    ACfgG]]8++,,v{n= -,  !  -,,s   A;;Bc                sV    \         P                  ! \        P                  RRV RV/4      # )zAdd a category to a gamerN   rO   rP   )r	   rn   r   rL   )rO   rP   s   &&r   add_game_to_categoryry      s'    ==))+=	7Tacn?oppr   c                s    Rp\         P                  ! \        P                  4      ;_uu_ 4       p\         P                  ! W2W34       RRR4       R#   + '       g   i     R# ; i)zRemove a category from a gamez>DELETE FROM games_categories WHERE category_id=? AND game_id=?N)r	   rt   r   rL   ru   )rO   rP   r_   rv   s   &&  r   remove_category_from_gamerz      s@    LE	x''	(	(F6;*@A 
)	(	(	(s   AA#	c            	     s   Rp \         P                  ! \        P                  4      ;_uu_ 4       p\         P                  ! WR4       RRR4       Rp\         P
                  ! \        P                  V4      pV Fi  pVR,          R8X  d   K  Rp\         P                  ! \        P                  4      ;_uu_ 4       p\         P                  ! WVR,          34       RRR4       Kk  	  R#   + '       g   i     L; i  + '       g   i     K  ; i)	z=Remove all categories that have no games associated with themzDELETE FROM games_categories WHERE NOT EXISTS(SELECT * FROM games WHERE game_id=id) OR NOT EXISTS(SELECT * FROM categories WHERE category_id=id)NzSELECT categories.* FROM categories LEFT JOIN games_categories ON categories.id = games_categories.category_id WHERE games_categories.category_id IS NULLrC   rE   z,DELETE FROM categories WHERE categories.id=?r&   r   )r	   rt   r   rL   ru   r^   )Zdelete_orphaned_gamesrv   Zfind_orphaned_categoriesZempty_categoriesrk   Zdelete_orphaned_categoriess         r   remove_unused_categoriesr{      s    	G  
x''	(	(F6"= 
)	5  ||H$4$46NO$Fz)%S"]]8++,,vHTNCTU -, % 
)	( -,,s   C'9 C:'C7	:Dc                sV    V ^8  d   Qh/ ^ \         9   d   \        \        ,          ;R&   # )r   rc   )__conditional_annotations__r   r   rG   rH   r   r   r      s     

Z J I4' I[ r   )F)NN).r|   r5   rA   Zcollectionsr    Z	itertoolsr   Ztypingr   r   r   r   r   Zlutrisr   Zlutris.databaser   rj   r	   Zlutris.gui.widgetsr
   Zlutris.util.stringsr   ro   ZABCr   r:   rc   rD   r@   rM   rU   rY   rZ   rb   rd   r=   r>   rl   rq   rw   rx   ry   rz   r{   r   )r|   r9   r   <module>r}      s    

 	 #  . .  -  1 4') SWW 
). 
) ,G+H*I  IE9&( V3`""q
BVr   