diff -Nru nicotine-2.1.3/debian/changelog nicotine-2.1.3/debian/changelog --- nicotine-2.1.3/debian/changelog 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/debian/changelog 2020-10-29 05:50:09.000000000 +0000 @@ -1,8 +1,8 @@ -nicotine (2.1.3-202010280117~ubuntu20.04.1) focal; urgency=low +nicotine (2.1.3-202010290550~ubuntu20.04.1) focal; urgency=low * Auto build. - -- Launchpad Package Builder Wed, 28 Oct 2020 01:17:36 +0000 + -- Launchpad Package Builder Thu, 29 Oct 2020 05:50:09 +0000 nicotine (2.1.3-dev1) groovy; urgency=medium diff -Nru nicotine-2.1.3/debian/git-build-recipe.manifest nicotine-2.1.3/debian/git-build-recipe.manifest --- nicotine-2.1.3/debian/git-build-recipe.manifest 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/debian/git-build-recipe.manifest 2020-10-29 05:50:09.000000000 +0000 @@ -1,2 +1,2 @@ -# git-build-recipe format 0.4 deb-version {debupstream}-202010280117 -lp:nicotine+ git-commit:0afa7312d0ac0ee5d18148d8f61b4097aecb791e +# git-build-recipe format 0.4 deb-version {debupstream}-202010290550 +lp:nicotine+ git-commit:2c2bf78d2046d4272c11843a53e8cc30a5a567dd diff -Nru nicotine-2.1.3/doc/SLSKPROTOCOL.md nicotine-2.1.3/doc/SLSKPROTOCOL.md --- nicotine-2.1.3/doc/SLSKPROTOCOL.md 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/doc/SLSKPROTOCOL.md 2020-10-29 05:50:09.000000000 +0000 @@ -1,6 +1,6 @@ # Soulseek Protocol Documentation -Last updated on 11 October 2020 +Last updated on 28 October 2020 ## Sections @@ -93,98 +93,101 @@ #### Message Index -| Code | Message | -| ---- | ------------------------------------------------- | -| 1 | [Login](#server-code-1) | -| 2 | [Set Listen Port](#server-code-2) | -| 3 | [Get Peer Address](#server-code-3) | -| 5 | [Add User](#server-code-5) | -| 6 | [Remove User](#server-code-6) | -| 7 | [Get Status](#server-code-7) | -| 13 | [Say in Chat Room](#server-code-13) | -| 14 | [Join Room](#server-code-14) | -| 15 | [Leave Room](#server-code-15) | -| 16 | [User Joined Room](#server-code-16) | -| 17 | [User Left Room](#server-code-17) | -| 18 | [Connect To Peer](#server-code-18) | -| 22 | [Private Messages](#server-code-22) | -| 23 | [Acknowledge Private Message](#server-code-23) | -| 26 | [File Search](#server-code-26) | -| 28 | [Set Online Status](#server-code-28) | -| 32 | [Ping](#server-code-32) | -| 34 | [Send Speed](#server-code-34) | -| 35 | [Shared Folders & Files](#server-code-35) | -| 36 | [Get User Stats](#server-code-36) | -| 40 | [Queued Downloads](#server-code-40) | -| 41 | [Kicked from Server](#server-code-41) | -| 42 | [User Search](#server-code-42) | -| 51 | [Interest Add](#server-code-51) | -| 52 | [Interest Remove](#server-code-52) | -| 54 | [Get Recommendations](#server-code-54) | -| 56 | [Get Global Recommendations](#server-code-56) | -| 57 | [Get User Interests](#server-code-57) | -| 60 | [Place In Line Response](#server-code-60) | -| 62 | [Room Added](#server-code-62) | -| 63 | [Room Removed](#server-code-63) | -| 64 | [Room List](#server-code-64) | -| 65 | [Exact File Search](#server-code-65) | -| 66 | [Global/Admin Message](#server-code-66) | -| 67 | [Global User List](#server-code-67) | -| 69 | [Privileged Users](#server-code-69) | -| 71 | [Have No Parents](#server-code-71) | -| 73 | [Parent's IP](#server-code-73) | -| 83 | [Parent Min Speed](#server-code-83) | -| 84 | [Parent Speed Ratio](#server-code-84) | -| 86 | [Parent Inactivity Timeout](#server-code-86) | -| 87 | [Search Inactivity Timeout](#server-code-87) | -| 88 | [Minimum Parents In Cache](#server-code-88) | -| 90 | [Distributed Alive Interval](#server-code-90) | -| 91 | [Add Privileged User](#server-code-91) | -| 92 | [Check Privileges](#server-code-92) | -| 93 | [Search Request](#server-code-93) | -| 100 | [Accept Children](#server-code-100) | -| 102 | [Possible Parents](#server-code-102) | -| 103 | [Wishlist Search](#server-code-103) | -| 104 | [Wishlist Interval](#server-code-104) | -| 110 | [Get Similar Users](#server-code-110) | -| 111 | [Get Item Recommendations](#server-code-111) | -| 112 | [Get Item Similar Users](#server-code-112) | -| 113 | [Room Tickers](#server-code-113) | -| 114 | [Room Ticker Add](#server-code-114) | -| 115 | [Room Ticker Remove](#server-code-115) | -| 116 | [Set Room Ticker](#server-code-116) | -| 117 | [Hated Interest Add](#server-code-117) | -| 118 | [Hated Interest Remove](#server-code-118) | -| 120 | [Room Search](#server-code-120) | -| 121 | [Send Upload Speed](#server-code-121) | -| 122 | [User Privileges](#server-code-122) | -| 123 | [Give Privileges](#server-code-123) | -| 124 | [Notify Privileges](#server-code-124) | -| 125 | [Acknowledge Notify Privileges](#server-code-125) | -| 126 | [Branch Level](#server-code-126) | -| 127 | [Branch Root](#server-code-127) | -| 129 | [Child Depth](#server-code-129) | -| 133 | [Private Room Users](#server-code-133) | -| 134 | [Private Room Add User](#server-code-134) | -| 135 | [Private Room Remove User](#server-code-135) | -| 136 | [Private Room Drop Membership](#server-code-136) | -| 137 | [Private Room Drop Ownership](#server-code-137) | -| 138 | [Private Room Unknown](#server-code-138) | -| 139 | [Private Room Added](#server-code-139) | -| 140 | [Private Room Removed](#server-code-140) | -| 141 | [Private Room Toggle](#server-code-141) | -| 142 | [New Password](#server-code-142) | -| 143 | [Private Room Add Operator](#server-code-143) | -| 144 | [Private Room Remove Operator](#server-code-144) | -| 145 | [Private Room Operator Added](#server-code-145) | -| 146 | [Private Room Operator Removed](#server-code-146) | -| 148 | [Private Room Owned](#server-code-148) | -| 149 | [Message Users](#server-code-149) | -| 150 | [Ask Public Chat](#server-code-150) | -| 151 | [Stop Public Chat](#server-code-151) | -| 152 | [Public Chat Message](#server-code-152) | -| 153 | [Related Searches](#server-code-153) | -| 1001 | [Cannot Connect](#server-code-1001) | +| Code | Message | Status | +| ---- | ------------------------------------------------- | ---------- | +| 1 | [Login](#server-code-1) | | +| 2 | [Set Listen Port](#server-code-2) | | +| 3 | [Get Peer Address](#server-code-3) | | +| 5 | [Add User](#server-code-5) | | +| 6 | [Remove User](#server-code-6) | | +| 7 | [Get Status](#server-code-7) | | +| 13 | [Say in Chat Room](#server-code-13) | | +| 14 | [Join Room](#server-code-14) | | +| 15 | [Leave Room](#server-code-15) | | +| 16 | [User Joined Room](#server-code-16) | | +| 17 | [User Left Room](#server-code-17) | | +| 18 | [Connect To Peer](#server-code-18) | | +| 22 | [Private Messages](#server-code-22) | | +| 23 | [Acknowledge Private Message](#server-code-23) | | +| 26 | [File Search](#server-code-26) | | +| 28 | [Set Online Status](#server-code-28) | | +| 32 | [Ping](#server-code-32) | Deprecated | +| 33 | [Send Connect Token](#server-code-33) | Deprecated | +| 34 | [Send Download Speed](#server-code-34) | Deprecated | +| 35 | [Shared Folders & Files](#server-code-35) | | +| 36 | [Get User Stats](#server-code-36) | | +| 40 | [Queued Downloads](#server-code-40) | Deprecated | +| 41 | [Kicked from Server](#server-code-41) | | +| 42 | [User Search](#server-code-42) | | +| 51 | [Interest Add](#server-code-51) | | +| 52 | [Interest Remove](#server-code-52) | | +| 54 | [Get Recommendations](#server-code-54) | | +| 56 | [Get Global Recommendations](#server-code-56) | | +| 57 | [Get User Interests](#server-code-57) | | +| 60 | [Place In Line Response](#server-code-60) | Deprecated | +| 62 | [Room Added](#server-code-62) | Deprecated | +| 63 | [Room Removed](#server-code-63) | Deprecated | +| 64 | [Room List](#server-code-64) | | +| 65 | [Exact File Search](#server-code-65) | Deprecated | +| 66 | [Global/Admin Message](#server-code-66) | | +| 67 | [Global User List](#server-code-67) | Deprecated | +| 68 | [Tunneled Message](#server-code-68) | Deprecated | +| 69 | [Privileged Users](#server-code-69) | | +| 71 | [Have No Parents](#server-code-71) | | +| 73 | [Parent's IP](#server-code-73) | | +| 83 | [Parent Min Speed](#server-code-83) | | +| 84 | [Parent Speed Ratio](#server-code-84) | | +| 86 | [Parent Inactivity Timeout](#server-code-86) | Deprecated | +| 87 | [Search Inactivity Timeout](#server-code-87) | Deprecated | +| 88 | [Minimum Parents In Cache](#server-code-88) | Deprecated | +| 90 | [Distributed Alive Interval](#server-code-90) | Deprecated | +| 91 | [Add Privileged User](#server-code-91) | | +| 92 | [Check Privileges](#server-code-92) | | +| 93 | [Search Request](#server-code-93) | | +| 100 | [Accept Children](#server-code-100) | | +| 102 | [Possible Parents](#server-code-102) | | +| 103 | [Wishlist Search](#server-code-103) | | +| 104 | [Wishlist Interval](#server-code-104) | | +| 110 | [Get Similar Users](#server-code-110) | | +| 111 | [Get Item Recommendations](#server-code-111) | | +| 112 | [Get Item Similar Users](#server-code-112) | | +| 113 | [Room Tickers](#server-code-113) | | +| 114 | [Room Ticker Add](#server-code-114) | | +| 115 | [Room Ticker Remove](#server-code-115) | | +| 116 | [Set Room Ticker](#server-code-116) | | +| 117 | [Hated Interest Add](#server-code-117) | | +| 118 | [Hated Interest Remove](#server-code-118) | | +| 120 | [Room Search](#server-code-120) | | +| 121 | [Send Upload Speed](#server-code-121) | | +| 122 | [User Privileges](#server-code-122) | | +| 123 | [Give Privileges](#server-code-123) | | +| 124 | [Notify Privileges](#server-code-124) | | +| 125 | [Acknowledge Notify Privileges](#server-code-125) | | +| 126 | [Branch Level](#server-code-126) | | +| 127 | [Branch Root](#server-code-127) | | +| 129 | [Child Depth](#server-code-129) | | +| 133 | [Private Room Users](#server-code-133) | | +| 134 | [Private Room Add User](#server-code-134) | | +| 135 | [Private Room Remove User](#server-code-135) | | +| 136 | [Private Room Drop Membership](#server-code-136) | | +| 137 | [Private Room Drop Ownership](#server-code-137) | | +| 138 | [Private Room Unknown](#server-code-138) | | +| 139 | [Private Room Added](#server-code-139) | | +| 140 | [Private Room Removed](#server-code-140) | | +| 141 | [Private Room Toggle](#server-code-141) | | +| 142 | [New Password](#server-code-142) | | +| 143 | [Private Room Add Operator](#server-code-143) | | +| 144 | [Private Room Remove Operator](#server-code-144) | | +| 145 | [Private Room Operator Added](#server-code-145) | | +| 146 | [Private Room Operator Removed](#server-code-146) | | +| 148 | [Private Room Owned](#server-code-148) | | +| 149 | [Message Users](#server-code-149) | | +| 150 | [Ask Public Chat](#server-code-150) | | +| 151 | [Stop Public Chat](#server-code-151) | | +| 152 | [Public Chat Message](#server-code-152) | | +| 153 | [Related Searches](#server-code-153) | Deprecated | +| 1001 | [Can't Connect To Peer](#server-code-1001) | | +| 1002 | [Can't Create Room](#server-code-1002) | Deprecated | ### Server Code 1 @@ -416,7 +419,7 @@ **For private rooms, also contain owner and operators** 3. Iterate the number of users **museekd uses a vector of strings** - 1. **string** user + 1. **string** username 4. **int** number of userdata 5. Iterate the number of users **museekd uses a vector of userdata** @@ -666,14 +669,36 @@ - Receive - Empty Message +### Server Code 33 + +**Send Connect Token** + +#### Function Names + +Museekd: Unimplemented +Nicotine: SendConnectToken + +#### Description + +**DEPRECATED** + +#### Data Order + + - Send + 1. **string** username + 2. **int** token + - Receive + 1. **string** username + 2. **int** token + ### Server Code 34 -**Send Speed** +**Send Download Speed** #### Function Names Museekd: SSendSpeed -Nicotine: SendSpeed +Nicotine: SendDownloadSpeed #### Description @@ -952,11 +977,11 @@ #### Data Order - Send - 1. **string** user + 1. **string** username 2. **int** req 3. **int** place - Receive - 1. **string** user + 1. **string** username 2. **int** req 3. **int** place @@ -1133,7 +1158,7 @@ - Receive 1. **int** number of users in room 2. Iterate the number of users - 1. **string** user + 1. **string** username 3. **int** number of userdata 4. Iterate the number of users 1. **int** status @@ -1151,6 +1176,34 @@ 1. **string** countrycode **Uppercase country code** +### Server Code 68 + +**Tunneled Message** + +#### Function Names + +Museekd: Unimplemented +Nicotine: TunneledMessage + +#### Description + +**DEPRECATED** + +#### Data Order + + - Send + 1. **string** username + 2. **int** request + 3. **int** code + 4. **string** message + - Receive + 1. **string** username + 2. **int** code + 3. **int** request + 4. **ip** ip + 5. **int** port + 6. **string** message + ### Server Code 69 **Privileged Users** @@ -1171,7 +1224,7 @@ - Receive 1. **int** number of users 2. Iterate number of users - 1. **string** user + 1. **string** username ### Server Code 71 @@ -1356,7 +1409,7 @@ - Send - *No Message* - Receive - 1. **string** user + 1. **string** username ### Server Code 92 @@ -1443,8 +1496,8 @@ - Receive *list of search parents* 1. **int** number of parents 2. Iterate for number of parents - 1. **string** user - 2. **IP** IP address + 1. **string** username + 2. **ip** ip 3. **int** port ### Server Code 103 @@ -1504,7 +1557,7 @@ - Receive 1. **int** number of users 2. Iterate for number of user - 1. **string** user + 1. **string** username 2. **int** status ### Server Code 111 @@ -1555,7 +1608,7 @@ 1. **string** item 2. **int** number of users 3. Iterate for number of user - 1. **string** user + 1. **string** username 2. **int** 0 ### Server Code 113 @@ -1581,7 +1634,7 @@ 1. **string** room 2. **int** number of users 3. Iterate for number of user - 1. **string** user + 1. **string** username 2. **string** tickers ### Server Code 114 @@ -1605,7 +1658,7 @@ - *No Message* - Receive 1. **string** room - 2. **string** user + 2. **string** username 3. **string** ticker ### Server Code 115 @@ -1629,7 +1682,7 @@ - *No Message* - Receive 1. **string** room - 2. **string** user + 2. **string** username ### Server Code 116 @@ -1750,9 +1803,9 @@ #### Data Order - Send - 1. **string** user + 1. **string** username - Receive - 1. **string** user + 1. **string** username 2. **char** privileged (boolean internal to museekd) @@ -1772,7 +1825,7 @@ #### Data Order - Send - 1. **string** user + 1. **string** username 2. **int** days - Receive - *No Message* @@ -1793,10 +1846,10 @@ - Send 1. **int** token - 2. **string** user + 2. **string** username - Receive 1. **int** token - 2. **string** user + 2. **string** username ### Server Code 125 @@ -1915,10 +1968,10 @@ - Send 1. **string** room - 2. **string** user + 2. **string** username - Receive 1. **string** room - 2. **string** user + 2. **string** username ### Server Code 135 @@ -1937,10 +1990,10 @@ - Send 1. **string** room - 2. **string** user + 2. **string** username - Receive 1. **string** room - 2. **string** user + 2. **string** username ### Server Code 136 @@ -2208,7 +2261,7 @@ 1. **int** number of users 2. Iterate the number of users **museekd uses a vector of strings** - 1. **string** user + 1. **string** username 3. **string** message - Receive - *No Message* @@ -2272,7 +2325,7 @@ - *No Message* - Receive 1. **string** room - 2. **string** user + 2. **string** username 3. **string** message ### Server Code 153 @@ -2281,12 +2334,14 @@ #### Description +**DEPRECATED ? (empty list from server as of 2018)** + The server returns a list of related search terms for a search query. #### Function Names Museekd: SRelatedSearch -Nicotine: Unimplemented +Nicotine: RelatedSearch #### Data Order @@ -2301,7 +2356,7 @@ ### Server Code 1001 -**Cannot Connect** +**Can't Connect To Peer** #### Function Names @@ -2312,18 +2367,39 @@ We send this to say we can't connect to peer after it has asked us to connect. We receive this if we asked peer to connect and it can't do this. This message means a connection can't be established either way. -See also: [Peer Connection Message -Order](#peer-connection-message-order) +See also: [Peer Connection Message Order](#peer-connection-message-order) #### Data Order - Send *to the Server if we cannot connect to a peer.* 1. **int** token - 2. **string** user + 2. **string** username - Receive *this response means we are both firewalled or otherwise unable to connect to each other.* 1. **int** token - 2. **string** user + 2. **string** username + +### Server Code 1002 + +**Can't Create Room** + +#### Function Names + +Museekd: Unimplemented +Nicotine: CantConnectToPeer + +#### Description + +**DEPRECATED (server sends a private message now)** + +Server tells us a new room cannot be created. + +#### Data Order + + - Send + - *No Message* + - Receive + 1. **string** room # Peer Messages @@ -2406,12 +2482,12 @@ #### Data Order - Send - - **string** user *Local Username* + - **string** username *Local Username* - **string** type *Connection Type (P, F or D)* - **uint32** token *Unique Number* - Receive - - **string** user *Remote Username* + - **string** username *Remote Username* - **string** type *Connection Type (P, F or D)* - **uint32** token *Unique Number* @@ -2543,7 +2619,7 @@ #### Data Order - Send - 1. **string** user + 1. **string** username 2. **int** ticket 3. **int** results size *number of results* @@ -2562,7 +2638,7 @@ 7. **off\_t** queue length - Receive 1. decompress - 2. **string** user + 2. **string** username 3. **int** ticket 4. **int** results size number of results @@ -3020,12 +3096,12 @@ - Send 1. **int** unknown - 2. **string** user + 2. **string** username 3. **int** ticket 4. **string** query - Receive 1. **int** unknown - 2. **string** user + 2. **string** username 3. **int** ticket 4. **string** query @@ -3109,12 +3185,12 @@ - Send 1. **off_t** unknown *always 210503729152 (?)* - 2. **string** user + 2. **string** username 3. **int** ticket 4. **string** query - Receive 1. **off_t** unknown *always 210503729152 (?)* - 2. **string** user + 2. **string** username 3. **int** ticket 4. **string** query diff -Nru nicotine-2.1.3/img/CREDITS.md nicotine-2.1.3/img/CREDITS.md --- nicotine-2.1.3/img/CREDITS.md 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/img/CREDITS.md 2020-10-29 05:50:09.000000000 +0000 @@ -25,7 +25,6 @@ Created by daelstorm (released under GPLv2+ and upgraded to GPLv3+) away.png -empty.png offline.png online.png Binary files /tmp/tmpb9tYeI/wIOIDlC89i/nicotine-2.1.3/img/empty.png and /tmp/tmpb9tYeI/A2yvnI9rFD/nicotine-2.1.3/img/empty.png differ diff -Nru nicotine-2.1.3/img/scripts/encode_bitmaps.py nicotine-2.1.3/img/scripts/encode_bitmaps.py --- nicotine-2.1.3/img/scripts/encode_bitmaps.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/img/scripts/encode_bitmaps.py 2020-10-29 05:50:09.000000000 +0000 @@ -28,7 +28,6 @@ ["img/away.png", "away"], ["img/online.png", "online"], ["img/offline.png", "offline"], - ["img/empty.png", "empty"], ["img/hilite.png", "hilite"], ["img/hilite3.png", "hilite3"], ["files/org.nicotine_plus.Nicotine.svg", "n"], @@ -39,7 +38,7 @@ p = os.path.join("img", "tray", name) if isfile(p): - table.append([p, "trayicon_%s" % name[:-4]]) + table.append([p, "trayicon_%s" % name[27:-4]]) flagtable = [] diff -Nru nicotine-2.1.3/pynicotine/gtkgui/chatrooms.py nicotine-2.1.3/pynicotine/gtkgui/chatrooms.py --- nicotine-2.1.3/pynicotine/gtkgui/chatrooms.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/chatrooms.py 2020-10-29 05:50:09.000000000 +0000 @@ -231,16 +231,13 @@ # Save self.frame.np.config.sections["server"]["autojoin"] = new_autojoin - def on_switch_page(self, notebook, page, page_num, force=0): + def on_switch_page(self, notebook, page, page_num, forceupdate=False): - if self.frame.MainNotebook.get_current_page() != self.frame.MainNotebook.page_num(self.frame.chathbox) and not force: + if self.frame.MainNotebook.get_current_page() != self.frame.MainNotebook.page_num(self.frame.chathbox) and not forceupdate: return - # page is None? - new_page = notebook.get_nth_page(page_num) - for name, room in self.joinedrooms.items(): - if room.Main == new_page: + if room.Main == page: GLib.idle_add(room.ChatEntry.grab_focus) # Remove hilite @@ -2022,7 +2019,7 @@ self.frame.images, angle=config["ui"]["labelrooms"], tabclosers=config["ui"]["tabclosers"], - show_image=config["notifications"]["notification_tab_icons"], + show_hilite_image=config["notifications"]["notification_tab_icons"], reorderable=config["ui"]["tab_reorderable"], notebookraw=self.frame.ChatNotebookRaw ) diff -Nru nicotine-2.1.3/pynicotine/gtkgui/dirchooser.py nicotine-2.1.3/pynicotine/gtkgui/dirchooser.py --- nicotine-2.1.3/pynicotine/gtkgui/dirchooser.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/dirchooser.py 2020-10-29 05:50:09.000000000 +0000 @@ -130,6 +130,8 @@ else: dialog.set_current_folder(os.path.expanduser("~")) + dialog.set_current_name(initialfile) + response = dialog.run() if response == Gtk.ResponseType.ACCEPT: diff -Nru nicotine-2.1.3/pynicotine/gtkgui/frame.py nicotine-2.1.3/pynicotine/gtkgui/frame.py --- nicotine-2.1.3/pynicotine/gtkgui/frame.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/frame.py 2020-10-29 05:50:09.000000000 +0000 @@ -54,7 +54,7 @@ from pynicotine.gtkgui.roomlist import RoomList from pynicotine.gtkgui.search import Searches from pynicotine.gtkgui.settingswindow import Settings -from pynicotine.gtkgui.tray import TrayApp +from pynicotine.gtkgui.tray import Tray from pynicotine.gtkgui.uploads import Uploads from pynicotine.gtkgui.userbrowse import UserBrowse from pynicotine.gtkgui.userinfo import UserInfo @@ -85,11 +85,11 @@ self.clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) self.clip_data = "" self.data_dir = data_dir - self.away = 0 self.current_tab = 0 self.rescanning = False self.brescanning = False self.needrescan = False + self.away = False self.autoaway = False self.awaytimerid = None self.bindip = bindip @@ -184,7 +184,7 @@ self.MainWindow.connect("delete-event", self.on_delete_event) self.MainWindow.connect("destroy", self.on_destroy) self.MainWindow.connect("key_press_event", self.on_key_press) - self.MainWindow.connect("motion-notify-event", self.on_button_press) + self.MainWindow.connect("motion-notify-event", self.on_disable_auto_away) # Set up actions for menubar self.set_up_actions() @@ -195,7 +195,7 @@ """ Tray/notifications """ - self.tray_app = TrayApp(self) + self.tray = Tray(self) self.notifications = Notifications(self) self.hilites = { @@ -207,7 +207,7 @@ # Tray icons don't work as expected on macOS if sys.platform != "darwin" and \ use_trayicon and config["ui"]["trayicon"]: - self.tray_app.create() + self.tray.create() """ Disable elements """ @@ -263,18 +263,7 @@ self.hidden_tabs = {} - # Initialise the Notebooks - self.chat_notebook = ChatRooms(self) - self.privatechat_notebook = PrivateChats(self) - self.user_info_notebook = UserTabs(self, UserInfo, self.UserInfoNotebookRaw) - self.user_browse_notebook = UserTabs(self, UserBrowse, self.UserBrowseNotebookRaw) - self.search_notebook = Searches(self) - - for w in self.chat_notebook, self.privatechat_notebook, self.user_info_notebook, self.user_browse_notebook, self.search_notebook: - w.set_tab_closers(config["ui"]["tabclosers"]) - w.set_reorderable(config["ui"]["tab_reorderable"]) - w.show_images(config["notifications"]["notification_tab_icons"]) - + # Initialise main notebook for tab in self.MainNotebook.get_children(): self.MainNotebook.set_tab_reorderable(tab, config["ui"]["tab_reorderable"]) @@ -305,42 +294,41 @@ hide_tab_template = _("Hide %(tab)s") # Initialize tabs labels - for label_tab in [ - self.ChatTabLabel, - self.PrivateChatTabLabel, - self.SearchTabLabel, - self.UserInfoTabLabel, - self.DownloadsTabLabel, - self.UploadsTabLabel, - self.UserBrowseTabLabel, - self.InterestsTabLabel - ]: + for page in self.MainNotebook.get_children(): + tab_label = self.MainNotebook.get_tab_label(page) + # Initialize the image label - img_label = ImageLabel(translated_tablabels[label_tab], self.images["empty"]) + img_label = ImageLabel( + translated_tablabels[tab_label], angle=config["ui"]["labelmain"], + show_hilite_image=config["notifications"]["notification_tab_icons"] + ) + + # Set tab text color + img_label.set_text_color(0) img_label.show() # Add it to the eventbox - label_tab.add(img_label) - - # Set tab icons, angle and text color - img_label.show_image(config["notifications"]["notification_tab_icons"]) - img_label.set_angle(config["ui"]["labelmain"]) - img_label.set_text_color(0) + tab_label.add(img_label) # Set the menu to hide the tab - eventbox_name = Gtk.Buildable.get_name(label_tab) - - label_tab.connect('button_press_event', self.on_tab_click, eventbox_name + "Menu", map_tablabels_to_box[label_tab]) + eventbox_name = Gtk.Buildable.get_name(tab_label) - self.__dict__[eventbox_name + "Menu"] = popup = PopupMenu(self) + tab_label.connect('button_press_event', self.on_tab_click, eventbox_name + "Menu", map_tablabels_to_box[tab_label]) - popup.setup( + self.__dict__[eventbox_name + "Menu"] = popup = PopupMenu(self).setup( ( - "#" + hide_tab_template % {"tab": translated_tablabels[label_tab]}, self.hide_tab, [label_tab, map_tablabels_to_box[label_tab]] + "#" + hide_tab_template % {"tab": translated_tablabels[tab_label]}, self.hide_tab, [tab_label, map_tablabels_to_box[tab_label]] ) ) - popup.set_user(map_tablabels_to_box[label_tab]) + popup.set_user(map_tablabels_to_box[tab_label]) + + # Initialise other notebooks + self.chat_notebook = ChatRooms(self) + self.privatechat_notebook = PrivateChats(self) + self.user_info_notebook = UserTabs(self, UserInfo, self.UserInfoNotebookRaw) + self.user_browse_notebook = UserTabs(self, UserBrowse, self.UserBrowseNotebookRaw) + self.search_notebook = Searches(self) self.chatrooms = self.chat_notebook self.chatrooms.show() @@ -519,12 +507,9 @@ def init_interface(self, msg): - if self.away == 0: + if not self.away: self.set_user_status(_("Online")) - self.tray_app.tray_status["status"] = "connect" - self.tray_app.set_image() - autoaway = self.np.config.sections["server"]["autoaway"] if autoaway > 0: @@ -534,10 +519,8 @@ else: self.set_user_status(_("Away")) - self.tray_app.tray_status["status"] = "away" - self.tray_app.set_image() - self.set_widget_online_status(True) + self.tray.set_away(self.away) self.uploads.init_interface(self.np.transfers.uploads) self.downloads.init_interface(self.np.transfers.downloads) @@ -571,7 +554,6 @@ return pixbuf names = [ - "empty", "away", "online", "offline", @@ -641,12 +623,10 @@ self.autoaway = self.away = False self.set_widget_online_status(False) + self.tray.set_connected(False) self.set_user_status(_("Offline")) - self.tray_app.tray_status["status"] = "disconnect" - self.tray_app.set_image() - self.searches.wish_list.interval = 0 self.chatrooms.conn_close() self.privatechats.conn_close() @@ -687,17 +667,15 @@ self.DownloadButtons.set_sensitive(status) self.UploadButtons.set_sensitive(status) - self.tray_app.set_server_actions_sensitive(status) + self.tray.set_server_actions_sensitive(status) def connect_error(self, conn): self.set_widget_online_status(False) + self.tray.set_connected(False) self.set_user_status(_("Offline")) - self.tray_app.tray_status["status"] = "disconnect" - self.tray_app.set_image() - self.uploads.conn_close() self.downloads.conn_close() @@ -715,8 +693,8 @@ self.disconnect_action.connect("activate", self.on_disconnect) self.application.add_action(self.disconnect_action) - self.away_action = Gio.SimpleAction.new("away", None) - self.away_action.connect("activate", self.on_away) + self.away_action = Gio.SimpleAction.new_stateful("away", None, GLib.Variant.new_boolean(False)) + self.away_action.connect("change-state", self.on_away) self.application.add_action(self.away_action) self.check_privileges_action = Gio.SimpleAction.new("checkprivileges", None) @@ -861,8 +839,7 @@ def on_connect(self, *args, getmessage=True): - self.tray_app.tray_status["status"] = "connect" - self.tray_app.set_image() + self.tray.set_connected(True) if self.np.active_server_conn is not None: return @@ -887,20 +864,18 @@ def on_away(self, *args): - self.away = (self.away + 1) % 2 + self.away = not self.away - if self.away == 0: + if not self.away: self.set_user_status(_("Online")) - - self.tray_app.tray_status["status"] = "connect" - self.tray_app.set_image() + self.on_disable_auto_away() else: self.set_user_status(_("Away")) - self.tray_app.tray_status["status"] = "away" - self.tray_app.set_image() + self.tray.set_away(self.away) self.np.queue.put(slskmessages.SetStatus(self.away and 1 or 2)) + self.away_action.set_state(GLib.Variant.new_boolean(self.away)) self.privatechats.update_colours() def on_check_privileges(self, *args): @@ -1061,7 +1036,7 @@ self.vpaned3.hide() if tab: - self.buddies_tab_label = ImageLabel(_("Buddy list"), self.images["empty"]) + self.buddies_tab_label = ImageLabel(_("Buddy list")) self.buddies_tab_label.show() if self.userlist.userlistvbox not in self.MainNotebook.get_children(): @@ -1252,6 +1227,13 @@ """ Main Notebook """ + def get_tab_label(self, tab_label): + + try: + return tab_label.get_child() + except AttributeError: + return tab_label + def chat_request_icon(self, status=0, widget=None): if status == 1 and not self.got_focus: @@ -1261,27 +1243,17 @@ return tablabel = self.get_tab_label(self.ChatTabLabel) + if not tablabel: return if status == 0: - if tablabel.get_image() == self.images["hilite"]: + if tablabel.get_hilite_image() == self.images["hilite"]: return - tablabel.set_image(status == 1 and self.images["hilite"] or self.images["hilite3"]) + tablabel.set_hilite_image(status == 1 and self.images["hilite"] or self.images["hilite3"]) tablabel.set_text_color(status + 1) - def get_tab_label(self, tab_label): - - tablabel = None - - if isinstance(tab_label, ImageLabel): - tablabel = tab_label - elif isinstance(tab_label, Gtk.EventBox): - tablabel = tab_label.get_child() - - return tablabel - def request_icon(self, tab_label, widget=None): if tab_label == self.PrivateChatTabLabel and not self.got_focus: @@ -1293,73 +1265,46 @@ return if self.current_tab != tab_label: - tablabel.set_image(self.images["hilite"]) + tablabel.set_hilite_image(self.images["hilite"]) tablabel.set_text_color(2) - def on_switch_page(self, notebook, page, page_nr): - - tab_labels = [] - tabs = self.MainNotebook.get_children() + def on_switch_page(self, notebook, page, page_num): - for i in tabs: - tab_labels.append(self.MainNotebook.get_tab_label(i)) - - label = tab_labels[page_nr] - - compare = { - self.ChatTabLabel: self.chat_notebook, - self.PrivateChatTabLabel: self.privatechat_notebook, - self.DownloadsTabLabel: None, - self.UploadsTabLabel: None, - self.SearchTabLabel: self.search_notebook, - self.UserInfoTabLabel: self.user_info_notebook, - self.UserBrowseTabLabel: self.user_browse_notebook, - self.InterestsTabLabel: None - } - - if "buddies_tab_label" in self.__dict__: - compare[self.buddies_tab_label] = None - - n = compare[label] + label = self.MainNotebook.get_tab_label(page) self.current_tab = label if label is not None: - if isinstance(label, ImageLabel): - label.set_image(self.images["empty"]) + try: + label.set_hilite_image(None) label.set_text_color(0) - elif isinstance(label, Gtk.EventBox): - label.get_child().set_image(self.images["empty"]) + except AttributeError: + label.get_child().set_hilite_image(None) label.get_child().set_text_color(0) - if n is not None: - n.popup_disable() - n.popup_enable() - - if n.get_current_page() != -1: - n.dismiss_icon(n, None, n.get_current_page()) + if page_num == self.MainNotebook.page_num(self.chathbox): + curr_page_num = self.chat_notebook.get_current_page() + curr_page = self.chat_notebook.get_nth_page(curr_page_num) + self.chatrooms.roomsctrl.on_switch_page(self.chat_notebook.notebook, curr_page, curr_page_num, forceupdate=True) + + elif page_num == self.MainNotebook.page_num(self.privatevbox): + curr_page_num = self.privatechat_notebook.get_current_page() + curr_page = self.privatechat_notebook.get_nth_page(curr_page_num) + self.privatechats.on_switch_page(self.privatechat_notebook.notebook, curr_page, curr_page_num, forceupdate=True) - if page_nr == self.MainNotebook.page_num(self.chathbox): - p = n.get_current_page() - self.chatrooms.roomsctrl.on_switch_page(n.notebook, None, p, 1) - - elif page_nr == self.MainNotebook.page_num(self.privatevbox): - p = n.get_current_page() - - if "privatechats" in self.__dict__: - self.privatechats.on_switch_page(n.notebook, None, p, 1) - - elif page_nr == self.MainNotebook.page_num(self.uploadsvbox): + elif page_num == self.MainNotebook.page_num(self.uploadsvbox): self.uploads.update(forceupdate=True) - elif page_nr == self.MainNotebook.page_num(self.downloadsvbox): + elif page_num == self.MainNotebook.page_num(self.downloadsvbox): self.downloads.update(forceupdate=True) def on_page_removed(self, main_notebook, child, page_num): + name = self.match_main_notebox(child) self.np.config.sections["ui"]["modes_visible"][name] = 0 self.on_page_reordered(main_notebook, child, page_num) def on_page_added(self, main_notebook, child, page_num): + name = self.match_main_notebox(child) self.np.config.sections["ui"]["modes_visible"][name] = 1 self.on_page_reordered(main_notebook, child, page_num) @@ -1378,7 +1323,7 @@ main_notebook.set_show_tabs(True) def on_key_press(self, widget, event): - self.on_button_press(None, None) + self.on_disable_auto_away() if event.state & (Gdk.ModifierType.MOD1_MASK | Gdk.ModifierType.CONTROL_MASK) != Gdk.ModifierType.MOD1_MASK: return False @@ -1492,26 +1437,20 @@ ui = self.np.config.sections["ui"] - self.chat_notebook.set_tab_pos(self.get_tab_position(ui["tabrooms"])) - self.chat_notebook.set_tab_angle(ui["labelrooms"]) - + # Main notebook self.MainNotebook.set_tab_pos(self.get_tab_position(ui["tabmain"])) - for label_tab in [ - self.ChatTabLabel, - self.PrivateChatTabLabel, - self.SearchTabLabel, - self.UserInfoTabLabel, - self.DownloadsTabLabel, - self.UploadsTabLabel, - self.UserBrowseTabLabel, - self.InterestsTabLabel - ]: - label_tab.get_child().set_angle(ui["labelmain"]) + for page in self.MainNotebook.get_children(): + tab_label = self.MainNotebook.get_tab_label(page) - if "buddies_tab_label" in self.__dict__: - self.buddies_tab_label.set_angle(ui["labelmain"]) + try: + tab_label.set_angle(ui["labelmain"]) + except AttributeError: + tab_label.get_child().set_angle(ui["labelmain"]) + # Other notebooks + self.chat_notebook.set_tab_pos(self.get_tab_position(ui["tabrooms"])) + self.chat_notebook.set_tab_angle(ui["labelrooms"]) self.privatechat_notebook.set_tab_pos(self.get_tab_position(ui["tabprivate"])) self.privatechat_notebook.set_tab_angle(ui["labelprivate"]) self.user_info_notebook.set_tab_pos(self.get_tab_position(ui["tabinfo"])) @@ -1991,14 +1930,18 @@ def on_auto_away(self): if not self.away: self.autoaway = True - self.on_away(None) + self.on_away() return False - def on_button_press(self, widget, event): + def on_disable_auto_away(self, *args): if self.autoaway: - self.on_away(None) self.autoaway = False + + if self.away: + # Disable away mode if not already done + self.on_away() + if self.awaytimerid is not None: self.remove_away_timer(self.awaytimerid) @@ -2426,7 +2369,7 @@ self.DownStatus.push(self.down_context_id, self.down_template % {'num': total_usersdown, 'speed': down}) self.UpStatus.push(self.up_context_id, self.up_template % {'num': total_usersup, 'speed': up}) - self.tray_app.set_transfer_status(self.tray_download_template % {'speed': down}, self.tray_upload_template % {'speed': up}) + self.tray.set_transfer_status(self.tray_download_template % {'speed': down}, self.tray_upload_template % {'speed': up}) """ Exit """ @@ -2482,10 +2425,10 @@ self.update_download_filters() self.np.config.write_configuration() - if not config["ui"]["trayicon"] and self.tray_app.is_tray_icon_visible(): - self.tray_app.hide() - elif config["ui"]["trayicon"] and not self.tray_app.is_tray_icon_visible(): - self.tray_app.create() + if not config["ui"]["trayicon"] and self.tray.is_tray_icon_visible(): + self.tray.hide() + elif config["ui"]["trayicon"] and not self.tray.is_tray_icon_visible(): + self.tray.create() if needcompletion: self.chatrooms.roomsctrl.update_completions() @@ -2508,40 +2451,25 @@ self.on_show_chat_buttons() + # Other notebooks for w in [self.chat_notebook, self.privatechat_notebook, self.user_info_notebook, self.user_browse_notebook, self.search_notebook]: w.set_tab_closers(config["ui"]["tabclosers"]) w.set_reorderable(config["ui"]["tab_reorderable"]) - w.show_images(config["notifications"]["notification_tab_icons"]) + w.show_hilite_images(config["notifications"]["notification_tab_icons"]) w.set_text_colors(None) - try: - for tab in self.MainNotebook.get_children(): - self.MainNotebook.set_tab_reorderable(tab, config["ui"]["tab_reorderable"]) - except Exception: - # Old gtk - pass - - tab_labels = [ - self.ChatTabLabel, - self.PrivateChatTabLabel, - self.DownloadsTabLabel, - self.UploadsTabLabel, - self.SearchTabLabel, - self.UserInfoTabLabel, - self.UserBrowseTabLabel, - self.InterestsTabLabel - ] + # Main notebook + for page in self.MainNotebook.get_children(): + tab_label = self.MainNotebook.get_tab_label(page) - if "buddies_tab_label" in self.__dict__: - tab_labels.append(self.buddies_tab_label) + try: + tab_label.show_hilite_image(config["notifications"]["notification_tab_icons"]) + tab_label.set_text_color(0) + except AttributeError: + tab_label.get_child().show_hilite_image(config["notifications"]["notification_tab_icons"]) + tab_label.get_child().set_text_color(0) - for label_tab in tab_labels: - if isinstance(label_tab, ImageLabel): - label_tab.show_image(config["notifications"]["notification_tab_icons"]) - label_tab.set_text_color(None) - elif isinstance(label_tab, Gtk.EventBox): - label_tab.get_child().show_image(config["notifications"]["notification_tab_icons"]) - label_tab.get_child().set_text_color(None) + self.MainNotebook.set_tab_reorderable(page, config["ui"]["tab_reorderable"]) self.set_tab_positions() @@ -2578,12 +2506,12 @@ if not self.np.config.sections["ui"]["exitdialog"]: return False - if self.tray_app.is_tray_icon_visible() and self.np.config.sections["ui"]["exitdialog"] == 2: + if self.tray.is_tray_icon_visible() and self.np.config.sections["ui"]["exitdialog"] == 2: if self.MainWindow.get_property("visible"): self.MainWindow.hide() return True - if self.tray_app.is_tray_icon_visible(): + if self.tray.is_tray_icon_visible(): option_dialog( parent=self.MainWindow, title=_('Close Nicotine+?'), diff -Nru nicotine-2.1.3/pynicotine/gtkgui/imagedata.py nicotine-2.1.3/pynicotine/gtkgui/imagedata.py --- nicotine-2.1.3/pynicotine/gtkgui/imagedata.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/imagedata.py 2020-10-29 05:50:09.000000000 +0000 @@ -1,7 +1,6 @@ n = b'\n \n \n \n \n \n \n \n\n' notify = b'\n \n \n \n \n \n \n \n\n' away = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x06bKGD\x00\xfd\x00\xd8\x00\x08\xc4\xa6\xc3)\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x07\x08\x13\x1f9\xf3\x18:\xd2\x00\x00\x00\x19tEXtComment\x00Created with GIMPW\x81\x0e\x17\x00\x00\x02\xfdIDAT8\xcbu\x93[h\x1ce\x14\xc7\x7f\xdf|3\xb3\xd7d\xb3\xc9\xc6\x96\x18\xd2B\xaa&\xa1M\xadmb5\xb5\xa9h+\xf5\x02Z\xbc\x15|h\xac\xc57E\xa4T\x84\xe2\x83J\xf1\xc1>\t}\xf0\xa1DAJA\x10\xa4\xda"5\xd4T!J\xc5BJ\xa9\r\xba\xd5\xcd\xee\xec\xadY\xb3\xdb\x9d\x99owf|\xd9\xe86\xe8\xff\xe5p\xe0\xf0;w\xc9\xbf2\x81\x8d\xc7N\xde7=\xb6#\xb5\xf3\xe27\x05\x0b\x08\x01u\xc0\xe3\x7f$Z6\x04l\xfb\xfc\xd2\xe4\xd7\xf7l\xea\x8a\x9bA\\\x98\xd2\xc0\xa9\xfb\xcd\xa1\xceO\x1e\x02~\x06\xd4\x7f\x014\xc0\x00F?\xbd0q\xd1\x0c\xc9\x0e\xe5\xfa\xe2\xf8;\x97xsj\x96HT\xd7g\xaf?w\x06\xe8kKv;`p8>\xfa\xea\xdbwO\x17\xb2\x0e\xf9E\x1b+Sg\xeb\xae$V\xbeF._\xa3\xa7/\x94|p\x9b\xd8\x01DWUn\x02RN\xec\xb9cj`0\xfa|\xb5\xd2\x10\xb7\xaa\x1eN\xdd#\xd1m\xf2\xf8\x8b\xfd\xc4\x92\x01k\xd4\x828\xf4T\xf5\xe9\x85\xdf\xbdsW~\r\x16\x01\t$\xdf\xfap\xe3\xbepL\xc6\x05\xd0\x05ly\xe5\xc8\x86\xf3Rj"\x12\x93$S&\xbdk\xc3\x8c\xf7Z\xac\x8f\x94\x89\xe9\x8d \xb2\xc9}\x00\xf8\xf3\xd1g\xfa\x86F\xb6t\xee\xd9\xbc=\xf9l\xd1r\x97\xda\x878yv\xfe\x89sE\xab\x86aJ\x94\xa7\x18\xbd\xbf\x9b\xcf\x8eg\xe6\x13=!W\xa9\xc6\x1f\xbdw\xeak\xfb7D\x86\\\xd7\x0fW\xca\xae7\xf7m\xe9\x8b\x15\x80\x0e\x8c]s^\xfa\x81@\x82\xf005\x1d\xdf\xf7\xf1D\x80o\xd88\xb6\n\n9;(\xe6\x1c\x96+\x8a\x0b_\xe5O\x9f:\x91>\xa2\xb7\x00\x9d\xd3gv\xbf\xafi\x10\x04`\x08\x9dF\x10\xa0\x99\x0e\x1e\x8a\x9bE\x97B\xd6\x11\xc5\x9c-J\x96\xeb_\xbf\xb2|\xed\xd4\x89\xf4Q`q\x05\xa0\xa5\x7f\xab.o\xae\xa4\xfchBj\x9e^\xa7\x19\xb8\x14\xb26\xf9\x8c\xa2d\xd5)\x17\x14%\xcb\xf1\xe7fJ\'\xe7fJ\xc7\x80\x1b\x80\xb7\x02\xd0\xb7>\xdc9\xaau\xd8\xa2i\xdc\xe2fAae\xeado\xd8\x14s.\xd5\xbf\x1a$\xbaubq\xa9\x15sN\xba\xd5\xb2v\xdb%\x8e\xefJ\xed}\xf7\xe3{OW\xca\xae,\x17\x94X^R\xe2\xf0\x93W\xf9\xf2\xbb5\xcc\xa5c\x98a\x8dX\xdc\xf0\xa21\xc3\x8b\'\x8c\xe6\x81G\xbe\xbf\x0b\xc8\xb6_Wb\xdf\xd4\xc0\xa1\xc1\xe1\xf8\xcb\xba\xa1\xad\x1f\xe9\xb7\xe4\xfe\xf1\x9ay~&(?v\xc0}!\x12\xa6\xd8\xd3\x85\xccX\xd8\x80\x03d\x01\xa5\xb7\x01\xec\xc1T\xf6\xa7\xfc|\xe0\x94j\xf1\xed\x87?R\xfbk\xb9 \xf8\xf1\xaa\xbc\x0c\\\x9e\x1c\xd3\x96\xf6\xee\xd4:\x84@\xbd\xf6^\xb3N\xdb\xfa\xfe\xd1\xbaub\xe0\xe0A\xf9F4jF\x13\rW\xfc\x92\x89T\x8e~Py\x1dX:;\xeb\xfb\xbb\'4/\x14B\xb6Z\x0fX\xf5 \x02\x18\x06F\x00\xb7\x15\xa0\x804\xb0\xd0\xf2\x83\xd5\xcf\xf47\xcc\xb1>@#\xd0a\x19\x00\x00\x00\x00IEND\xaeB`\x82' -empty = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x10\x08\x06\x00\x00\x00\xd7\x9f\x15\xb7\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x07\n\x152\'\x87\xb9\xe9g\x00\x00\x00\x19tEXtComment\x00Created with GIMPW\x81\x0e\x17\x00\x00\x00\x0cIDAT\x08\xd7c`\xa0.\x00\x00\x00P\x00\x01"\x13\xe8u\x00\x00\x00\x00IEND\xaeB`\x82' hilite = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\tpHYs\x00\x00\x04\x9d\x00\x00\x04\x9d\x01|4k\xa1\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x02\rIDAT8\x8d\xa5\x92\xdbK\x93a\x1c\xc7?\xcf\xf3\xbe\xaf\xdb<\xccM\x9aQ\x8e\xb1\x03\xc2\r\xb2\x12\x84\x17p\x81\xd8\x06Y\x0f\x9a\x89n\xf8<%7\xd8\xb5\xb2\xf3yU\t\xf6:\xe4\xde\xc1A\x1c,\x13\x92#\xa05\xa1\xf4n\xb2\x9b\xc9\xc5\x92\x82\xc5\xf7\xafn\xac|o\xcf\xa0,\x90\x12\xb0\xc1\x8aC\xd5Y\x90\xb5\xa4\x12r;6=v\x93\xbfe\xf6a_\xcfF\xf4\xceN~\xf3\xb1\xca\xefGU\xfe\xc0TV.\xa2r\xa9\xd9\xfd\x0fO\x87.\x16\xf3\x7f\xdc\x01\xc0\xc2\xa7{_\x82\xa1\xfa\xee\x95\xd5U\xf6\x0f\xcahn\xed\'\x9b1\xd7B\x9d\x83\xc1bVs\x12\xd4\xd5H\xd5\x18\xf0\x0e\xfak<\xb8+\xbc\xac\xae\x99L\xcf||0\xf9\xfc\xed\xcbbVw\x12\xccEb\xc9\x9eS\xb6\x15Km\xe9\xf1\xf5\x05f^\x87wR\xc9\xcc\xb8\x13+\x9d\x86\xb6\x8d\x12\x02\xba\xba:\xe8\xeb\xef\xa5\xf7D\x87\xa1#\xcb\x9dX\xc7\r\x84\xc1\xd2\xf2\xb7\xf9\xa4\xe1\xd6k\xcdt\x9a\xd4\xc6VBs\xd9K\xff,\x18\x9f\xfa:\xa7{\x8ew\xeeY\xf6\x8bD\xe2\x87J\xa7}\x97\xc6&\x16rN\xec\x7f\xe7\'\x07`\xc0|\xdbS\xc2\xf4\x00\x00\x00\x00IEND\xaeB`\x82' hilite3 = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\tpHYs\x00\x00\x04\x9d\x00\x00\x04\x9d\x01|4k\xa1\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x02\x1eIDAT8\x8d\xa5\x92\xbdkSa\x14\x87\x7f\xef}\xdf\x9b\xdb\xc4\x1b\x93j\xd2\n\r\x92\x16\x13$C\x9b\xa1"\x82\xe2\xacnuqP\x14\x9c:d\xa9d\xe8\xe6\x1fP\x1129\x88\xab\x83CUBP\x10J\xa9\x19R\xaa!\xe2\x07\x91\xa4\xd1\x94\xa6\xe9\xcdW\xf3\xd54\xbd\xf7\xbe\xaf\x93\x90\xc4D\x04\xcfv\x0e\xcf\xef\xe1\x1c8\x04#j9\x12q{\xa6\xbc7\x0c\x13\x96\x9d\x9f\xbb/W\xc2\x8b\xda0\x8e\x0c\x0eB\x91\x88r%xq\xcd\xeav_\x18W\xedLU\x18\xf4\xc3V\xa7\xb2_\\\x7f\x1b\xdfXx\xbc\xb4\xd4\xf9\xab\xe0y\xfcC\xd6v\xc633\xed\xb0b\x8cQ0\x02HD\x82\x80\x81|6\x93\xb8\x1a\x0c^\x02!\xe27/\xf5\x86\x9fDc!\xea\x9a\x9c9k\xb7\x82\x80\xc0\xe0\x02\x86\x10\x10\x10\x00(\x02\xbes\xf3\xcf^\xc5\xee\xf5f\xfa\x04\x13\xae\xa9\xbbB\xa2\xd0\x05\x87\xce9t.`p\x01\x93\x03\xa6\x10\x00\x95\xe9\xac\xdf{\xb37\xc3z\x1bN\xa9\xa3\xa9\x1b\xd0M\x01.\x01\x0c\x1c\x04\x04&7!S\x02!\x08\x14\x99\xaa#7\xe8\xb6\x1a\x9f\r\xce\x91o\x1e!\x97\xdbF\xb1ZC\xb1\xd1\xc2\xe6\xc7\x14\x00\x80J\x02\xfb\x95\x83\xecHAr+~\xfb\xb4\xdei\x1dtu\xec\xd4\xdb(\x1d\x1eC\xabV@e\n\x02\x81R\xa9X|\x9fL.\x8f\x14\xac\x84\xc3\xed|\xfa\xeb\xe5I&\x0cYV [\x14\xa8v\'N\xa8*d\xe8\xdd\xe8\xfaf\xe8\xe1b\xff?\xf4\t\x00\xe0\xc1\xad\x85\x94\xd4i\xee\x9dt\x8d\xa3\\\xf8\x8e\xdd\x1f\x9f`\xb7\x0b\xd4+\xa5r\xc2\x82\xd5A\x9e\x0e\x0e\x00`\xda+\x9b\xb3>\xd7\xb5S\x0e\x1b&\x9c2\xd8a\r[\x89\xc4\xd3Gw\xee\xbf\x19d\xd90Az\xe3]a\xde\xe76l.\x85\xedi\x05\xacE\xe3\xed\x8aV\x8f\x0ec\xff8\x01\x008\x87 \x10\xf0{=\x98\x0b\x9c\xc7\\\xd0ga\x90l\xc3\xd8\xa1\x1b\x10\x19\x99\xdc\xf6\xb7\x92<\xc6\\\xb5j\x15\x95rC\xa3\n\xcf\xfc\xb3`5\xfa%\xc5\xac\x01\xff\x91\xc1c\x9av,\xaaU\xe7\xf5\x17\xaf\xd3\xeda\xec\x7f\xd7/1\xd0\xde\xe3Z\xa7\xdd\xcc\x00\x00\x00\x00IEND\xaeB`\x82' offline = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x06bKGD\x00\xfd\x00\xd8\x00\x08\xc4\xa6\xc3)\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd6\x07\x08\r"/{\x9cIG\x00\x00\x00\x19tEXtComment\x00Created with GIMPW\x81\x0e\x17\x00\x00\x02\xe9IDAT8\xcbu\x93Mh\x1ce\x18\xc7\x7f\xef\xbc3\xb3\xb3\xbb\x93f7\xbb\xb5\xa1\xf6\x0b\xac%\x91\xbaXm \xda\x98\x14B\x03\xa5\x17[\xb5F\xa9`-zT\xf4RT\xbcY<\x89\xc7\x1e*\x94\x80\xa7\x9e\xbch\x1b\xc4\xd4R\xeb\xa5T\\I\x11\xb5\xa4\xa9\xc9\xeeNv\x136\x1f\xeb\xcc\xbc\xb3\xf3\xe1e\xa3\xdb\xa0\xff\xcb\xc3\x03\x0f\xbf\xe7[\xf2\xafL\xe0\xe0\xe5\xa1\xa7\xa7F\n\xc5\xd1o\xebu\x07H\x01.\x10\xf1?\x12\x1d\x9b\x02\x0e\xdf\x19\x1f\xbb\xfad.gK\xdd\x12"\x95\xc1\x8d\xe3\xd0\xfer\xeay\xe0\' \xf8/\x80\x06\x18@\xe9\xc6\xe8\x91\x1fRR\xf6\xa88\x16\x1f\x95\xcb\xbcq\xeb&\x19]\xd7\xef\xbdx\xfak`gW\xb2\x87\x01\x83=v\xe9\xc3\x81\x03SU\xdf\xa7\xe2z,\xba.G\x8by\x9c\xbf6h\xac7\xd9mh\xf9\xe7\x0e\x8b\x11 \xb3\xa5r\x13\x90rb\xc7#g\x1f\xcbfN\xaf\x86m\xb1\x11E\xb8aD\x9fi2\xb9{\x179\t\xe9\x919q\xe6\xfd\x8d\x17\xfeX\x88\xa6\xef\xfe\x9eT\x00\t\xe4?+\x1d<\x95\x95\xd2\x16@\x0e8t\xfe\xc0\xfe\xef\xa4\xa6\x89\xac\x94\x14S&\xfd\x96\xc5\xe0)\x87\xedC+\xa4\xf3\xed\xc4*\xa9g\x81\x85\x93\x8f\xee\x1c8\xd4\xbbmb\xb8\x90\x7f\xc9\xf1U\xb3{\x88c\xb3\'NL;\xad\x16\xa6\x94DQ\xc0p_\x9e\xcf\xefWf\x8b\xa6\xae\x820\xfc\xb3\xdf4\xfb\xf7\xdb\xe9\x01\x95\xc4\xd6\x8a\xaf\xa2\x99\xc6\xf2W\x9b\x00\x1d\x18\n\'\'\x7fL\xa4DD\x11\xc2\xb0H\xa2\x08\x91\x84\x88\xa4\x8d\xa7\x82\xa4\xe6{I\xcd\xf3Y\r\x02\xbeq\x96\xae\\\x9c\x9b?\xafw\x00\xdb\xae\x8e\x1f\xbb\xa0\x01\t\x80aA\xdcF\x0b\xdb\x10\x074\x94\xa2\xea\xf9\xa2\xe6y\xc2Q*\xbe\xbb\xb6\xfe\xdb\xc5\xb9\xf9\x8f\x81\xca&@\xbb\xbf\xb1\xb6\xde,\x14\xe2\x9cD\x13\xca%\x89\x15U\xd7\xa3\xe2+j\x9eG]\x058\xbe\x1f_o,_\xbe\xdeX\xfe\x14x\x00D\x9b\x00}\xb4\x98+eb_\x08/\xa6\x1e\x04,\xba.\x0f\\\x8f\x9a\xafXk\xb7\xe93tl]j5\xdf\x9f\xef\xb4\xac=t\x89G\xb7\x17\x8f_z\xe6\xa9++J\xc9\xba\x1f\x88f;\x10\xaf_\xfb\x95\xef/\xec\xa0<\x93\xc5\x12\x1a\xb6nDY\xc3\x88zM#\x1c\xbfq\xebq\xa0\xda}]\xbdg\xf7\xedyk\xb0\xc7~\xd3\xd0\xb4}\xbb&\x1dy\xf2\xb5\x9693\x9d\xacL\xbc\xad^I[4\n9\xe4\xa2\x83\x07\xf8@\x15\x08\xf4.\x80W|\xb9z{v)\xf1[\xbf\xd8\xc3_\x9c\t^m-%\xc9\x9d\xdb\xb2\x0c\x94\xc7\x86\xb4\xe6\xf1Q\xadG\x08\x82w>\t]\xba\xd6\xf7\x8f\xf6\xee\x15{\xce\x9d\x93\xefe2f\xa67V\xe2\xe7\x85\xf4\xea\x07\x97V\xdf\x05\x9a\xd7n\xc6\xf1\xb1#Z\x94J!;\xad\'ly\x10\x01\x0c\x02O\x00\xaa\x13\x10\x00\xf3\xc0\xbd\x8e\x9fl}\xa6\xbf\x01\x18\x97;(\xac\x8a\xd9\x9b\x00\x00\x00\x00IEND\xaeB`\x82' diff -Nru nicotine-2.1.3/pynicotine/gtkgui/notifications.py nicotine-2.1.3/pynicotine/gtkgui/notifications.py --- nicotine-2.1.3/pynicotine/gtkgui/notifications.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/notifications.py 2020-10-29 05:50:09.000000000 +0000 @@ -45,7 +45,7 @@ if room not in self.frame.hilites["rooms"]: self.frame.hilites["rooms"].append(room) - self.frame.tray_app.set_image() + self.frame.tray.set_image() elif location == "private": if user in self.frame.hilites[location]: self.frame.hilites[location].remove(user) @@ -53,7 +53,7 @@ elif user not in self.frame.hilites[location]: self.frame.hilites[location].append(user) - self.frame.tray_app.set_image() + self.frame.tray.set_image() if tab and self.frame.np.config.sections["ui"]["urgencyhint"] and not self.frame.got_focus: self.frame.MainWindow.set_urgency_hint(True) @@ -83,7 +83,7 @@ self.frame.hilites["private"].remove(user) self.set_title(user) - self.frame.tray_app.set_image() + self.frame.tray.set_image() def set_title(self, user=None): diff -Nru nicotine-2.1.3/pynicotine/gtkgui/privatechat.py nicotine-2.1.3/pynicotine/gtkgui/privatechat.py --- nicotine-2.1.3/pynicotine/gtkgui/privatechat.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/privatechat.py 2020-10-29 05:50:09.000000000 +0000 @@ -73,7 +73,7 @@ self.frame.images, angle=config["ui"]["labelprivate"], tabclosers=config["ui"]["tabclosers"], - show_image=config["notifications"]["notification_tab_icons"], + show_hilite_image=config["notifications"]["notification_tab_icons"], reorderable=config["ui"]["tab_reorderable"], show_status_image=config["ui"]["tab_status_icons"], notebookraw=self.frame.PrivatechatNotebookRaw @@ -87,16 +87,13 @@ self.notebook.connect("switch-page", self.on_switch_page) - def on_switch_page(self, notebook, page, page_num, force=0): + def on_switch_page(self, notebook, page, page_num, forceupdate=False): - if self.frame.MainNotebook.get_current_page() != self.frame.MainNotebook.page_num(self.frame.privatevbox) and not force: + if self.frame.MainNotebook.get_current_page() != self.frame.MainNotebook.page_num(self.frame.privatevbox) and not forceupdate: return - # page is None? - new_page = notebook.get_nth_page(page_num) - for user, tab in list(self.users.items()): - if tab.Main == new_page: + if tab.Main == page: GLib.idle_add(tab.ChatLine.grab_focus) # Remove hilite if selected tab belongs to a user in the hilite list if user in self.frame.hilites["private"]: diff -Nru nicotine-2.1.3/pynicotine/gtkgui/search.py nicotine-2.1.3/pynicotine/gtkgui/search.py --- nicotine-2.1.3/pynicotine/gtkgui/search.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/search.py 2020-10-29 05:50:09.000000000 +0000 @@ -74,7 +74,7 @@ self.frame.images, angle=ui["labelsearch"], tabclosers=ui["tabclosers"], - show_image=self.frame.np.config.sections["notifications"]["notification_tab_icons"], + show_hilite_image=self.frame.np.config.sections["notifications"]["notification_tab_icons"], reorderable=ui["tab_reorderable"], notebookraw=self.frame.SearchNotebookRaw ) @@ -233,6 +233,11 @@ def do_global_search(self, id, text): self.frame.np.queue.put(slskmessages.FileSearch(id, text)) + """ Request a list of related searches from the server. + Seemingly non-functional since 2018 (always receiving empty lists). """ + + # self.frame.np.queue.put(slskmessages.RelatedSearch(text)) + def do_rooms_search(self, id, text, room=None): if room is not None: self.frame.np.queue.put(slskmessages.RoomSearch(room, id, text)) @@ -723,7 +728,7 @@ # Update tab notification self.frame.searches.request_changed(self.Main) if self.frame.MainNotebook.get_current_page() != self.frame.MainNotebook.page_num(self.frame.searchvbox): - self.frame.SearchTabLabel.get_child().set_image(self.frame.images["online"]) + self.frame.SearchTabLabel.get_child().set_hilite_image(self.frame.images["hilite"]) def get_flag(self, user, flag=None): diff -Nru nicotine-2.1.3/pynicotine/gtkgui/transferlist.py nicotine-2.1.3/pynicotine/gtkgui/transferlist.py --- nicotine-2.1.3/pynicotine/gtkgui/transferlist.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/transferlist.py 2020-10-29 05:50:09.000000000 +0000 @@ -182,7 +182,7 @@ if not tablabel: return - tablabel.set_image(self.frame.images["online"]) + tablabel.set_hilite_image(self.frame.images["hilite"]) def on_ban(self, widget): self.select_transfers() diff -Nru nicotine-2.1.3/pynicotine/gtkgui/tray.py nicotine-2.1.3/pynicotine/gtkgui/tray.py --- nicotine-2.1.3/pynicotine/gtkgui/tray.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/tray.py 2020-10-29 05:50:09.000000000 +0000 @@ -27,7 +27,7 @@ from pynicotine.logfacility import log -class TrayApp: +class Tray: def __init__(self, frame): try: @@ -75,7 +75,7 @@ ("#" + _("Lookup a User's IP"), self.on_get_a_users_ip), ("#" + _("Lookup a User's Info"), self.on_get_a_users_info), ("#" + _("Lookup a User's Shares"), self.on_get_a_users_shares), - ("$" + _("Toggle Away"), self.frame.on_away), + ("$" + _("Away"), self.frame.on_away), ("#" + _("Settings"), self.frame.on_settings), ("#" + _("Quit"), self.frame.on_quit) ) @@ -304,6 +304,30 @@ except Exception as e: log.add_warning(_("ERROR: cannot set trayicon image: %(error)s"), {'error': e}) + def set_away(self, enable): + if enable: + self.tray_status["status"] = "away" + else: + self.tray_status["status"] = "connect" + + self.set_image() + + # Toggle away checkbox in tray menu + away_item = self.tray_popup_menu.get_children()[8] + handler_id = self.tray_popup_menu.handlers[away_item] + + with away_item.handler_block(handler_id): + # Temporarily disable handler, we only want to change the visual checkbox appearance + away_item.set_active(enable) + + def set_connected(self, enable): + if enable: + self.tray_status["status"] = "connect" + else: + self.tray_status["status"] = "disconnect" + + self.set_image() + def set_server_actions_sensitive(self, status): items = self.tray_popup_menu.get_children() diff -Nru nicotine-2.1.3/pynicotine/gtkgui/ui/menus/menubar.ui nicotine-2.1.3/pynicotine/gtkgui/ui/menus/menubar.ui --- nicotine-2.1.3/pynicotine/gtkgui/ui/menus/menubar.ui 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/ui/menus/menubar.ui 2020-10-29 05:50:09.000000000 +0000 @@ -17,7 +17,7 @@
- _Away/Return + _Away app.away <Alt>a diff -Nru nicotine-2.1.3/pynicotine/gtkgui/userinfo.py nicotine-2.1.3/pynicotine/gtkgui/userinfo.py --- nicotine-2.1.3/pynicotine/gtkgui/userinfo.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/userinfo.py 2020-10-29 05:50:09.000000000 +0000 @@ -31,6 +31,7 @@ from gi.repository import Gtk from pynicotine import slskmessages +from pynicotine.gtkgui.dirchooser import save_file from pynicotine.gtkgui.utils import append_line from pynicotine.gtkgui.utils import humanize from pynicotine.gtkgui.utils import human_speed @@ -39,7 +40,6 @@ from pynicotine.gtkgui.utils import load_ui_elements from pynicotine.gtkgui.utils import PopupMenu from pynicotine.logfacility import log -from pynicotine.utils import clean_file # User Info and User Browse Notebooks @@ -56,7 +56,7 @@ self.frame.images, angle=ui["labelinfo"], tabclosers=ui["tabclosers"], - show_image=self.frame.np.config.sections["notifications"]["notification_tab_icons"], + show_hilite_image=self.frame.np.config.sections["notifications"]["notification_tab_icons"], reorderable=ui["tab_reorderable"], show_status_image=ui["tab_status_icons"], notebookraw=notebookraw @@ -258,7 +258,7 @@ ("#" + _("Zoom In"), self.make_zoom_in), ("#" + _("Zoom Out"), self.make_zoom_out), ("", None), - ("#" + _("Save Image"), self.on_save_picture) + ("#" + _("Save Picture"), self.on_save_picture) ) def on_popup_likes_menu(self, widget, event): @@ -463,8 +463,17 @@ if self.image is None or self.image_pixbuf is None: return - filename = "%s %s.jpg" % (self.user, time.strftime("%Y-%m-%d %H:%M:%S")) - pathname = os.path.join(self.frame.np.config.sections["transfers"]["downloaddir"], clean_file(filename)) + response = save_file( + self.frame.MainWindow, + self.frame.np.config.sections["transfers"]["downloaddir"], + "%s %s.jpg" % (self.user, time.strftime("%Y-%m-%d %H_%M_%S")), + title="Save as..." + ) + + if not response: + return + + pathname = response[0] if not os.path.exists(pathname): self.image_pixbuf.savev(pathname, "jpeg", ["quality"], ["100"]) diff -Nru nicotine-2.1.3/pynicotine/gtkgui/utils.py nicotine-2.1.3/pynicotine/gtkgui/utils.py --- nicotine-2.1.3/pynicotine/gtkgui/utils.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/gtkgui/utils.py 2020-10-29 05:50:09.000000000 +0000 @@ -587,39 +587,35 @@ class ImageLabel(Gtk.Box): - def __init__(self, label="", image=None, onclose=None, closebutton=False, angle=0, show_image=True, statusimage=None, show_status_image=False): + def __init__(self, label="", onclose=None, closebutton=False, angle=0, hilite_image=None, show_hilite_image=True, status_image=None, show_status_image=False): Gtk.Box.__init__(self) self.closebutton = closebutton self.angle = angle - self._show_image = show_image - self._show_status_image = show_status_image - self.notify = 0 - - self._entered = 0 - self._pressed = 0 self.onclose = onclose - self.status_img = None - self.statusimage = Gtk.Image() - self.label = Gtk.Label() - self.text = label - - self.set_text(self.text) + self.label = Gtk.Label() self.label.set_angle(angle) self.label.show() - if self._show_status_image: - self.set_status_image(statusimage) - self.statusimage.show() + self.text = label + self.set_text(self.text) - self.image = Gtk.Image() - self.set_image(image) + self.status_image = Gtk.Image() + self.status_pixbuf = None - if self._show_image: - self.image.show() + if show_status_image: + self.set_status_image(status_image) + self.status_image.show() + + self.hilite_image = Gtk.Image() + self.hilite_pixbuf = None + + if show_hilite_image: + self.set_hilite_image(hilite_image) + self.hilite_image.show() self._pack_children() self._order_children() @@ -646,9 +642,9 @@ self.add(self.box) self.box.show() - self.box.pack_start(self.statusimage, False, False, 0) + self.box.pack_start(self.status_image, False, False, 0) self.box.pack_start(self.label, True, True, 0) - self.box.pack_start(self.image, False, False, 0) + self.box.pack_start(self.hilite_image, False, False, 0) if self.closebutton and self.onclose is not None: self._add_close_button() @@ -658,17 +654,17 @@ if self.angle == 90: if "button" in self.__dict__ and self.closebutton != 0: self.box.reorder_child(self.button, 0) - self.box.reorder_child(self.image, 1) + self.box.reorder_child(self.hilite_image, 1) self.box.reorder_child(self.label, 2) - self.box.reorder_child(self.statusimage, 3) + self.box.reorder_child(self.status_image, 3) else: - self.box.reorder_child(self.image, 0) + self.box.reorder_child(self.hilite_image, 0) self.box.reorder_child(self.label, 1) - self.box.reorder_child(self.statusimage, 2) + self.box.reorder_child(self.status_image, 2) else: - self.box.reorder_child(self.statusimage, 0) + self.box.reorder_child(self.status_image, 0) self.box.reorder_child(self.label, 1) - self.box.reorder_child(self.image, 2) + self.box.reorder_child(self.hilite_image, 2) if "button" in self.__dict__ and self.closebutton != 0: self.box.reorder_child(self.button, 3) @@ -683,13 +679,11 @@ self._order_children() - def show_image(self, show=True): - self._show_image = show - - if self._show_image: - self.image.show() + def show_hilite_image(self, show=True): + if show: + self.hilite_image.show() else: - self.image.hide() + self.hilite_image.hide() def set_angle(self, angle): self.angle = angle @@ -703,10 +697,11 @@ if "button" in self.__dict__: return + close_image = Gtk.Image() + close_image.set_from_icon_name("window-close-symbolic", Gtk.IconSize.MENU) + self.button = Gtk.Button() - img = Gtk.Image() - img.set_from_icon_name("window-close-symbolic", Gtk.IconSize.MENU) - self.button.add(img) + self.button.add(close_image) if self.onclose is not None: self.button.connect("clicked", self.onclose) @@ -725,10 +720,6 @@ del self.button def set_text_color(self, notify=None, text=None): - if notify is None: - notify = self.notify - else: - self.notify = notify color = NICOTINE.np.config.sections["ui"]["tab_default"] @@ -752,28 +743,27 @@ else: self.label.set_markup("%s" % (color, self.text.replace("<", "<").replace(">", ">").replace("&", "&"))) - def set_image(self, img): - self.img = img - self.image.set_from_pixbuf(img) + def set_hilite_image(self, pixbuf): + self.hilite_pixbuf = pixbuf + self.hilite_image.set_from_pixbuf(pixbuf) - def get_image(self): - return self.img + def get_hilite_image(self): + return self.hilite_pixbuf def set_status_image(self, img): - if img is self.status_img: + if img is self.status_pixbuf: return - if NICOTINE: - if NICOTINE.np.config.sections["ui"]["tab_status_icons"]: - self.statusimage.show() - else: - self.statusimage.hide() + if NICOTINE.np.config.sections["ui"]["tab_status_icons"]: + self.status_image.show() + else: + self.status_image.hide() - self.status_img = img - self.statusimage.set_from_pixbuf(img) + self.status_pixbuf = img + self.status_image.set_from_pixbuf(img) def get_status_image(self): - return self.status_img + return self.status_pixbuf def set_text(self, lbl): self.set_text_color(notify=None, text=lbl) @@ -789,7 +779,7 @@ - You can choose the label orientation (angle). """ - def __init__(self, images, angle=0, tabclosers=False, show_image=True, reorderable=True, show_status_image=False, notebookraw=None): + def __init__(self, images, angle=0, tabclosers=False, show_hilite_image=True, reorderable=True, show_status_image=False, notebookraw=None): # We store the real Gtk.Notebook object self.notebook = notebookraw @@ -799,42 +789,43 @@ self.reorderable = reorderable self.images = images - self._show_image = show_image + self._show_hilite_image = show_hilite_image self._show_status_image = show_status_image - self.pages = [] - self.notebook.connect("switch-page", self.dismiss_icon) - self.notebook.connect("key_press_event", self.on_key_press) self.angle = angle + def get_labels(self, page): + tab_label = self.notebook.get_tab_label(page).get_child() + menu_label = self.notebook.get_menu_label(page) + + return tab_label, menu_label + def set_reorderable(self, reorderable): self.reorderable = reorderable - for data in self.pages: - page, label_tab, status, label_tab_menu = data - try: - self.notebook.set_tab_reorderable(page, self.reorderable) - except Exception: - pass + for page in self.notebook.get_children(): + self.notebook.set_tab_reorderable(page, self.reorderable) def set_tab_closers(self, closers): self.tabclosers = closers - for data in self.pages: - page, label_tab, status, label_tab_menu = data - label_tab.set_onclose(self.tabclosers) + for page in self.notebook.get_children(): + tab_label, menu_label = self.get_labels(page) + + tab_label.set_onclose(self.tabclosers) - def show_images(self, show_image=True): + def show_hilite_images(self, show_image=True): - self._show_image = show_image + self._show_hilite_image = show_image - for data in self.pages: - page, label_tab, status, label_tab_menu = data - label_tab.show_image(self._show_image) + for page in self.notebook.get_children(): + tab_label, menu_label = self.get_labels(page) + + tab_label.show_hilite_image(self._show_hilite_image) def set_tab_angle(self, angle): @@ -843,37 +834,22 @@ self.angle = angle - for data in self.pages: - page, label_tab, status, label_tab_menu = data - label_tab.set_angle(angle) + for page in self.notebook.get_children(): + tab_label, menu_label = self.get_labels(page) + + tab_label.set_angle(angle) def set_tab_pos(self, pos): self.notebook.set_tab_pos(pos) - def on_key_press(self, widget, event): - - if event.state & (Gdk.ModifierType.MOD1_MASK | Gdk.ModifierType.CONTROL_MASK) != Gdk.ModifierType.MOD1_MASK: - return False - - if event.keyval in [Gdk.keyval_from_name("Up"), Gdk.keyval_from_name("Left")]: - self.prev_page() - elif event.keyval in [Gdk.keyval_from_name("Down"), Gdk.keyval_from_name("Right")]: - self.next_page() - else: - return False - - widget.stop_emission_by_name("key_press_event") - - return True - def append_page(self, page, label, onclose=None, angle=0, fulltext=None): self.set_tab_angle(angle) closebutton = self.tabclosers label_tab = ImageLabel( - label, self.images["empty"], onclose, closebutton=closebutton, - angle=angle, show_image=self._show_image, statusimage=None, + label, onclose, closebutton=closebutton, angle=angle, + show_hilite_image=self._show_hilite_image, show_status_image=self._show_status_image ) @@ -883,9 +859,7 @@ label_tab.set_tooltip_text(fulltext) # menu for all tabs - label_tab_menu = ImageLabel(label, self.images["empty"]) - - self.pages.append([page, label_tab, 0, label_tab_menu]) + label_tab_menu = ImageLabel(label) eventbox = Gtk.EventBox() eventbox.set_visible_window(False) @@ -904,14 +878,7 @@ def remove_page(self, page): - for i in self.pages[:]: - if i[0] == page: - Gtk.Notebook.remove_page(self.notebook, self.page_num(page)) - i[1].destroy() - i[3].destroy() - self.pages.remove(i) - - break + Gtk.Notebook.remove_page(self.notebook, self.page_num(page)) if self.notebook.get_n_pages() == 0: self.notebook.set_show_tabs(False) @@ -922,58 +889,48 @@ def set_status_image(self, page, status): + tab_label, menu_label = self.get_labels(page) image = self.images[("offline", "away", "online")[status]] - for i in self.pages: - if page == i[0]: - i[1].set_status_image(image) - i[3].set_status_image(image) - return - - def set_image(self, page, status): + tab_label.set_status_image(image) + menu_label.set_status_image(image) - image = self.images[("empty", "hilite3", "hilite")[status]] + def set_hilite_image(self, page, status): - for i in self.pages: - if page == i[0]: + tab_label, menu_label = self.get_labels(page) + image = None - if status == 1 and i[2] == 2: - return + if status > 0: + image = self.images[("hilite3", "hilite")[status - 1]] - if i[2] != status: - i[1].set_image(image) - i[3].set_image(image) - i[2] = status + if status == 1 and tab_label.get_hilite_image() == self.images["hilite"]: + # Chat mentions have priority over normal notifications + return - return + tab_label.set_hilite_image(image) + menu_label.set_hilite_image(image) def set_text(self, page, label): - for i in self.pages: - if i[0] == page: - i[1].set_text(label) - i[3].set_text(label) - return - - def set_text_colors(self, color=None): - - for i in self.pages: - i[1].set_text_color(color) - - def set_text_color(self, page, color=None): - - for i in self.pages: - if i[0] == page: - i[1].set_text_color(color) - return + tab_label, menu_label = self.get_labels(page) - def dismiss_icon(self, notebook, page, page_num): + tab_label.set_text(label) + menu_label.set_text(label) + + def set_text_colors(self, status): - # page is None? - new_page = self.get_nth_page(page_num) + for page in self.notebook.get_children(): + self.set_text_color(page, status) + + def set_text_color(self, page, status): + + tab_label, menu_label = self.get_labels(page) + tab_label.set_text_color(status) + + def dismiss_icon(self, notebook, page, page_num): - self.set_image(new_page, 0) - self.set_text_color(new_page, 0) + self.set_hilite_image(page, status=0) + self.set_text_color(page, status=0) def request_hilite(self, page): @@ -981,8 +938,8 @@ if current == page: return - self.set_image(page, 2) - self.set_text_color(page, 2) + self.set_hilite_image(page, status=2) + self.set_text_color(page, status=2) def request_changed(self, page): @@ -990,8 +947,8 @@ if current == page: return - self.set_image(page, 1) - self.set_text_color(page, 1) + self.set_hilite_image(page, status=1) + self.set_text_color(page, status=1) def get_current_page(self): return self.notebook.get_current_page() diff -Nru nicotine-2.1.3/pynicotine/pynicotine.py nicotine-2.1.3/pynicotine/pynicotine.py --- nicotine-2.1.3/pynicotine/pynicotine.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/pynicotine.py 2020-10-29 05:50:09.000000000 +0000 @@ -201,6 +201,7 @@ slskmessages.SayChatroom: self.say_chat_room, slskmessages.JoinRoom: self.join_room, slskmessages.UserLeftRoom: self.user_left_room, + slskmessages.CantCreateRoom: self.dummy_message, slskmessages.QueuedDownloads: self.dummy_message, slskmessages.GetPeerAddress: self.get_peer_address, slskmessages.OutConn: self.out_conn, @@ -261,6 +262,7 @@ slskmessages.FileSearch: self.search_request, slskmessages.RoomSearch: self.room_search_request, slskmessages.UserSearch: self.search_request, + slskmessages.RelatedSearch: self.dummy_message, slskmessages.PossibleParents: self.possible_parents, slskmessages.DistribAlive: self.dummy_message, slskmessages.DistribSearch: self.distrib_search, diff -Nru nicotine-2.1.3/pynicotine/slskmessages.py nicotine-2.1.3/pynicotine/slskmessages.py --- nicotine-2.1.3/pynicotine/slskmessages.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/slskmessages.py 2020-10-29 05:50:09.000000000 +0000 @@ -551,7 +551,7 @@ return self.pack_object(self.room) def parse_network_message(self, message): - self.room = self.get_object(message, bytes)[1] + pos, self.room = self.get_object(message, bytes) class UserJoinedRoom(ServerMessage): @@ -700,7 +700,8 @@ class ServerPing(ServerMessage): """ Server code: 32 """ - """ We test if the server responds. DEPRECATED """ + """ We test if the server responds. """ + """ DEPRECATED """ def make_network_message(self): return b"" @@ -710,10 +711,27 @@ pass -class SendSpeed(ServerMessage): +class SendConnectToken(ServerMessage): + """ Server code: 33 """ + """ DEPRECATED """ + + def __init__(self, user, token): + self.user = user + self.token = token + + def make_network_message(self): + return self.pack_object(self.user) + self.pack_object(self.token) + + def parse_network_message(self, message): + pos, self.user = self.get_object(message, bytes) + pos, self.token = self.get_object(message, int, pos) + + +class SendDownloadSpeed(ServerMessage): """ Server code: 34 """ """ We used to send this after a finished download to let the server update - the speed statistics for a user. DEPRECATED """ + the speed statistics for a user. """ + """ DEPRECATED """ def __init__(self, user=None, speed=None): self.user = user @@ -769,7 +787,8 @@ class QueuedDownloads(ServerMessage): """ Server code: 40 """ """ The server sends this to indicate if someone has download slots available - or not. DEPRECATED """ + or not. """ + """ DEPRECATED """ def parse_network_message(self, message): pos, self.user = self.get_object(message, bytes) @@ -907,7 +926,8 @@ class PlaceInLineResponse(ServerMessage): """ Server code: 60 """ """ Server sends this to indicate change in place in queue while we're - waiting for files from other peer. DEPRECATED """ + waiting for files from other peer. """ + """ DEPRECATED """ def __init__(self, user=None, req=None, place=None): self.req = req @@ -931,17 +951,19 @@ class RoomAdded(ServerMessage): """ Server code: 62 """ """ The server tells us a new room has been added. """ + """ DEPRECATED """ def parse_network_message(self, message): - self.room = self.get_object(message, bytes)[1] + pos, self.room = self.get_object(message, bytes) class RoomRemoved(ServerMessage): """ Server code: 63 """ """ The server tells us a room has been removed. """ + """ DEPRECATED """ def parse_network_message(self, message): - self.room = self.get_object(message, bytes)[1] + pos, self.room = self.get_object(message, bytes) class RoomList(ServerMessage): @@ -989,8 +1011,8 @@ class ExactFileSearch(ServerMessage): """ Server code: 65 """ - """ Someone is searching for a file with an exact name. DEPRECATED - (no results even with official client) """ + """ Someone is searching for a file with an exact name. """ + """ DEPRECATED (no results even with official client) """ def parse_network_message(self, message): pos, self.user = self.get_object(message, bytes) @@ -1006,12 +1028,13 @@ """ A global message from the server admin has arrived. """ def parse_network_message(self, message): - self.msg = self.get_object(message, bytes)[1] + pos, self.msg = self.get_object(message, bytes) class GlobalUserList(JoinRoom): """ Server code: 67 """ - """ We send this to get a global list of all users online. DEPRECATED """ + """ We send this to get a global list of all users online. """ + """ DEPRECATED """ def make_network_message(self): return b"" @@ -1174,8 +1197,7 @@ class AcceptChildren(ServerMessage): """ Server code: 100 """ - """ We tell the server if we want to accept child nodes. - TODO: actually use this somewhere """ + """ We tell the server if we want to accept child nodes. """ def __init__(self, enabled=None): self.enabled = enabled @@ -1284,8 +1306,8 @@ def parse_network_message(self, message): pos, self.room = self.get_object(message, bytes) - pos, n = self.get_object(message, int, pos) - for i in range(n): + pos, num = self.get_object(message, int, pos) + for i in range(num): pos, user = self.get_object(message, bytes, pos) pos, msg = self.get_object(message, bytes, pos) self.msgs[user] = msg @@ -1588,7 +1610,7 @@ self.room = room def parse_network_message(self, message): - self.room = self.get_object(message, bytes)[1] + pos, self.room = self.get_object(message, bytes) class PrivateRoomRemoved(PrivateRoomAdded): @@ -1713,8 +1735,31 @@ pos, self.msg = self.get_object(message, bytes, pos) +class RelatedSearch(ServerMessage): + """ Server code: 153 """ + """ The server returns a list of related search terms for a search query. """ + """ DEPRECATED ? (empty list from server as of 2018) """ + + def __init__(self, query=None): + self.query = query + self.terms = [] + + def make_network_message(self): + return self.pack_object(self.query) + + def parse_network_message(self, message): + pos, self.query = self.get_object(message, bytes) + pos, num = self.get_object(message, int, pos) + + for i in range(num): + pos, term = self.get_object(message, bytes, pos) + pos, score = self.get_object(message, int, pos) + + self.terms.append((term, score)) + + class CantConnectToPeer(ServerMessage): - """ Message 1001 """ + """ Server code: 1001 """ """ We send this to say we can't connect to peer after it has asked us to connect. We receive this if we asked peer to connect and it can't do this. This message means a connection can't be established either way. @@ -1734,13 +1779,14 @@ def parse_network_message(self, message): pos, self.token = self.get_object(message, int) -# These are probably leftovers, not sure what to do with them +class CantCreateRoom(ServerMessage): + """ Server code: 1002 """ + """ Server tells us a new room cannot be created. """ + """ DEPRECATED (server sends a private message now) """ -# class CantCreateRoom(ServerMessage): - # """ Server tells us a new room cannot be created""" - # def parseNetworkMessage(self, message): - # self.room = self.getObject(message, types.StringType)[1] + def parse_network_message(self, message): + pos, self.room = self.get_object(message, bytes) """ @@ -2386,7 +2432,6 @@ Search requests are sent to us by the server using SearchRequest if we're a branch root, or by our parent using DistribSearch. - (TODO: check that this works / is implemented) """ __slots__ = "conn", "user", "searchid", "searchterm" @@ -2449,7 +2494,6 @@ Search requests are sent to us by the server using SearchRequest if we're a branch root, or by our parent using DistribSearch. - (TODO: check that this works / is implemented) """ __slots__ = "conn", "user", "searchid", "searchterm" diff -Nru nicotine-2.1.3/pynicotine/slskproto.py nicotine-2.1.3/pynicotine/slskproto.py --- nicotine-2.1.3/pynicotine/slskproto.py 2020-10-28 01:17:36.000000000 +0000 +++ nicotine-2.1.3/pynicotine/slskproto.py 2020-10-29 05:50:09.000000000 +0000 @@ -45,6 +45,7 @@ from pynicotine.slskmessages import BranchLevel from pynicotine.slskmessages import BranchRoot from pynicotine.slskmessages import CantConnectToPeer +from pynicotine.slskmessages import CantCreateRoom from pynicotine.slskmessages import ChangePassword from pynicotine.slskmessages import CheckPrivileges from pynicotine.slskmessages import ChildDepth @@ -124,6 +125,7 @@ from pynicotine.slskmessages import QueueFailed from pynicotine.slskmessages import QueueUpload from pynicotine.slskmessages import Recommendations +from pynicotine.slskmessages import RelatedSearch from pynicotine.slskmessages import Relogged from pynicotine.slskmessages import RemoveThingIHate from pynicotine.slskmessages import RemoveThingILike @@ -140,7 +142,8 @@ from pynicotine.slskmessages import SearchInactivityTimeout from pynicotine.slskmessages import SearchParent from pynicotine.slskmessages import SearchRequest -from pynicotine.slskmessages import SendSpeed +from pynicotine.slskmessages import SendConnectToken +from pynicotine.slskmessages import SendDownloadSpeed from pynicotine.slskmessages import SendUploadSpeed from pynicotine.slskmessages import ServerConn from pynicotine.slskmessages import ServerMessage @@ -288,11 +291,12 @@ MessageAcked: 23, FileSearch: 26, SetStatus: 28, - ServerPing: 32, # Depreciated - SendSpeed: 34, # Depreciated + ServerPing: 32, # Deprecated + SendConnectToken: 33, # Deprecated + SendDownloadSpeed: 34, # Deprecated SharedFoldersFiles: 35, GetUserStats: 36, - QueuedDownloads: 40, # Depreciated + QueuedDownloads: 40, # Deprecated Relogged: 41, UserSearch: 42, AddThingILike: 51, @@ -300,23 +304,23 @@ Recommendations: 54, GlobalRecommendations: 56, UserInterests: 57, - PlaceInLineResponse: 60, # Depreciated - RoomAdded: 62, # Depreciated - RoomRemoved: 63, # Depreciated + PlaceInLineResponse: 60, # Deprecated + RoomAdded: 62, # Deprecated + RoomRemoved: 63, # Deprecated RoomList: 64, - ExactFileSearch: 65, # Depreciated + ExactFileSearch: 65, # Deprecated AdminMessage: 66, - GlobalUserList: 67, # Depreciated - TunneledMessage: 68, # Depreciated + GlobalUserList: 67, # Deprecated + TunneledMessage: 68, # Deprecated PrivilegedUsers: 69, HaveNoParent: 71, SearchParent: 73, - ParentMinSpeed: 83, # Unused - ParentSpeedRatio: 84, # Unused - ParentInactivityTimeout: 86, # Depreciated - SearchInactivityTimeout: 87, # Depreciated - MinParentsInCache: 88, # Depreciated - DistribAliveInterval: 90, # Depreciated + ParentMinSpeed: 83, # Unused + ParentSpeedRatio: 84, # Unused + ParentInactivityTimeout: 86, # Deprecated + SearchInactivityTimeout: 87, # Deprecated + MinParentsInCache: 88, # Deprecated + DistribAliveInterval: 90, # Deprecated AddToPrivileged: 91, CheckPrivileges: 92, SearchRequest: 93, @@ -339,9 +343,9 @@ GivePrivileges: 123, NotifyPrivileges: 124, AckNotifyPrivileges: 125, - BranchLevel: 126, # Unimplemented - BranchRoot: 127, # Unimplemented - ChildDepth: 129, # Unimplemented + BranchLevel: 126, # Unimplemented + BranchRoot: 127, # Unimplemented + ChildDepth: 129, # Unimplemented PrivateRoomUsers: 133, PrivateRoomAddUser: 134, PrivateRoomRemoveUser: 135, @@ -360,7 +364,9 @@ JoinPublicRoom: 150, LeavePublicRoom: 151, PublicRoomMessage: 152, - CantConnectToPeer: 1001 + RelatedSearch: 153, # Deprecated ? + CantConnectToPeer: 1001, + CantCreateRoom: 1002 # Deprecated } peercodes = { @@ -375,7 +381,7 @@ FolderContentsResponse: 37, TransferRequest: 40, TransferResponse: 41, - PlaceholdUpload: 42, # Depreciated + PlaceholdUpload: 42, # Deprecated QueueUpload: 43, PlaceInQueue: 44, UploadFailed: 46, @@ -388,9 +394,9 @@ distribclasses = { 0: DistribAlive, 3: DistribSearch, - 4: DistribBranchLevel, # Unimplemented - 5: DistribBranchRoot, # Unimplemented - 7: DistribChildDepth, # Unimplemented + 4: DistribBranchLevel, # Unimplemented + 5: DistribBranchRoot, # Unimplemented + 7: DistribChildDepth, # Unimplemented 93: DistribServerSearch }