19
19
from pathlib import Path
20
20
import traceback
21
21
from operator import attrgetter
22
+ from collections import namedtuple
22
23
23
24
import gi
24
25
gi .require_version ('Gtk' , '3.0' )
@@ -611,6 +612,8 @@ def __init__(self, category):
611
612
612
613
self .add (box )
613
614
615
+ PkgInfoSearchCache = namedtuple ('PkgInfoSearchCache' , ['name' , 'display_name' , 'keywords' , 'summary' , 'description' ])
616
+
614
617
class Application (Gtk .Application ):
615
618
(ACTION_TAB , PROGRESS_TAB , SPINNER_TAB ) = list (range (3 ))
616
619
@@ -1072,6 +1075,8 @@ def on_installer_ready(self):
1072
1075
# Can take some time, don't block for it (these are categorizing packages based on apt info, not our listings)
1073
1076
GLib .idle_add (self .process_unmatched_packages )
1074
1077
1078
+ GLib .idle_add (self .pregenerate_search_cache )
1079
+
1075
1080
housekeeping .run ()
1076
1081
1077
1082
self .refresh_cache_menuitem .set_sensitive (True )
@@ -1397,11 +1402,11 @@ def on_process_exited(proc, result):
1397
1402
# Add a callback when we exit mintsources
1398
1403
p .wait_async (None , on_process_exited )
1399
1404
1400
- def should_show_pkginfo (self , pkginfo ):
1405
+ def should_show_pkginfo (self , pkginfo , allow_unverified_flatpaks ):
1401
1406
if pkginfo .pkg_hash .startswith ("apt" ):
1402
1407
return True
1403
1408
1404
- if not self . settings . get_boolean ( prefs . ALLOW_UNVERIFIED_FLATPAKS ) :
1409
+ if not allow_unverified_flatpaks :
1405
1410
return pkginfo .verified
1406
1411
1407
1412
return pkginfo .refid .startswith ("app/" )
@@ -1753,6 +1758,10 @@ def on_search_changed(self, searchentry):
1753
1758
self .show_category (self .current_category )
1754
1759
elif terms != "" and len (terms ) >= 3 :
1755
1760
self .show_search_results (terms )
1761
+ elif terms == "" :
1762
+ page = self .page_stack .get_visible_child_name ()
1763
+ if page == self .PAGE_LIST or page == self .PAGE_SEARCHING :
1764
+ self .go_back_action ()
1756
1765
1757
1766
self .search_changed_timer = 0
1758
1767
return False
@@ -2185,6 +2194,11 @@ def show_active_tasks(self):
2185
2194
def on_back_button_clicked (self , button ):
2186
2195
self .go_back_action ()
2187
2196
2197
+ def cancel_running_search (self ):
2198
+ if self .search_idle_timer > 0 :
2199
+ GLib .source_remove (self .search_idle_timer )
2200
+ self .search_idle_timer = 0
2201
+
2188
2202
def go_back_action (self ):
2189
2203
XApp .set_window_progress (self .main_window , 0 )
2190
2204
self .stop_progress_pulse ()
@@ -2202,6 +2216,8 @@ def go_back_action(self):
2202
2216
self .installer .cancel_task (self .current_task )
2203
2217
self .current_task = None
2204
2218
2219
+ self .cancel_running_search ()
2220
+
2205
2221
if self .page_stack .get_visible_child_name () == self .PAGE_PREFS :
2206
2222
self .search_tool_item .set_sensitive (True )
2207
2223
@@ -2299,6 +2315,37 @@ def get_application_icon(self, pkginfo, size):
2299
2315
2300
2316
return imaging .get_icon (icon_string , size )
2301
2317
2318
+ def update_package_search_cache (self , pkginfo , search_in_description ):
2319
+ if not hasattr (pkginfo , "search_cache" ):
2320
+ pkginfo .search_cache = PkgInfoSearchCache (
2321
+ name = pkginfo .name .upper (),
2322
+ display_name = pkginfo .get_display_name ().upper (),
2323
+ keywords = pkginfo .get_keywords ().upper (),
2324
+ summary = pkginfo .get_summary ().upper (),
2325
+ description = None
2326
+ if not search_in_description
2327
+ else self .installer .get_description (pkginfo , for_search = True ).upper ()
2328
+ )
2329
+
2330
+ # installer.get_description() is very slow, so we only fetch it if it's required
2331
+ if search_in_description and pkginfo .search_cache .description is None :
2332
+ description = self .installer .get_description (pkginfo , True ).upper ()
2333
+ pkginfo .search_cache = pkginfo .search_cache ._replace (description = description )
2334
+
2335
+ def pregenerate_search_cache (self ):
2336
+ search_in_description = self .settings .get_boolean (prefs .SEARCH_IN_DESCRIPTION )
2337
+ pkginfos = self .installer .cache .values ()
2338
+
2339
+ def generate_package_cache (pkginfos ):
2340
+ try :
2341
+ pkginfo = next (pkginfos )
2342
+ self .update_package_search_cache (pkginfo , search_in_description )
2343
+ return True
2344
+ except StopIteration :
2345
+ return False
2346
+
2347
+ GLib .idle_add (generate_package_cache , iter (pkginfos ))
2348
+
2302
2349
@print_timing
2303
2350
def show_search_results (self , terms ):
2304
2351
if not self .gui_ready :
@@ -2333,53 +2380,65 @@ def show_search_results(self, terms):
2333
2380
2334
2381
searched_packages = []
2335
2382
2336
- if self .search_idle_timer > 0 :
2337
- GLib .source_remove (self .search_idle_timer )
2338
- self .search_idle_timer = 0
2383
+ self .cancel_running_search ()
2339
2384
2340
2385
search_in_summary = self .settings .get_boolean (prefs .SEARCH_IN_SUMMARY )
2341
2386
search_in_description = self .settings .get_boolean (prefs .SEARCH_IN_DESCRIPTION )
2342
2387
2343
2388
package_type_preference = self .settings .get_string (prefs .PACKAGE_TYPE_PREFERENCE )
2344
2389
hidden_packages = set ()
2390
+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
2345
2391
2346
2392
def idle_search_one_package (pkginfos ):
2347
2393
try :
2348
- pkginfo = pkginfos . pop ( 0 )
2349
- except IndexError :
2394
+ pkginfo = next ( pkginfos )
2395
+ except StopIteration :
2350
2396
self .search_idle_timer = 0
2397
+
2398
+ if package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_APT :
2399
+ results = [p for p in searched_packages if not (p .pkg_hash .startswith ("f" ) and p .name in hidden_packages )]
2400
+ elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK :
2401
+ results = [p for p in searched_packages if not (p .pkg_hash .startswith ("a" ) and p .name in hidden_packages )]
2402
+ else :
2403
+ results = searched_packages
2404
+
2405
+ GLib .idle_add (self .on_search_results_complete , results )
2351
2406
return False
2352
2407
2353
2408
flatpak = pkginfo .pkg_hash .startswith ("f" )
2354
2409
is_match = False
2355
2410
2356
2411
while True :
2357
- if not self .should_show_pkginfo (pkginfo ):
2412
+ if not self .should_show_pkginfo (pkginfo , allow_unverified_flatpaks ):
2358
2413
break
2359
2414
2360
- if all (piece in pkginfo .name .upper () for piece in termsSplit ):
2415
+ self .update_package_search_cache (pkginfo , search_in_description )
2416
+
2417
+ if all (piece in pkginfo .search_cache .name for piece in termsSplit ):
2361
2418
is_match = True
2362
2419
pkginfo .search_tier = 0
2363
2420
break
2421
+
2364
2422
# pkginfo.name for flatpaks is their id (org.foo.BarMaker), which
2365
2423
# may not actually contain the app's name. In this case their display
2366
2424
# names are better. The 'name' is still checked first above, because
2367
2425
# it's static - get_display_name() may involve a lookup with appstream.
2368
- if flatpak and all (piece in pkginfo .get_display_name (). upper () for piece in termsSplit ):
2426
+ if flatpak and all (piece in pkginfo .search_cache . display_name for piece in termsSplit ):
2369
2427
is_match = True
2370
2428
pkginfo .search_tier = 0
2371
2429
break
2372
2430
2373
- if termsUpper in pkginfo .get_keywords (). upper () :
2431
+ if termsUpper in pkginfo .search_cache . keywords :
2374
2432
is_match = True
2375
2433
pkginfo .search_tier = 50
2376
2434
break
2377
2435
2378
- if (search_in_summary and termsUpper in pkginfo .get_summary (). upper () ):
2436
+ if (search_in_summary and termsUpper in pkginfo .search_cache . summary ):
2379
2437
is_match = True
2380
2438
pkginfo .search_tier = 100
2381
2439
break
2382
- if (search_in_description and termsUpper in self .installer .get_description (pkginfo ).upper ()):
2440
+
2441
+ if (search_in_description and termsUpper in pkginfo .search_cache .description ):
2383
2442
is_match = True
2384
2443
pkginfo .search_tier = 200
2385
2444
break
@@ -2392,23 +2451,9 @@ def idle_search_one_package(pkginfos):
2392
2451
elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK and flatpak :
2393
2452
hidden_packages .add (DEB_EQUIVS .get (pkginfo .name ))
2394
2453
2395
- # Repeat until empty
2396
- if len (pkginfos ) > 0 :
2397
- return True
2398
-
2399
- self .search_idle_timer = 0
2400
-
2401
- if package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_APT :
2402
- results = [p for p in searched_packages if not (p .pkg_hash .startswith ("f" ) and p .name in hidden_packages )]
2403
- elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK :
2404
- results = [p for p in searched_packages if not (p .pkg_hash .startswith ("a" ) and p .name in hidden_packages )]
2405
- else :
2406
- results = searched_packages
2407
-
2408
- GLib .idle_add (self .on_search_results_complete , results )
2409
- return False
2454
+ return True
2410
2455
2411
- self .search_idle_timer = GLib .idle_add (idle_search_one_package , list (listing ))
2456
+ self .search_idle_timer = GLib .idle_add (idle_search_one_package , iter (listing ))
2412
2457
2413
2458
def on_search_results_complete (self , results ):
2414
2459
self .page_stack .set_visible_child_name (self .PAGE_LIST )
@@ -2479,6 +2524,7 @@ def sort_packages(self, pkgs, key_func):
2479
2524
2480
2525
def show_packages (self , pkginfos , from_search = False ):
2481
2526
self .stop_slideshow_timer ()
2527
+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
2482
2528
2483
2529
if self .one_package_idle_timer > 0 :
2484
2530
GLib .source_remove (self .one_package_idle_timer )
@@ -2515,7 +2561,7 @@ def show_packages(self, pkginfos, from_search=False):
2515
2561
apps = [info for info in pkginfos ] # should_show_pkginfo was applied during search matching
2516
2562
apps = self .sort_packages (apps , attrgetter ("unverified" , "search_tier" , "score_desc" , "name" ))
2517
2563
else :
2518
- apps = [info for info in pkginfos if self .should_show_pkginfo (info )]
2564
+ apps = [info for info in pkginfos if self .should_show_pkginfo (info , allow_unverified_flatpaks )]
2519
2565
apps = self .sort_packages (apps , attrgetter ("unverified" , "score_desc" , "name" ))
2520
2566
apps = apps [0 :201 ]
2521
2567
@@ -2845,10 +2891,11 @@ def on_package_type_button_clicked(self, button, pkginfo):
2845
2891
self .show_package (pkginfo , self .previous_page )
2846
2892
2847
2893
def get_flatpak_for_deb (self , pkginfo ):
2894
+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
2848
2895
try :
2849
2896
fp_name = FLATPAK_EQUIVS [pkginfo .name ]
2850
2897
flatpak_pkginfo = self .installer .find_pkginfo (fp_name , installer .PKG_TYPE_FLATPAK )
2851
- if self .should_show_pkginfo (flatpak_pkginfo ):
2898
+ if self .should_show_pkginfo (flatpak_pkginfo , allow_unverified_flatpaks ):
2852
2899
return flatpak_pkginfo
2853
2900
except :
2854
2901
return None
0 commit comments