1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
diff --git a/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/applet.js b/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/applet.js
index 22303a136..d86f95c9f 100644
--- a/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/applet.js
+++ b/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/applet.js
@@ -639,7 +639,89 @@ class GroupedWindowListApplet extends Applet.Applet {
this.state.set({lastTitleDisplay: titleDisplay});
}
+ _matchFavoriteToWindow(fav, wmClass, wmInstance, gtkAppId) {
+ if (!fav || !fav.app) {
+ return false;
+ }
+
+ let favId = fav.id.toLowerCase();
+ // Get base name without path and .desktop
+ let baseName = favId.substring(favId.lastIndexOf('/') + 1).replace('.desktop', '');
+
+ // 1. Direct match on desktop ID base name (case-insensitive)
+ if (baseName === wmClass || baseName === wmInstance || (gtkAppId && baseName === gtkAppId)) {
+ return true;
+ }
+
+ // 2. Match on StartupWMClass from the app info
+ let appInfo = fav.app.get_app_info();
+ if (appInfo) {
+ let startupClass = appInfo.get_startup_wm_class ? appInfo.get_startup_wm_class() : null;
+ if (!startupClass) {
+ try {
+ startupClass = appInfo.get_string("StartupWMClass");
+ } catch (e) {}
+ }
+ if (startupClass) {
+ startupClass = startupClass.toLowerCase();
+ if (startupClass === wmClass || startupClass === wmInstance) {
+ return true;
+ }
+ }
+
+ // 3. Match executable/command name
+ let exec = appInfo.get_executable ? appInfo.get_executable() : null;
+ if (!exec) {
+ try {
+ let cmd = appInfo.get_commandline ? appInfo.get_commandline() : "";
+ if (cmd) {
+ exec = cmd.split(' ')[0];
+ }
+ } catch (e) {}
+ }
+ if (exec) {
+ let execBase = exec.substring(exec.lastIndexOf('/') + 1).toLowerCase();
+ if (execBase === wmClass || execBase === wmInstance) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ _matchWindowToPinnedApp(metaWindow) {
+ if (!this.pinnedFavorites || !this.pinnedFavorites._favorites) {
+ return null;
+ }
+
+ let wmClass = metaWindow.get_wm_class();
+ let wmInstance = metaWindow.get_wm_class_instance();
+ let gtkAppId = metaWindow.get_gtk_application_id ? metaWindow.get_gtk_application_id() : null;
+
+ wmClass = wmClass ? wmClass.toLowerCase() : "";
+ wmInstance = wmInstance ? wmInstance.toLowerCase() : "";
+ gtkAppId = gtkAppId ? gtkAppId.toLowerCase() : "";
+
+ // Iterate through pinned favorites
+ for (let fav of this.pinnedFavorites._favorites) {
+ if (this._matchFavoriteToWindow(fav, wmClass, wmInstance, gtkAppId)) {
+ return fav.app;
+ }
+ }
+ return null;
+ }
+
getAppFromWindow(metaWindow) {
+ if (!metaWindow) {
+ return null;
+ }
+
+ // Try matching against pinned favorites first to prevent duplicate icons
+ let pinnedMatch = this._matchWindowToPinnedApp(metaWindow);
+ if (pinnedMatch) {
+ return pinnedMatch;
+ }
+
const tracker = this.state.trigger('getTracker');
if (!tracker) {
return null;
@@ -651,6 +733,20 @@ class GroupedWindowListApplet extends Applet.Applet {
if (!app) {
app = tracker.get_app_from_pid(metaWindow.get_client_pid());
}
+
+ // Fallback: If the tracker returned a transient app, check if its ID matches a pinned favorite
+ if (app && this.pinnedFavorites && this.pinnedFavorites._favorites) {
+ let appId = app.get_id().toLowerCase();
+ for (let fav of this.pinnedFavorites._favorites) {
+ if (!fav || !fav.app) continue;
+ let favId = fav.id.toLowerCase();
+ let baseName = favId.substring(favId.lastIndexOf('/') + 1).replace('.desktop', '');
+ if (appId === baseName || appId === favId) {
+ return fav.app;
+ }
+ }
+ }
+
return app;
}
@@ -1030,6 +1126,9 @@ class GroupedWindowListApplet extends Applet.Applet {
onWindowSkipTaskbarChanged(display, metaWindow) {
const currentWorkspace = this.getCurrentWorkspace();
+ if (!currentWorkspace) {
+ return;
+ }
if (metaWindow.is_skip_taskbar()) {
currentWorkspace.windowRemoved(currentWorkspace.metaWorkspace, metaWindow);
|