gajim.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2026
October
September
August
July
June
May
April
March
February
January
2025
December
November
October
September
August
July
June
May
April
March
February
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
2016
December
November
October
September
August
July
June
May
April
March
February
January
2015
December
November
October
September
August
July
June
May
April
March
February
January
2014
December
November
October
September
August
July
June
May
April
March
February
January
2013
December
November
October
September
August
July
June
May
April
March
February
January
2012
December
November
October
September
August
July
June
May
April
March
February
January
2011
December
November
October
September
August
July
June
May
April
March
February
January
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008
December
November
October
September
August
July
June
May
April
March
February
January
2007
December
November
October
September
August
July
June
May
April
March
February
January
2006
December
November
October
September
August
July
June
May
April
List overview
Download
Commits
September 2021
----- 2026 -----
October 2026
September 2026
August 2026
July 2026
June 2026
May 2026
April 2026
March 2026
February 2026
January 2026
----- 2025 -----
December 2025
November 2025
October 2025
September 2025
August 2025
July 2025
June 2025
May 2025
April 2025
March 2025
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
----- 2007 -----
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
----- 2006 -----
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
commits@gajim.org
1 participants
49 discussions
Start a n
N
ew thread
[Git][gajim/gajim][mainwindow] 2 commits: Fix sending chatstates for window motion-notify-event
by Daniel Brötzmann (@wurstsalat)
05 Sep '21
05 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: 023ec7f5 by wurstsalat at 2021-09-05T13:20:59+02:00 Fix sending chatstates for window motion-notify-event - - - - - d64890fc by wurstsalat at 2021-09-05T13:46:47+02:00 Main: Fix chat window actions (close/switch tab) - - - - - 3 changed files: - gajim/gtk/chat_list.py - gajim/gtk/controls/base.py - gajim/gtk/main.py Changes: ===================================== gajim/gtk/chat_list.py ===================================== @@ -181,7 +181,7 @@ def select_chat(self, account, jid): row = self._chats[(account, jid)] self.select_row(row) - def select_next_chat(self, forwards): + def select_next_chat(self, forwards, unread_first=False): # Selects the next chat, but prioritizes chats with unread messages. # Direction is forwards (True) or backwards (False) row = self.get_selected_chat() @@ -191,26 +191,28 @@ def select_next_chat(self, forwards): self.select_chat(row.account, row.jid) return - index = row.get_index() - current = index unread_found = False - # Loop until finding a chat with unread count or completing a cycle - while True: - if forwards: - index += 1 - if index >= len(self.get_children()): - index = 0 - else: - index -= 1 - if index < 0: - index = len(self.get_children()) - 1 + if unread_first: + index = row.get_index() + current = index + + # Loop until finding a chat with unread count or completing a cycle + while True: + if forwards: + index += 1 + if index >= len(self.get_children()): + index = 0 + else: + index -= 1 + if index < 0: + index = len(self.get_children()) - 1 - row = self.get_row_at_index(index) - if row.unread_count > 0: - unread_found = True - break - if index == current: - break + row = self.get_row_at_index(index) + if row.unread_count > 0: + unread_found = True + break + if index == current: + break if unread_found: self.select_chat(row.account, row.jid) @@ -232,6 +234,11 @@ def select_next_chat(self, forwards): self.select_chat(next_row.account, next_row.jid) + def select_chat_number(self, number): + row = self.get_row_at_index(number) + if row is not None: + self.select_chat(row.account, row.jid) + def toggle_chat_pinned(self, account, jid): row = self._chats[(account, jid)] row.toggle_pinned() ===================================== gajim/gtk/controls/base.py ===================================== @@ -54,7 +54,6 @@ from gajim.gui.conversation.scrolled import ScrolledView from gajim.gui.conversation.jump_to_end_button import JumpToEndButton from gajim.gui.dialogs import DialogButton -from gajim.gui.dialogs import ConfirmationDialog from gajim.gui.dialogs import PastePreviewDialog from gajim.gui.message_input import MessageInputTextView from gajim.gui.util import get_hardware_key_codes @@ -242,11 +241,6 @@ def __init__(self, widget_name, account, jid): self._client.get_module('Chatstate').set_active(self.contact) - # TODO - # id_ = app.window.connect('motion-notify-event', - # self._on_window_motion_notify) - # self.handlers[id_] = app.window - self.encryption = self.get_encryption_state() self.conversation_view.encryption_enabled = self.encryption is not None @@ -831,7 +825,7 @@ def _on_message_textview_key_press_event(self, textview, event): if event.get_state() & Gdk.ModifierType.SHIFT_MASK: if (event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_ISO_Left_Tab): - app.window.select_next_chat(False) + app.window.select_next_chat(False, unread_first=True) return True if event.keyval in (Gdk.KEY_Page_Down, Gdk.KEY_Page_Up): @@ -840,7 +834,7 @@ def _on_message_textview_key_press_event(self, textview, event): if event.get_state() & Gdk.ModifierType.CONTROL_MASK: if event.keyval == Gdk.KEY_Tab: - app.window.select_next_chat(True) + app.window.select_next_chat(True, unread_first=True) return True message_buffer = self.msg_textview.get_buffer() @@ -1043,17 +1037,6 @@ def send_message(self, message_buffer = self.msg_textview.get_buffer() message_buffer.set_text('') # clear message buffer (and tv of course) - def _on_window_motion_notify(self, *args): - """ - It gets called no matter if it is the active window or not - """ - # TODO: - return - if self.parent_win.get_active_jid() == self.contact.jid: - # if window is the active one, set last interaction - self._client.get_module('Chatstate').set_mouse_activity( - self.contact, self.msg_textview.has_text()) - def _on_message_tv_buffer_changed(self, textbuffer): has_text = self.msg_textview.has_text() app.window.lookup_action( ===================================== gajim/gtk/main.py ===================================== @@ -58,6 +58,8 @@ def __init__(self): self._account_side_bar = AccountSideBar() self._ui.account_box.add(self._account_side_bar) + self.connect('motion-notify-event', self._on_window_motion_notify) + self._ui.connect_signals(self) self.register_events([ @@ -190,13 +192,13 @@ def _on_action(self, action, _param): if action == 'escape': self._chat_page.hide_search() - # if action == 'escape' and app.settings.get('escape_key_closes'): - # self.remove_tab(control, self.CLOSE_ESC) - # return + if action == 'escape' and app.settings.get('escape_key_closes'): + self._chat_page.remove_chat(control.account, control.contact.jid) + return - # if action == 'close-tab': - # self.remove_tab(control, self.CLOSE_CTRL_KEY) - # return + if action == 'close-tab': + self._chat_page.remove_chat(control.account, control.contact.jid) + return # if action == 'move-tab-up': # old_position = self.notebook.get_current_page() @@ -214,32 +216,36 @@ def _on_action(self, action, _param): # old_position + 1) # return - # if action == 'switch-next-tab': - # new = self.notebook.get_current_page() + 1 - # if new >= self.notebook.get_n_pages(): - # new = 0 - # self.notebook.set_current_page(new) - # return + if action == 'switch-next-tab': + self.select_next_chat(True) + return - # if action == 'switch-prev-tab': - # new = self.notebook.get_current_page() - 1 - # if new < 0: - # new = self.notebook.get_n_pages() - 1 - # self.notebook.set_current_page(new) - # return + if action == 'switch-prev-tab': + self.select_next_chat(False) + return - # if action == 'switch-next-unread-tab-right': - # self.move_to_next_unread_tab(True) - # return + if action == 'switch-next-unread-tab-right': + self.select_next_chat(True, unread_first=True) + return - # if action == 'switch-next-unread-tab-left': - # self.move_to_next_unread_tab(False) - # return + if action == 'switch-next-unread-tab-left': + self.select_next_chat(False, unread_first=True) + return - # if action.startswith('switch-tab-'): - # number = int(action[-1]) - # self.notebook.set_current_page(number - 1) - # return + if action.startswith('switch-tab-'): + number = int(action[-1]) - 1 + self.select_chat_number(number) + return + + def _on_window_motion_notify(self, _widget, _event): + control = self.get_active_control() + if control is None: + return + + if self.get_property('has-toplevel-focus'): + client = app.get_client(control.account) + client.get_module('Chatstate').set_mouse_activity( + control.contact, control.msg_textview.has_text()) def _set_startup_finished(self): self._startup_finished = True @@ -339,11 +345,17 @@ def select_chat(self, account, jid): self._main_stack.show_chat_page() self._chat_page.select_chat(account, jid) - def select_next_chat(self, forwards): + def select_next_chat(self, forwards, unread_first=False): + chat_list_stack = self._chat_page.get_chat_list_stack() + chat_list = chat_list_stack.get_current_chat_list() + if chat_list is not None: + chat_list.select_next_chat(forwards, unread_first) + + def select_chat_number(self, number): chat_list_stack = self._chat_page.get_chat_list_stack() chat_list = chat_list_stack.get_current_chat_list() if chat_list is not None: - chat_list.select_next_chat(forwards) + chat_list.select_chat_number(number) @staticmethod def _add_to_roster(_action, param): View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/76ff8ab14c1cbc0feaa3495f2c35b9c…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/76ff8ab14c1cbc0feaa3495f2c35b9c…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][mainwindow] Add back chat selection via Ctrl(+Shft)+Tab
by Daniel Brötzmann (@wurstsalat)
05 Sep '21
05 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: 76ff8ab1 by wurstsalat at 2021-09-05T12:47:35+02:00 Add back chat selection via Ctrl(+Shft)+Tab - - - - - 4 changed files: - gajim/gtk/chat_list.py - gajim/gtk/chat_stack.py - gajim/gtk/controls/base.py - gajim/gtk/main.py Changes: ===================================== gajim/gtk/chat_list.py ===================================== @@ -181,6 +181,57 @@ def select_chat(self, account, jid): row = self._chats[(account, jid)] self.select_row(row) + def select_next_chat(self, forwards): + # Selects the next chat, but prioritizes chats with unread messages. + # Direction is forwards (True) or backwards (False) + row = self.get_selected_chat() + if row is None: + row = self.get_row_at_index(0) + if row is not None: + self.select_chat(row.account, row.jid) + return + + index = row.get_index() + current = index + unread_found = False + # Loop until finding a chat with unread count or completing a cycle + while True: + if forwards: + index += 1 + if index >= len(self.get_children()): + index = 0 + else: + index -= 1 + if index < 0: + index = len(self.get_children()) - 1 + + row = self.get_row_at_index(index) + if row.unread_count > 0: + unread_found = True + break + if index == current: + break + + if unread_found: + self.select_chat(row.account, row.jid) + return + + index = row.get_index() + if forwards: + next_row = self.get_row_at_index(index + 1) + else: + next_row = self.get_row_at_index(index - 1) + if next_row is None: + if forwards: + next_row = self.get_row_at_index(0) + else: + last = len(self.get_children()) - 1 + next_row = self.get_row_at_index(last) + self.select_chat(next_row.account, next_row.jid) + return + + self.select_chat(next_row.account, next_row.jid) + def toggle_chat_pinned(self, account, jid): row = self._chats[(account, jid)] row.toggle_pinned() ===================================== gajim/gtk/chat_stack.py ===================================== @@ -14,6 +14,7 @@ import logging +from gi.repository import GLib from gi.repository import Gtk from gajim.common import ged @@ -94,6 +95,8 @@ def remove_chat(self, account, jid): def show_chat(self, account, jid): self.set_visible_child_name(f'{account}:{jid}') + control = self.get_control(account, jid) + GLib.idle_add(control.focus) def clear(self): self.set_visible_child_name('empty') ===================================== gajim/gtk/controls/base.py ===================================== @@ -831,8 +831,7 @@ def _on_message_textview_key_press_event(self, textview, event): if event.get_state() & Gdk.ModifierType.SHIFT_MASK: if (event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_ISO_Left_Tab): - # TODO: - self.parent_win.move_to_next_unread_tab(False) + app.window.select_next_chat(False) return True if event.keyval in (Gdk.KEY_Page_Down, Gdk.KEY_Page_Up): @@ -841,8 +840,7 @@ def _on_message_textview_key_press_event(self, textview, event): if event.get_state() & Gdk.ModifierType.CONTROL_MASK: if event.keyval == Gdk.KEY_Tab: - # TODO - self.parent_win.move_to_next_unread_tab(True) + app.window.select_next_chat(True) return True message_buffer = self.msg_textview.get_buffer() ===================================== gajim/gtk/main.py ===================================== @@ -339,6 +339,12 @@ def select_chat(self, account, jid): self._main_stack.show_chat_page() self._chat_page.select_chat(account, jid) + def select_next_chat(self, forwards): + chat_list_stack = self._chat_page.get_chat_list_stack() + chat_list = chat_list_stack.get_current_chat_list() + if chat_list is not None: + chat_list.select_next_chat(forwards) + @staticmethod def _add_to_roster(_action, param): _workspace, account, jid = param.unpack() View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/commit/76ff8ab14c1cbc0feaa3495f2c35b9c5…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/commit/76ff8ab14c1cbc0feaa3495f2c35b9c5…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][mainwindow] AdhocMUC: Fixes
by Daniel Brötzmann (@wurstsalat)
03 Sep '21
03 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: 961f6eda by wurstsalat at 2021-09-04T00:49:42+02:00 AdhocMUC: Fixes - - - - - 2 changed files: - gajim/gtk/adhoc_muc.py - gajim/gtk/controls/groupchat.py Changes: ===================================== gajim/gtk/adhoc_muc.py ===================================== @@ -31,7 +31,7 @@ log = logging.getLogger('gajim.gui.adhoc_muc') -class AdhocMUC(Gtk.ApplicationWindow): +class AdhocMUC: # Keep a reference on windows so garbage collector don't restroy them instances = [] # type: List[AdhocMUC] @@ -141,12 +141,14 @@ def on_invite_button_clicked(self, _widget): 'muc#roomconfig_membersonly': True, 'muc#roomconfig_whois': 'anyone', 'muc#roomconfig_changesubject': True, + 'muc#roomconfig_persistentroom': False, # Ejabberd options 'public_list': False, } app.interface.create_groupchat(self.account, room_jid, config) + app.window.select_chat(self.account, room_jid) self.window.destroy() def on_cancel_button_clicked(self, _widget): ===================================== gajim/gtk/controls/groupchat.py ===================================== @@ -649,6 +649,10 @@ def on_approve(): callback=on_approve)], modal=False).show() + def _on_message_received(self, event): + if not event.msgtxt: + return + def _on_mam_message_received(self, event): if not event.properties.type.is_groupchat: return View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/commit/961f6eda1627833403ac622f53692643…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/commit/961f6eda1627833403ac622f53692643…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][mainwindow] RosterItemExchange: Fix roster actions
by Daniel Brötzmann (@wurstsalat)
03 Sep '21
03 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: 709c0ea0 by wurstsalat at 2021-09-04T00:35:03+02:00 RosterItemExchange: Fix roster actions - - - - - 1 changed file: - gajim/gtk/roster_item_exchange.py Changes: ===================================== gajim/gtk/roster_item_exchange.py ===================================== @@ -201,10 +201,13 @@ def _on_accept_button_clicked(self, _widget): if app.jid_is_transport(self._jid_from): self._client.get_module('Presence').automatically_added.append( jid) - # TODO: - app.interface.roster.req_sub( - self, jid, message, self.account, groups=groups, - nickname=model[iter_][2], auto_auth=True) + + self._client.get_module('Presence').subscribe( + jid, + msg=message, + name=model[iter_][2], + groups=groups, + auto_auth=True) iter_ = model.iter_next(iter_) InformationDialog(i18n.ngettext('Added %d contact', 'Added %d contacts', @@ -220,15 +223,10 @@ def _on_accept_button_clicked(self, _widget): groups = model[iter_][3].split(', ') if groups == ['']: groups = [] - # TODO: - for contact in app.contacts.get_contact(self.account, jid): - contact.name = model[iter_][2] - self._client.get_module('Roster').update_contact( + self._client.get_module('Roster').set_item( jid, model[iter_][2], groups) - self._client.get_module('Roster').draw_contact( - jid, self.account) + # Update opened chats - # TODO: ctrl = app.window.get_control(self.account, jid) if ctrl: ctrl.update_ui() @@ -242,9 +240,7 @@ def _on_accept_button_clicked(self, _widget): # It is selected jid = model[iter_][1] self._client.get_module('Presence').unsubscribe(jid) - # TODO: - app.interface.roster.remove_contact(jid, self.account) - app.contacts.remove_jid(self.account, jid) + self._client.get_module('Roster').delete_item(jid) iter_ = model.iter_next(iter_) InformationDialog(i18n.ngettext('Removed %d contact', 'Removed %d contacts', View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/commit/709c0ea0a95267f96cebc80ad147df75…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/commit/709c0ea0a95267f96cebc80ad147df75…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][mainwindow] 3 commits: Remove contact context menu
by Daniel Brötzmann (@wurstsalat)
03 Sep '21
03 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: c4184628 by wurstsalat at 2021-09-03T23:58:33+02:00 Remove contact context menu - - - - - 1a4a217d by wurstsalat at 2021-09-03T23:58:39+02:00 BaseControl: Remove obsolete file transfer code - - - - - 147100b6 by wurstsalat at 2021-09-04T00:10:50+02:00 GUI Menu Builder: Add TODOs, remove obsolete code - - - - - 5 changed files: - gajim/data/gui/chat_control.ui - − gajim/data/gui/contact_context_menu.ui - gajim/gtk/controls/base.py - gajim/gtk/controls/chat.py - gajim/gui_menu_builder.py Changes: ===================================== gajim/data/gui/chat_control.ui ===================================== @@ -148,7 +148,6 @@ <property name="name">ChatControl-BannerEventBox</property> <property name="can-focus">False</property> <property name="no-show-all">True</property> - <signal name="button-press-event" handler="_on_banner_eventbox_button_press_event" swapped="no"/> <child> <object class="GtkBox"> <property name="visible">True</property> @@ -214,7 +213,6 @@ <property name="selectable">True</property> <property name="ellipsize">end</property> <property name="xalign">0</property> - <signal name="populate-popup" handler="on_banner_label_populate_popup" swapped="no"/> </object> <packing> <property name="left-attach">1</property> ===================================== gajim/data/gui/contact_context_menu.ui deleted ===================================== @@ -1,210 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.36.0 --> -<interface> - <requires lib="gtk+" version="3.22"/> - <object class="GtkAccelGroup" id="accelgroup1"/> - <object class="GtkMenu" id="contact_context_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuItem" id="start_chat_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Start _Chat</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="send_single_message_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Send Single _Message...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="send_file_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Send _File...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="invite_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">In_vite to</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="convert_to_groupchat_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Invite _Contacts</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="execute_command_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">E_xecute Command...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="manage_contact"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes">M_anage Contact</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="menu2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkMenuItem" id="rename_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Rename...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="edit_groups_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Edit _Groups...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="add_special_notification_menuitem"> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">Add Special _Notification...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="separatormenuitem1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="subscription_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Subscription</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="subscription_menuitem_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuItem" id="resend_authorization_to_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Allow contact to see my status</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="rerequest_authorization_from_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">A_sk to see contact status</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="forbid_contact_to_see_my_status1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Forbid contact to see my status</property> - <property name="use_underline">True</property> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="unblock_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Unblock</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="block_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Block</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="unignore_menuitem"> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">_Unignore</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="ignore_menuitem"> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">_Ignore</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="add_to_roster_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Add to Contact List...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="remove_from_roster_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Remo_ve</property> - <property name="use_underline">True</property> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="last_separator"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="information_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Information</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="history_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_History</property> - <property name="use_underline">True</property> - </object> - </child> - </object> -</interface> ===================================== gajim/gtk/controls/base.py ===================================== @@ -269,6 +269,9 @@ def __init__(self, widget_name, account, jid): def _connect_contact_signals(self): raise NotImplementedError + def _process_jingle_av_event(self): + raise NotImplementedError + def _get_action(self, name): return app.window.lookup_action(name + self.control_id) @@ -423,12 +426,6 @@ def get_tab_label(self, chatstate): # Return a markup'd label and optional Gtk.Color in a tuple like: # return (label_str, None) - def prepare_context_menu(self, hide_buttonbar_items=False): - """ - Derived classes SHOULD implement this - """ - return None - def set_session(self, session): oldsession = None if hasattr(self, 'session'): @@ -650,22 +647,6 @@ def on_language_changed(self, checker, _param): gspell_lang = checker.get_language() self.contact.settings.set('speller_language', gspell_lang.get_code()) - def on_banner_label_populate_popup(self, _label, menu): - """ - Override the default context menu and add our own menuitems - """ - item = Gtk.SeparatorMenuItem.new() - menu.prepend(item) - - menu2 = self.prepare_context_menu() # pylint: disable=assignment-from-none - i = 0 - for item in menu2: - menu2.remove(item) - menu.prepend(item) - menu.reorder_child(item, i) - i += 1 - menu.show_all() - def shutdown(self): # remove_gui_extension_point() is called on shutdown, but also when # a plugin is getting disabled. Plugins don’t know the difference. @@ -738,15 +719,6 @@ def paste_clipboard_as_quote(self, _item: Gtk.MenuItem) -> None: def on_quote(self, _widget, text): self.insert_as_quote(text) - # moved from ChatControl - def _on_banner_eventbox_button_press_event(self, _widget, event): - """ - If right-clicked, show popup - """ - if event.button == 3: # right click - # TODO: - self.parent_win.popup_menu(event) - def _on_message_textview_paste_event(self, _texview): clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) image = clipboard.wait_for_image() @@ -809,8 +781,29 @@ def _start_filetransfer(self, path): app.interface.send_httpupload(self, path) else: - ft = app.interface.instances['file_transfers'] - ft.send_file(self.account, self.contact, path) + app.interface.instances['file_transfers'].send_file( + self.account, self.contact, path) + + def _on_send_file(self, action, _param): + name = action.get_name() + if 'httpupload' in name: + app.interface.send_httpupload(self) + return + + if 'jingle' in name: + app.interface.instances['file_transfers'].show_file_send_request( + self.account, self.contact) + return + + method = self._get_pref_ft_method() + if method is None: + return + + if method == 'httpupload': + app.interface.send_httpupload(self) + else: + app.interface.instances['file_transfers'].show_file_send_request( + self.account, self.contact) def _on_message_textview_key_press_event(self, textview, event): if event.keyval == Gdk.KEY_space: @@ -1323,54 +1316,6 @@ def clear(tv): start, end = buffer_.get_bounds() buffer_.delete(start, end) - def _on_send_file(self, action, _param): - name = action.get_name() - if 'httpupload' in name: - app.interface.send_httpupload(self) - return - - if 'jingle' in name: - self._on_send_file_jingle() - return - - method = self._get_pref_ft_method() - if method is None: - return - - if method == 'httpupload': - app.interface.send_httpupload(self) - else: - self._on_send_file_jingle() - - def _on_send_file_jingle(self): - def _on_send(): - app.interface.instances['file_transfers'].show_file_send_request( - self.account, self.contact) - - if self.is_privatechat: - gc_control = app.window.get_control( - self.account, self._room_contact.jid) - self_contact = self._room_contact.get_self() - - if (gc_control.is_anonymous and - self.contact.affiliation.value not - in ['admin', 'owner'] and - self_contact.affiliation.value in ['admin', 'owner']): - # Show a warning if we're trying to send a file to a PM contact - # which is not Admin/Owner, and thus cannot normally see our JID - ConfirmationDialog( - _('Privacy'), - _('Warning'), - _('If you send a file to <b>%s</b>, your real XMPP ' - 'address will be revealed.') % self.contact.name, - [DialogButton.make('Cancel'), - DialogButton.make( - 'OK', - text=_('_Send Anyway'), - callback=_on_send)]).show() - return - _on_send() - def set_control_active(self, state): if state: self.set_emoticon_popover() ===================================== gajim/gtk/controls/chat.py ===================================== @@ -318,9 +318,6 @@ def _on_information(self, _action, _param): app.window.contact_info(self.account, self.contact.jid) def _on_invite_contacts(self, _action, _param): - """ - User wants to invite some friends to chat - """ AdhocMUC(self.account, [self.contact.jid]) def _on_send_chatstate(self, action, param): @@ -402,6 +399,7 @@ def _on_nickname_received(self, _event): self.update_ui() def _on_update_client_info(self, event): + # TODO: contact = app.contacts.get_contact( self.account, event.jid, event.resource) if contact is None: @@ -645,26 +643,6 @@ def add_message(self, correct_id=correct_id, additional_data=additional_data) - def prepare_context_menu(self, hide_buttonbar_items=False): - """ - Set compact view menuitem active state sets active and sensitivity state - for history_menuitem (False for tranasports) and file_transfer_menuitem - and hide()/show() for add_to_roster_menuitem - """ - if app.jid_is_transport(self.contact.jid): - menu = gui_menu_builder.get_transport_menu(self.contact, - self.account) - else: - menu = gui_menu_builder.get_contact_menu( - self.contact, - self.account, - use_multiple_contacts=False, - show_start_chat=False, - show_encryption=True, - control=self, - show_buttonbar_items=not hide_buttonbar_items) - return menu - def shutdown(self): # PluginSystem: removing GUI extension points connected with ChatControl # instance object @@ -746,12 +724,6 @@ def _on_drag_data_received(self, _widget, _context, _x_coord, _y_coord, AdhocMUC(self.account, [self.contact.jid], [dropped_jid]) - def _on_convert_to_gc_menuitem_activate(self, _widget): - """ - User wants to invite some friends to chat - """ - AdhocMUC(self.account, [self.contact.jid]) - def _on_client_state_changed(self, _client, _signal_name, state): self.msg_textview.set_sensitive(state.is_connected) self.msg_textview.set_editable(state.is_connected) ===================================== gajim/gui_menu_builder.py ===================================== @@ -14,10 +14,10 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see <
http://www.gnu.org/licenses/
>. -from gi.repository import Gtk, Gio, GLib -from nbxmpp.namespaces import Namespace +from gi.repository import Gtk +from gi.repository import Gio +from gi.repository import GLib -from gajim import gtkgui_helpers from gajim.common import app from gajim.common import helpers from gajim.common.helpers import is_affiliation_change_allowed @@ -25,407 +25,11 @@ from gajim.common.i18n import _ from gajim.common.const import URIType from gajim.common.const import URIAction +from gajim.gtkgui_helpers import destroy_widget -from gajim.gui.util import get_builder -from gajim.gui.const import ControlType - - -def build_resources_submenu(contacts, account, action, room_jid=None, - room_account=None, cap=None): - """ - Build a submenu with contact's resources. room_jid and room_account are for - action self.on_invite_to_room - """ - roster = app.interface.roster - sub_menu = Gtk.Menu() - - for c in contacts: - item = Gtk.MenuItem.new_with_label( - '%s (%s)' % (c.resource, str(c.priority))) - sub_menu.append(item) - if action == roster.on_invite_to_room: # pylint: disable=comparison-with-callable - item.connect('activate', action, [(c, account)], room_jid, - room_account, c.resource) - else: # start_chat, execute_command, send_file - item.connect('activate', action, c, account, c.resource) - - if cap and not c.supports(cap): - item.set_sensitive(False) - - return sub_menu - -def build_invite_submenu(invite_menuitem, list_, ignore_rooms=None, -show_bookmarked=False, force_resource=False): - """ - list_ in a list of (contact, account) - force_resource means we want to send invitation even if there is only one - resource - """ - if ignore_rooms is None: - ignore_rooms = [] - roster = app.interface.roster - # used if we invite only one contact with several resources - contact_list = [] - if len(list_) == 1: - contact, account = list_[0] - contact_list = app.contacts.get_contacts(account, contact.jid) - contacts_transport = -1 - connected_accounts = [] - # -1 is at start, False when not from the same, None when jabber - for (contact, account) in list_: - if not account in connected_accounts: - connected_accounts.append(account) - transport = app.get_transport_name_from_jid(contact.jid) - if transport == 'jabber': - transport = None - if contacts_transport == -1: - contacts_transport = transport - elif contacts_transport != transport: - contacts_transport = False - - if contacts_transport is False: - # they are not all from the same transport - invite_menuitem.set_sensitive(False) - return - invite_to_submenu = Gtk.Menu() - invite_menuitem.set_submenu(invite_to_submenu) - rooms = [] # a list of (room_jid, account) tuple - minimized_controls = [] - for account in connected_accounts: - minimized_controls += \ - list(app.interface.minimized_controls[account].values()) - for gc_control in app.interface.msg_win_mgr.get_controls( - ControlType.GROUPCHAT) + minimized_controls: - acct = gc_control.account - if acct not in connected_accounts: - continue - room_jid = gc_control.room_jid - room_name = gc_control.room_name - if room_jid in ignore_rooms: - continue - if room_jid in app.gc_connected[acct] and \ - app.gc_connected[acct][room_jid] and \ - contacts_transport in ['jabber', None]: - rooms.append((room_jid, room_name, acct)) - if rooms: - item = Gtk.SeparatorMenuItem.new() # separator - invite_to_submenu.append(item) - for (room_jid, room_name, account) in rooms: - menuitem = Gtk.MenuItem.new_with_label(room_name) - if len(contact_list) > 1: # several resources - menuitem.set_submenu(build_resources_submenu( - contact_list, account, roster.on_invite_to_room, room_jid, - account)) - else: - # use resource if it's self contact - if contact.jid == app.get_jid_from_account(account): - resource = contact.resource - else: - resource = None - menuitem.connect('activate', roster.on_invite_to_room, list_, - room_jid, account, resource) - invite_to_submenu.append(menuitem) - - if not show_bookmarked: - return - rooms2 = [] # a list of (room_jid, account) tuple - r_jids = [] # list of room jids - for account in connected_accounts: - con = app.connections[account] - for bookmark in con.get_module('Bookmarks').bookmarks: - if bookmark.jid in r_jids: - continue - if bookmark.jid not in app.gc_connected[account] or not \ - app.gc_connected[account][bookmark.jid]: - rooms2.append((bookmark.jid, account)) - r_jids.append(bookmark.jid) - - if not rooms2: - return - item = Gtk.SeparatorMenuItem.new() # separator - invite_to_submenu.append(item) - for (room_jid, account) in rooms2: - menuitem = Gtk.MenuItem.new_with_label(room_jid.localpart) - if len(contact_list) > 1: # several resources - menuitem.set_submenu(build_resources_submenu( - contact_list, account, roster.on_invite_to_room, str(room_jid), - account)) - else: - # use resource if it's self contact - if contact.jid == app.get_jid_from_account(account): - resource = contact.resource - else: - resource = None - menuitem.connect('activate', roster.on_invite_to_room, list_, - str(room_jid), account, resource) - invite_to_submenu.append(menuitem) - -def get_contact_menu(contact, account, use_multiple_contacts=True, -show_start_chat=True, show_encryption=False, show_buttonbar_items=True, -control=None, gc_contact=None, is_anonymous=True): - """ - Build contact popup menu for roster and chat window. If control is not set, - we hide invite_contacts_menuitem - """ - if not contact: - return - - jid = contact.jid - our_jid = jid == app.get_jid_from_account(account) - roster = app.interface.roster - - xml = get_builder('contact_context_menu.ui') - contact_context_menu = xml.get_object('contact_context_menu') - - start_chat_menuitem = xml.get_object('start_chat_menuitem') - execute_command_menuitem = xml.get_object('execute_command_menuitem') - rename_menuitem = xml.get_object('rename_menuitem') - edit_groups_menuitem = xml.get_object('edit_groups_menuitem') - send_file_menuitem = xml.get_object('send_file_menuitem') - information_menuitem = xml.get_object('information_menuitem') - history_menuitem = xml.get_object('history_menuitem') - send_single_message_menuitem = xml.get_object('send_single_message_menuitem') - invite_menuitem = xml.get_object('invite_menuitem') - block_menuitem = xml.get_object('block_menuitem') - unblock_menuitem = xml.get_object('unblock_menuitem') - ignore_menuitem = xml.get_object('ignore_menuitem') - unignore_menuitem = xml.get_object('unignore_menuitem') - # Subscription submenu - subscription_menuitem = xml.get_object('subscription_menuitem') - send_auth_menuitem, ask_auth_menuitem, revoke_auth_menuitem = \ - subscription_menuitem.get_submenu().get_children() - add_to_roster_menuitem = xml.get_object('add_to_roster_menuitem') - remove_from_roster_menuitem = xml.get_object( - 'remove_from_roster_menuitem') - manage_contact_menuitem = xml.get_object('manage_contact') - convert_to_gc_menuitem = xml.get_object('convert_to_groupchat_menuitem') - last_separator = xml.get_object('last_separator') - - items_to_hide = [] - - contacts = app.contacts.get_contacts(account, jid) - if len(contacts) > 1 and use_multiple_contacts: # several resources - send_file_menuitem.set_submenu(build_resources_submenu( - contacts, - account, - roster.on_send_file_menuitem_activate, - cap=Namespace.JINGLE_FILE_TRANSFER_5)) - execute_command_menuitem.set_submenu(build_resources_submenu( - contacts, account, roster.on_execute_command, cap=Namespace.COMMANDS)) - else: - if contact.supports(Namespace.JINGLE_FILE_TRANSFER_5): - send_file_menuitem.set_sensitive(True) - send_file_menuitem.connect('activate', - roster.on_send_file_menuitem_activate, contact, account) - else: - send_file_menuitem.set_sensitive(False) - - if contact.supports(Namespace.COMMANDS): - execute_command_menuitem.set_sensitive(True) - if gc_contact and gc_contact.jid and not is_anonymous: - execute_command_menuitem.connect('activate', - roster.on_execute_command, gc_contact, account, - gc_contact.resource) - else: - execute_command_menuitem.connect('activate', - roster.on_execute_command, contact, account, - contact.resource) - else: - execute_command_menuitem.set_sensitive(False) - - start_chat_menuitem.connect( - 'activate', app.interface.on_open_chat_window, contact, account) - - rename_menuitem.connect('activate', roster.on_rename, 'contact', jid, - account) - - history_menuitem.set_action_name('app.browse-history') - dict_ = {'jid': GLib.Variant('s', contact.jid), - 'account': GLib.Variant('s', account)} - variant = GLib.Variant('a{sv}', dict_) - history_menuitem.set_action_target_value(variant) - - if control: - convert_to_gc_menuitem.connect('activate', - control._on_convert_to_gc_menuitem_activate) - else: - items_to_hide.append(convert_to_gc_menuitem) - - if _('Not in contact list') not in contact.get_shown_groups(): - # contact is in normal group - edit_groups_menuitem.connect('activate', roster.on_edit_groups, [(contact, - account)]) - else: - # contact is in group 'Not in contact list' - edit_groups_menuitem.set_sensitive(False) - - # Hide items when it's self contact row - if our_jid: - items_to_hide += [rename_menuitem, edit_groups_menuitem] - - # Unsensitive many items when account is offline - if app.account_is_disconnected(account): - for widget in (start_chat_menuitem, rename_menuitem, - edit_groups_menuitem, send_file_menuitem, convert_to_gc_menuitem, - information_menuitem): - widget.set_sensitive(False) - - if not show_start_chat: - items_to_hide.append(start_chat_menuitem) - - if not show_buttonbar_items: - items_to_hide += [history_menuitem, send_file_menuitem, - information_menuitem, convert_to_gc_menuitem, last_separator] - - if not control: - items_to_hide.append(convert_to_gc_menuitem) - - # Hide items when it's a pm - if gc_contact: - items_to_hide += [rename_menuitem, edit_groups_menuitem, - subscription_menuitem, remove_from_roster_menuitem] - - for item in items_to_hide: - item.set_no_show_all(True) - item.hide() - - # Zeroconf Account - if app.settings.get_account_setting(account, 'is_zeroconf'): - for item in (send_single_message_menuitem, - invite_menuitem, block_menuitem, unblock_menuitem, ignore_menuitem, - unignore_menuitem, subscription_menuitem, - manage_contact_menuitem, convert_to_gc_menuitem): - item.set_no_show_all(True) - item.hide() - - if contact.show in ('offline', 'error'): - information_menuitem.set_sensitive(False) - send_file_menuitem.set_sensitive(False) - else: - information_menuitem.connect('activate', roster.on_info_zeroconf, - contact, account) - - contact_context_menu.connect('selection-done', - gtkgui_helpers.destroy_widget) - contact_context_menu.show_all() - return contact_context_menu - - # normal account - - if gc_contact: - if not gc_contact.jid: - # it's a pm and we don't know real JID - invite_menuitem.set_sensitive(False) - else: - bookmarked = False - c_ = app.contacts.get_contact(account, gc_contact.jid, - gc_contact.resource) - if c_ and c_.supports(Namespace.CONFERENCE): - bookmarked = True - build_invite_submenu(invite_menuitem, [(gc_contact, account)], - show_bookmarked=bookmarked) - else: - force_resource = False - if control and control.resource: - force_resource = True - build_invite_submenu(invite_menuitem, [(contact, account)], - show_bookmarked=contact.supports(Namespace.CONFERENCE), - force_resource=force_resource) - - if app.account_is_disconnected(account): - invite_menuitem.set_sensitive(False) - - send_single_message_menuitem.connect('activate', - roster.on_send_single_message_menuitem_activate, account, contact) - - remove_from_roster_menuitem.connect('activate', roster.on_req_usub, - [(contact, account)]) - information_menuitem.connect('activate', roster.on_info, contact, account) - - if _('Not in contact list') not in contact.get_shown_groups(): - # contact is in normal group - add_to_roster_menuitem.hide() - add_to_roster_menuitem.set_no_show_all(True) - - if contact.sub in ('from', 'both'): - send_auth_menuitem.set_sensitive(False) - else: - send_auth_menuitem.connect('activate', roster.authorize, jid, account) - if contact.sub in ('to', 'both'): - ask_auth_menuitem.set_sensitive(False) - else: - ask_auth_menuitem.connect('activate', roster.req_sub, jid, - _('I would like to add you to my contact list'), account, - contact.groups, contact.name) - transport = app.get_transport_name_from_jid(jid, - use_config_setting=False) - if contact.sub in ('to', 'none') or transport not in ['jabber', None]: - revoke_auth_menuitem.set_sensitive(False) - else: - revoke_auth_menuitem.connect('activate', roster.revoke_auth, jid, - account) - - elif app.connections[account].roster_supported: - # contact is in group 'Not in contact list' - add_to_roster_menuitem.set_no_show_all(False) - subscription_menuitem.set_sensitive(False) - - add_to_roster_menuitem.connect('activate', roster.on_add_to_roster, - contact, account) - else: - add_to_roster_menuitem.hide() - add_to_roster_menuitem.set_no_show_all(True) - subscription_menuitem.set_sensitive(False) - - # Hide items when it's self contact row - if our_jid: - manage_contact_menuitem.set_sensitive(False) - - # Unsensitive items when account is offline - if app.account_is_disconnected(account): - for widget in (send_single_message_menuitem, subscription_menuitem, - add_to_roster_menuitem, remove_from_roster_menuitem, - execute_command_menuitem): - widget.set_sensitive(False) - - - con = app.connections[account] - if con.get_module('Blocking').supported: - transport = app.get_transport_name_from_jid(jid, use_config_setting=False) - if helpers.jid_is_blocked(account, jid): - block_menuitem.set_no_show_all(True) - block_menuitem.hide() - if transport not in ('jabber', None): - unblock_menuitem.set_no_show_all(True) - unblock_menuitem.hide() - unignore_menuitem.set_no_show_all(False) - unignore_menuitem.connect('activate', roster.on_unblock, [(contact, - account)]) - else: - unblock_menuitem.connect('activate', roster.on_unblock, [(contact, - account)]) - else: - unblock_menuitem.set_no_show_all(True) - unblock_menuitem.hide() - if transport not in ('jabber', None): - block_menuitem.set_no_show_all(True) - block_menuitem.hide() - ignore_menuitem.set_no_show_all(False) - ignore_menuitem.connect('activate', roster.on_block, [(contact, - account)]) - else: - block_menuitem.connect('activate', roster.on_block, [(contact, - account)]) - else: - unblock_menuitem.set_no_show_all(True) - block_menuitem.set_sensitive(False) - unblock_menuitem.hide() - - contact_context_menu.connect('selection-done', gtkgui_helpers.destroy_widget) - contact_context_menu.show_all() - return contact_context_menu def get_transport_menu(contact, account): + # TODO: roster = app.interface.roster jid = contact.jid @@ -434,7 +38,7 @@ def get_transport_menu(contact, account): # Send single message item = Gtk.MenuItem.new_with_mnemonic(_('Send Single _Message…')) item.connect('activate', roster.on_send_single_message_menuitem_activate, - account, contact) + account, contact) menu.append(item) if app.account_is_disconnected(account): item.set_sensitive(False) @@ -443,14 +47,14 @@ def get_transport_menu(contact, account): if helpers.jid_is_blocked(account, jid): blocked = True - item = Gtk.SeparatorMenuItem.new() # separator + item = Gtk.SeparatorMenuItem.new() menu.append(item) # Execute Command item = Gtk.MenuItem.new_with_mnemonic(_('E_xecute Command…')) menu.append(item) item.connect('activate', roster.on_execute_command, contact, account, - contact.resource) + contact.resource) if app.account_is_disconnected(account): item.set_sensitive(False) @@ -474,7 +78,7 @@ def get_transport_menu(contact, account): if app.account_is_disconnected(account): item.set_sensitive(False) - item = Gtk.SeparatorMenuItem.new() # separator + item = Gtk.SeparatorMenuItem.new() manage_transport_submenu.append(item) # Block @@ -496,7 +100,7 @@ def get_transport_menu(contact, account): if app.account_is_disconnected(account): item.set_sensitive(False) - item = Gtk.SeparatorMenuItem.new() # separator + item = Gtk.SeparatorMenuItem.new() menu.append(item) # Information @@ -506,7 +110,7 @@ def get_transport_menu(contact, account): if app.account_is_disconnected(account): information_menuitem.set_sensitive(False) - menu.connect('selection-done', gtkgui_helpers.destroy_widget) + menu.connect('selection-done', destroy_widget) menu.show_all() return menu @@ -516,7 +120,7 @@ def get_singlechat_menu(control_id, account, jid, type_): (_('Send File'), [ ('win.send-file-httpupload-', _('Upload File…')), ('win.send-file-jingle-', _('Send File Directly…')), - ]), + ]), ('win.send-marker-', _('Send Read Markers')), (_('Send Chatstate'), ['chatstate']), ('win.invite-contacts-', _('Invite Contacts…')), @@ -776,7 +380,7 @@ def get_conv_context_menu(account, uri): ('-add-contact', _('Add to Contact List…')), ] else: - return + return None menu = Gtk.Menu() for item in context_menu: View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/18ce70583e1c3e94f8d1f8ad072cc74…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/18ce70583e1c3e94f8d1f8ad072cc74…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][mainwindow] 2 commits: Move TransformChatToMUC to AdhocMUC and adapt to Gajim changes
by Daniel Brötzmann (@wurstsalat)
03 Sep '21
03 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: 9279f402 by wurstsalat at 2021-09-03T17:48:08+02:00 Move TransformChatToMUC to AdhocMUC and adapt to Gajim changes - - - - - 18ce7058 by wurstsalat at 2021-09-03T17:48:12+02:00 GTK GUI Helpers: Remove unused methods - - - - - 7 changed files: - gajim/data/gui/chat_to_muc_window.ui → gajim/data/gui/adhoc_muc.ui - gajim/dialogs.py → gajim/gtk/adhoc_muc.py - gajim/gtk/controls/base.py - gajim/gtk/controls/chat.py - gajim/gtk/controls/groupchat.py - gajim/gtk/tooltips.py - gajim/gtkgui_helpers.py Changes: ===================================== gajim/data/gui/chat_to_muc_window.ui → gajim/data/gui/adhoc_muc.ui ===================================== @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.20.0 --> +<!-- Generated with glade 3.38.2 --> <interface> - <requires lib="gtk+" version="3.12"/> + <requires lib="gtk+" version="3.24"/> <object class="GtkListStore" id="guests_store"> <columns> <!-- column-name icon --> @@ -12,29 +12,24 @@ <column type="gchararray"/> </columns> </object> - <object class="GtkImage" id="image1"> + <object class="GtkWindow" id="adhoc_muc_window"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-jump-to</property> - </object> - <object class="GtkWindow" id="chat_to_muc_window"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="border_width">5</property> + <property name="can-focus">False</property> + <property name="border-width">5</property> <property name="title" translatable="yes">Invite Friends!</property> - <property name="type_hint">dialog</property> + <property name="type-hint">dialog</property> <signal name="destroy" handler="on_chat_to_muc_window_destroy" swapped="no"/> <signal name="key-press-event" handler="on_chat_to_muc_window_key_press_event" swapped="no"/> <child> - <object class="GtkBox" id="general_vbox"> + <object class="GtkBox"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="orientation">vertical</property> <property name="spacing">6</property> <child> - <object class="GtkLabel" id="label3"> + <object class="GtkLabel"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="label" translatable="yes">You are now entering a group chat. Select the contacts you want to invite</property> @@ -50,17 +45,17 @@ Select the contacts you want to invite</property> <child> <object class="GtkScrolledWindow" id="guests_scrolledwindow"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="shadow_type">in</property> + <property name="can-focus">True</property> + <property name="shadow-type">in</property> <child> <object class="GtkTreeView" id="guests_treeview"> - <property name="height_request">300</property> + <property name="height-request">300</property> <property name="visible">True</property> - <property name="can_focus">True</property> + <property name="can-focus">True</property> <property name="model">guests_store</property> - <property name="headers_visible">False</property> + <property name="headers-visible">False</property> <child internal-child="selection"> - <object class="GtkTreeSelection" id="treeview-selection1"/> + <object class="GtkTreeSelection"/> </child> <child> <object class="GtkTreeViewColumn"> @@ -95,20 +90,20 @@ Select the contacts you want to invite</property> </packing> </child> <child> - <object class="GtkExpander" id="expander1"> + <object class="GtkExpander"> <property name="visible">True</property> - <property name="can_focus">True</property> + <property name="can-focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <child> - <object class="GtkBox" id="vbox1"> + <object class="GtkBox"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="orientation">vertical</property> <child> <object class="GtkLabel" id="server_select_label"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="label" translatable="yes">Please select a group chat server.</property> <property name="justify">center</property> <property name="wrap">True</property> @@ -123,7 +118,7 @@ Select the contacts you want to invite</property> <child> <object class="GtkComboBox" id="server_list_comboboxentry"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> </object> <packing> <property name="expand">False</property> @@ -134,9 +129,9 @@ Select the contacts you want to invite</property> </object> </child> <child type="label"> - <object class="GtkLabel" id="label2"> + <object class="GtkLabel"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="label" translatable="yes">Group chat server</property> </object> @@ -149,19 +144,19 @@ Select the contacts you want to invite</property> </packing> </child> <child> - <object class="GtkButtonBox" id="hbuttonbox1"> + <object class="GtkButtonBox"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="spacing">6</property> - <property name="layout_style">end</property> + <property name="layout-style">end</property> <child> <object class="GtkButton" id="cancel_button"> - <property name="label">gtk-cancel</property> + <property name="label">_Cancel</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> <signal name="clicked" handler="on_cancel_button_clicked" swapped="no"/> </object> <packing> @@ -173,12 +168,11 @@ Select the contacts you want to invite</property> </child> <child> <object class="GtkButton" id="invite_button"> - <property name="label" translatable="yes">In_vite</property> + <property name="label" translatable="yes">Invite</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="image">image1</property> - <property name="use_underline">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> <signal name="clicked" handler="on_invite_button_clicked" swapped="no"/> </object> <packing> ===================================== gajim/dialogs.py → gajim/gtk/adhoc_muc.py ===================================== @@ -1,16 +1,3 @@ -# Copyright (C) 2003-2005 Vincent Hanquez <tab AT
snarc.org
> -# Copyright (C) 2003-2014 Yann Leboulanger <asterix AT
lagaule.org
> -# Copyright (C) 2005 Alex Mauer <hawke AT
hawkesnest.net
> -# Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT
gmail.com
> -# Travis Shirk <travis AT
pobox.com
> -# Copyright (C) 2005-2008 Nikos Kouremenos <kourem AT
gmail.com
> -# Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT
lapin.org
> -# Copyright (C) 2007 Lukas Petrovicky <lukas AT
petrovicky.net
> -# Copyright (C) 2007-2008 Brendan Taylor <whateley AT
gmail.com
> -# Julien Pivotto <roidelapluie AT
gmail.com
> -# Stephan Erb <steve-e AT h3c.de> -# Copyright (C) 2008 Jonathan Schleifer <js-gajim AT
webkeks.org
> -# # This file is part of Gajim. # # Gajim is free software; you can redistribute it and/or modify @@ -36,16 +23,19 @@ from gi.repository import Gdk from gajim.common import app +from gajim.common.i18n import _ + +from .util import get_icon_name +from .util import get_builder -from gajim.gui.util import get_icon_name -from gajim.gui.util import get_builder +log = logging.getLogger('gajim.gui.adhoc_muc') -log = logging.getLogger('gajim.dialogs') +class AdhocMUC(Gtk.ApplicationWindow): -class TransformChatToMUC: # Keep a reference on windows so garbage collector don't restroy them - instances = [] # type: List[TransformChatToMUC] + instances = [] # type: List[AdhocMUC] + def __init__(self, account, jids, preselected=None): """ This window is used to transform a one-to-one chat to a MUC. We do 2 @@ -57,111 +47,107 @@ def __init__(self, account, jids, preselected=None): self.auto_jids = jids self.preselected_jids = preselected - self.xml = get_builder('chat_to_muc_window.ui') - self.window = self.xml.get_object('chat_to_muc_window') - - for widget_to_add in ('invite_button', 'cancel_button', - 'server_list_comboboxentry', 'guests_treeview', 'guests_store', - 'server_and_guests_hseparator', 'server_select_label'): - self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add) + self.xml = get_builder('adhoc_muc.ui') + self.window = self.xml.get_object('adhoc_muc_window') server_list = [] self.servers = Gtk.ListStore(str) - self.server_list_comboboxentry.set_model(self.servers) + self.xml.server_list_comboboxentry.set_model(self.servers) cell = Gtk.CellRendererText() - self.server_list_comboboxentry.pack_start(cell, True) - self.server_list_comboboxentry.add_attribute(cell, 'text', 0) + self.xml.server_list_comboboxentry.pack_start(cell, True) + self.xml.server_list_comboboxentry.add_attribute(cell, 'text', 0) # get the muc server of our server - con = app.connections[account] - service_jid = con.get_module('MUC').service_jid + self._client = app.get_client(account) + service_jid = self._client.get_module('MUC').service_jid if service_jid is not None: server_list.append(str(service_jid)) # add servers or recently joined groupchats - recently_groupchat = app.settings.get_account_setting( + recent_groupchats = app.settings.get_account_setting( account, 'recent_groupchats').split() - for g in recently_groupchat: - server = app.get_server_from_jid(g) + for group_chat in recent_groupchats: + server = app.get_server_from_jid(group_chat) if server not in server_list and not server.startswith('irc'): server_list.append(server) # add a default server if not server_list: server_list.append('
conference.jabber.org
') - for s in server_list: - self.servers.append([s]) + for serv in server_list: + self.servers.append([serv]) - self.server_list_comboboxentry.set_active(0) + self.xml.server_list_comboboxentry.set_active(0) # set treeview # name, jid - self.guests_store.set_sort_column_id(1, Gtk.SortType.ASCENDING) - self.guests_treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) - - # All contacts beside the following can be invited: - # transports, zeroconf contacts, minimized groupchats - def invitable(contact, contact_transport=None): - return (contact.jid not in self.auto_jids and - contact.jid != app.get_jid_from_account(account) and - contact.jid not in app.interface.minimized_controls[account] and - not contact.is_transport() and - contact_transport in ('jabber', None)) - - # set jabber id and pseudos - for account_ in app.settings.get_active_accounts(): - if app.connections[account_].is_zeroconf: + self.xml.guests_store.set_sort_column_id(1, Gtk.SortType.ASCENDING) + self.xml.guests_treeview.get_selection().set_mode( + Gtk.SelectionMode.MULTIPLE) + + for acc in app.settings.get_active_accounts(): + client = app.get_client(acc) + if client.is_zeroconf: continue - for jid in app.contacts.get_jid_list(account_): - contact = app.contacts.get_contact_with_highest_priority( - account_, jid) - contact_transport = app.get_transport_name_from_jid(jid) - # Add contact if it can be invited - if invitable(contact, contact_transport) and \ - contact.show not in ('offline', 'error'): - icon_name = get_icon_name(contact.show) - name = contact.name - if name == '': - name = jid.split('@')[0] - iter_ = self.guests_store.append([icon_name, name, jid]) + for contact in client.get_module('Roster').iter_contacts(): + jid = str(contact.jid) + if (jid not in self.auto_jids and + jid != str(self._client.get_own_jid().bare)): + icon = get_icon_name(contact.show.value) + iter_ = self.xml.guests_store.append( + [icon, contact.name, jid]) # preselect treeview rows if self.preselected_jids and jid in self.preselected_jids: - path = self.guests_store.get_path(iter_) - self.guests_treeview.get_selection().select_path(path) + path = self.xml.guests_store.get_path(iter_) + self.xml.guests_treeview.get_selection().select_path( + path) # show all self.window.show_all() self.xml.connect_signals(self) - def on_chat_to_muc_window_destroy(self, widget): + def on_chat_to_muc_window_destroy(self, _widget): self.instances.remove(self) - def on_chat_to_muc_window_key_press_event(self, widget, event): - if event.keyval == Gdk.KEY_Escape: # ESCAPE + def on_chat_to_muc_window_key_press_event(self, _widget, event): + if event.keyval == Gdk.KEY_Escape: self.window.destroy() - def on_invite_button_clicked(self, widget): - row = self.server_list_comboboxentry.get_child().get_displayed_row() - model = self.server_list_comboboxentry.get_model() + def on_invite_button_clicked(self, _widget): + row = self.xml.server_list_comboboxentry.get_child().get_displayed_row() + model = self.xml.server_list_comboboxentry.get_model() server = model[row][0].strip() if server == '': return guest_list = [] - guests = self.guests_treeview.get_selection().get_selected_rows() + guests = self.xml.guests_treeview.get_selection().get_selected_rows() for guest in guests[1]: - iter_ = self.guests_store.get_iter(guest) - guest_list.append(self.guests_store[iter_][2]) + iter_ = self.xml.guests_store.get_iter(guest) + guest_list.append(self.xml.guests_store[iter_][2]) for guest in self.auto_jids: guest_list.append(guest) - room_jid = str(uuid.uuid4()) + '@' + server + room_jid = f'{uuid.uuid4()}@{server}' app.automatic_rooms[self.account][room_jid] = {} app.automatic_rooms[self.account][room_jid]['invities'] = guest_list app.automatic_rooms[self.account][room_jid]['continue_tag'] = True - app.interface.create_groupchat(self.account, room_jid) + + config = { + # XEP-0045 options + 'muc#roomconfig_roomname': _('Adhoc Group Chat'), + 'muc#roomconfig_publicroom': False, + 'muc#roomconfig_membersonly': True, + 'muc#roomconfig_whois': 'anyone', + 'muc#roomconfig_changesubject': True, + + # Ejabberd options + 'public_list': False, + } + + app.interface.create_groupchat(self.account, room_jid, config) self.window.destroy() - def on_cancel_button_clicked(self, widget): + def on_cancel_button_clicked(self, _widget): self.window.destroy() ===================================== gajim/gtk/controls/base.py ===================================== @@ -50,8 +50,6 @@ from gajim.common.structs import OutgoingMessage from gajim.common.connection_handlers_events import NotificationEvent -from gajim import gtkgui_helpers - from gajim.gui.conversation.view import ConversationView from gajim.gui.conversation.scrolled import ScrolledView from gajim.gui.conversation.jump_to_end_button import JumpToEndButton @@ -1532,15 +1530,15 @@ def scroll_messages(self, direction, msg_buf, msg_type): not history[pos - 1].startswith('/') or history[pos - 1].startswith('/me'))): self.correcting = True - gtkgui_helpers.add_css_class( - self.msg_textview, 'gajim-msg-correcting') + self.msg_textview.get_style_context().add_class( + 'gajim-msg-correcting') message = history[pos - 1] msg_buf.set_text(message) return if self.correcting: # We were previously correcting - gtkgui_helpers.remove_css_class( - self.msg_textview, 'gajim-msg-correcting') + self.msg_textview.get_style_context().remove_class( + 'gajim-msg-correcting') self.correcting = False pos += -1 if direction == 'up' else +1 if pos == -1: ===================================== gajim/gtk/controls/chat.py ===================================== @@ -50,11 +50,9 @@ from gajim.common.const import KindConstant from gajim.common.const import PEPEventType -from gajim import gtkgui_helpers from gajim import gui_menu_builder -from gajim.dialogs import TransformChatToMUC - +from gajim.gui.adhoc_muc import AdhocMUC from gajim.gui.call_widget import CallWidget from gajim.gui.dialogs import DialogButton from gajim.gui.dialogs import ConfirmationDialog @@ -323,7 +321,7 @@ def _on_invite_contacts(self, _action, _param): """ User wants to invite some friends to chat """ - TransformChatToMUC(self.account, [self.contact.jid]) + AdhocMUC(self.account, [self.contact.jid]) def _on_send_chatstate(self, action, param): action.set_state(param) @@ -478,8 +476,8 @@ def _on_message_sent(self, event): displaymarking = None if self.correcting: self.correcting = False - gtkgui_helpers.remove_css_class( - self.msg_textview, 'gajim-msg-correcting') + self.msg_textview.get_style_context().remove_class( + 'gajim-msg-correcting') self.add_message(event.message, 'outgoing', @@ -746,13 +744,13 @@ def _on_drag_data_received(self, _widget, _context, _x_coord, _y_coord, if dropped_transport or c_transport: return # transport contacts cannot be invited - TransformChatToMUC(self.account, [self.contact.jid], [dropped_jid]) + AdhocMUC(self.account, [self.contact.jid], [dropped_jid]) def _on_convert_to_gc_menuitem_activate(self, _widget): """ User wants to invite some friends to chat """ - TransformChatToMUC(self.account, [self.contact.jid]) + AdhocMUC(self.account, [self.contact.jid]) def _on_client_state_changed(self, _client, _signal_name, state): self.msg_textview.set_sensitive(state.is_connected) ===================================== gajim/gtk/controls/groupchat.py ===================================== @@ -40,7 +40,6 @@ from gi.repository import GLib from gi.repository import Gio -from gajim import gtkgui_helpers from gajim import gui_menu_builder from gajim.common import app @@ -1105,8 +1104,8 @@ def _on_message_sent(self, event): self.last_sent_msg = event.message_id if self.correcting: self.correcting = False - gtkgui_helpers.remove_css_class( - self.msg_textview, 'gajim-msg-correcting') + self.msg_textview.get_style_context().remove_class( + 'gajim-msg-correcting') def send_message(self, message, xhtml=None, process_commands=True): """ ===================================== gajim/gtk/tooltips.py ===================================== @@ -41,7 +41,6 @@ from gajim.common.const import PEPEventType from gajim.common.i18n import Q_ from gajim.common.i18n import _ -from gajim.gtkgui_helpers import add_css_class from .avatar import get_show_circle from .util import get_builder @@ -375,4 +374,9 @@ def colorize_status(widget, show): Colorize the status message inside the tooltip by it's semantics. """ css_class = get_css_show_class(show)[14:] - add_css_class(widget, css_class, prefix='gajim-status-') + style = widget.get_style_context() + # Remove all css classes with gajim-status- prefix + for css_cls in style.list_classes(): + if css_cls.startswith('gajim-status-'): + style.remove_class(css_cls) + style.add_class(f'gajim-status-{css_class}') ===================================== gajim/gtkgui_helpers.py ===================================== @@ -181,37 +181,9 @@ def __label_size_allocate(widget, allocation): def get_action(action): return app.app.lookup_action(action) -def add_css_class(widget, class_name, prefix=None): - if class_name and prefix: - class_name = prefix + class_name - - style = widget.get_style_context() - if prefix is not None: - # Remove all css classes with prefix - for css_cls in style.list_classes(): - if css_cls.startswith(prefix): - style.remove_class(css_cls) - - if class_name is not None: - style.add_class(class_name) - def add_css_to_widget(widget, css): provider = Gtk.CssProvider() provider.load_from_data(bytes(css.encode())) context = widget.get_style_context() context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) - -def remove_css_class(widget, class_name): - style = widget.get_style_context() - style.remove_class(class_name) - -def pango_to_css_weight(number): - # Pango allows for weight values between 100 and 1000 - # CSS allows only full hundred numbers like 100, 200 .. - number = int(number) - if number < 100: - return 100 - if number > 900: - return 900 - return int(math.ceil(number / 100.0)) * 100 View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/b9f3ff425d493b8856c23a3f297e879…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/b9f3ff425d493b8856c23a3f297e879…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][mainwindow] Fix synchronising contacts
by Daniel Brötzmann (@wurstsalat)
02 Sep '21
02 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: b9f3ff42 by wurstsalat at 2021-09-03T01:18:27+02:00 Fix synchronising contacts - - - - - 3 changed files: - gajim/app_actions.py - gajim/dialogs.py - + gajim/gtk/synchronise_contacts.py Changes: ===================================== gajim/app_actions.py ===================================== @@ -21,11 +21,11 @@ from gajim.common import helpers from gajim.common.app import interface from gajim.common.exceptions import GajimGeneralException -from gajim import dialogs from gajim.gui.dialogs import ShortcutsWindow from gajim.gui.about import AboutDialog from gajim.gui.discovery import ServiceDiscoveryWindow +from gajim.gui.synchronise_contacts import SynchroniseSelectAccountDialog from gajim.gui.util import open_window # General Actions @@ -127,7 +127,7 @@ def on_import_contacts(_action, param): app.interface.instances['import_contacts'].dialog.present() else: app.interface.instances['import_contacts'] = \ - dialogs.SynchroniseSelectAccountDialog(account) + SynchroniseSelectAccountDialog(account) # Advanced Actions ===================================== gajim/dialogs.py ===================================== @@ -35,161 +35,14 @@ from gi.repository import Gtk from gi.repository import Gdk -from gajim.common.i18n import _ - from gajim.common import app -from gajim.common.exceptions import GajimGeneralException -from gajim.gui.dialogs import ErrorDialog from gajim.gui.util import get_icon_name from gajim.gui.util import get_builder -from gajim.gui.util import get_app_window log = logging.getLogger('gajim.dialogs') -class SynchroniseSelectAccountDialog: - def __init__(self, account): - # 'account' can be None if we are about to create our first one - if not app.account_is_available(account): - ErrorDialog(_('You are not connected to the server'), - _('Without a connection, you can not synchronise your contacts.')) - raise GajimGeneralException('You are not connected to the server') - self.account = account - self.xml = get_builder('synchronise_select_account_dialog.ui') - self.dialog = self.xml.get_object('synchronise_select_account_dialog') - self.dialog.set_transient_for(get_app_window('AccountsWindow')) - self.accounts_treeview = self.xml.get_object('accounts_treeview') - model = Gtk.ListStore(str, str, bool) - self.accounts_treeview.set_model(model) - # columns - renderer = Gtk.CellRendererText() - self.accounts_treeview.insert_column_with_attributes(-1, _('Name'), - renderer, text=0) - renderer = Gtk.CellRendererText() - self.accounts_treeview.insert_column_with_attributes(-1, _('Server'), - renderer, text=1) - - self.xml.connect_signals(self) - self.init_accounts() - self.dialog.show_all() - - def on_accounts_window_key_press_event(self, widget, event): - if event.keyval == Gdk.KEY_Escape: - self.window.destroy() - - def init_accounts(self): - """ - Initialize listStore with existing accounts - """ - model = self.accounts_treeview.get_model() - model.clear() - for remote_account in app.connections: - if remote_account == self.account: - # Do not show the account we're sync'ing - continue - iter_ = model.append() - model.set(iter_, 0, remote_account, 1, - app.get_hostname_from_account(remote_account)) - - def on_cancel_button_clicked(self, widget): - self.dialog.destroy() - - def on_ok_button_clicked(self, widget): - sel = self.accounts_treeview.get_selection() - (model, iter_) = sel.get_selected() - if not iter_: - return - remote_account = model.get_value(iter_, 0) - - if not app.account_is_available(remote_account): - ErrorDialog(_('This account is not connected to the server'), - _('You cannot synchronize with an account unless it is connected.')) - return - - try: - SynchroniseSelectContactsDialog(self.account, remote_account) - except GajimGeneralException: - # if we showed ErrorDialog, there will not be dialog instance - return - self.dialog.destroy() - - @staticmethod - def on_destroy(widget): - del app.interface.instances['import_contacts'] - - -class SynchroniseSelectContactsDialog: - def __init__(self, account, remote_account): - self.local_account = account - self.remote_account = remote_account - self.xml = get_builder('synchronise_select_contacts_dialog.ui') - self.dialog = self.xml.get_object('synchronise_select_contacts_dialog') - self.contacts_treeview = self.xml.get_object('contacts_treeview') - model = Gtk.ListStore(bool, str) - self.contacts_treeview.set_model(model) - # columns - renderer1 = Gtk.CellRendererToggle() - renderer1.set_property('activatable', True) - renderer1.connect('toggled', self.toggled_callback) - self.contacts_treeview.insert_column_with_attributes(-1, - _('Synchronise'), renderer1, active=0) - renderer2 = Gtk.CellRendererText() - self.contacts_treeview.insert_column_with_attributes(-1, _('Name'), - renderer2, text=1) - - self.xml.connect_signals(self) - self.init_contacts() - self.dialog.show_all() - - def toggled_callback(self, cell, path): - model = self.contacts_treeview.get_model() - iter_ = model.get_iter(path) - model[iter_][0] = not cell.get_active() - - def on_contacts_window_key_press_event(self, widget, event): - if event.keyval == Gdk.KEY_Escape: - self.window.destroy() - - def init_contacts(self): - """ - Initialize listStore with existing accounts - """ - model = self.contacts_treeview.get_model() - model.clear() - - # recover local contacts - local_jid_list = app.contacts.get_contacts_jid_list(self.local_account) - - remote_jid_list = app.contacts.get_contacts_jid_list( - self.remote_account) - for remote_jid in remote_jid_list: - if remote_jid not in local_jid_list: - iter_ = model.append() - model.set(iter_, 0, True, 1, remote_jid) - - def on_cancel_button_clicked(self, widget): - self.dialog.destroy() - - def on_ok_button_clicked(self, widget): - model = self.contacts_treeview.get_model() - iter_ = model.get_iter_first() - while iter_: - if model[iter_][0]: - # it is selected - remote_jid = model[iter_][1] - message = 'I\'m synchronizing my contacts from my %s account, could you please add this address to your contact list?' % \ - app.get_hostname_from_account(self.remote_account) - remote_contact = app.contacts.get_first_contact_from_jid( - self.remote_account, remote_jid) - # keep same groups and same nickname - app.interface.roster.req_sub(self, remote_jid, message, - self.local_account, groups=remote_contact.groups, - nickname=remote_contact.name, auto_auth=True) - iter_ = model.iter_next(iter_) - self.dialog.destroy() - - class TransformChatToMUC: # Keep a reference on windows so garbage collector don't restroy them instances = [] # type: List[TransformChatToMUC] ===================================== gajim/gtk/synchronise_contacts.py ===================================== @@ -0,0 +1,188 @@ +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see <
http://www.gnu.org/licenses/
>. + +from gi.repository import Gdk +from gi.repository import Gtk + +from gajim.common import app +from gajim.common.exceptions import GajimGeneralException +from gajim.common.i18n import _ + +from .dialogs import ErrorDialog +from .util import get_app_window +from .util import get_builder + + +class SynchroniseSelectAccountDialog: + def __init__(self, account): + # 'account' can be None if we are about to create our first one + if not app.account_is_available(account): + ErrorDialog( + _('You are not connected to the server'), + _('You cannot synchronize with an account unless it is ' + 'connected.')) + raise GajimGeneralException('You are not connected to the server') + self.account = account + self.xml = get_builder('synchronise_select_account_dialog.ui') + self.dialog = self.xml.get_object('synchronise_select_account_dialog') + self.dialog.set_transient_for(get_app_window('AccountsWindow')) + self.accounts_treeview = self.xml.get_object('accounts_treeview') + model = Gtk.ListStore(str, str, bool) + self.accounts_treeview.set_model(model) + # columns + renderer = Gtk.CellRendererText() + self.accounts_treeview.insert_column_with_attributes( + -1, _('Name'), renderer, text=0) + renderer = Gtk.CellRendererText() + self.accounts_treeview.insert_column_with_attributes( + -1, _('Server'), renderer, text=1) + + self.xml.connect_signals(self) + self.init_accounts() + self.dialog.show_all() + + def on_accounts_window_key_press_event(self, _widget, event): + if event.keyval == Gdk.KEY_Escape: + self.window.destroy() + + def init_accounts(self): + """ + Initialize listStore with existing accounts + """ + model = self.accounts_treeview.get_model() + model.clear() + for remote_account in app.connections: + if remote_account == self.account: + # Do not show the account we're sync'ing + continue + iter_ = model.append() + model.set( + iter_, + 0, + remote_account, + 1, + app.get_hostname_from_account(remote_account)) + + def on_cancel_button_clicked(self, _widget): + self.dialog.destroy() + + def on_ok_button_clicked(self, _widget): + sel = self.accounts_treeview.get_selection() + (model, iter_) = sel.get_selected() + if not iter_: + return + remote_account = model.get_value(iter_, 0) + + if not app.account_is_available(remote_account): + ErrorDialog( + _('This account is not connected to the server'), + _('You cannot synchronize with an account unless it is ' + 'connected.')) + return + + try: + SynchroniseSelectContactsDialog(self.account, remote_account) + except GajimGeneralException: + # if we showed ErrorDialog, there will not be dialog instance + return + self.dialog.destroy() + + @staticmethod + def on_destroy(_widget): + del app.interface.instances['import_contacts'] + + +class SynchroniseSelectContactsDialog: + def __init__(self, local_account, remote_account): + self._remote_account = remote_account + + self._local_client = app.get_client(local_account) + self._remote_client = app.get_client(remote_account) + + self.xml = get_builder('synchronise_select_contacts_dialog.ui') + self.dialog = self.xml.get_object('synchronise_select_contacts_dialog') + self.contacts_treeview = self.xml.get_object('contacts_treeview') + model = Gtk.ListStore(bool, str) + self.contacts_treeview.set_model(model) + # columns + renderer1 = Gtk.CellRendererToggle() + renderer1.set_property('activatable', True) + renderer1.connect('toggled', self.toggled_callback) + self.contacts_treeview.insert_column_with_attributes( + -1, _('Synchronise'), renderer1, active=0) + renderer2 = Gtk.CellRendererText() + self.contacts_treeview.insert_column_with_attributes( + -1, _('Name'), renderer2, text=1) + + self.xml.connect_signals(self) + self.init_contacts() + self.dialog.show_all() + + def toggled_callback(self, cell, path): + model = self.contacts_treeview.get_model() + iter_ = model.get_iter(path) + model[iter_][0] = not cell.get_active() + + def on_contacts_window_key_press_event(self, _widget, event): + if event.keyval == Gdk.KEY_Escape: + self.window.destroy() + + def init_contacts(self): + """ + Initialize listStore with existing accounts + """ + model = self.contacts_treeview.get_model() + model.clear() + + # recover local contacts + local_jid_list = [] + for contact in self._local_client.get_module('Roster').iter_contacts(): + local_jid_list.append(str(contact.jid)) + + remote_jid_list = [] + for contact in self._remote_client.get_module('Roster').iter_contacts(): + remote_jid_list.append(str(contact.jid)) + + for remote_jid in remote_jid_list: + if remote_jid not in local_jid_list: + iter_ = model.append() + model.set(iter_, 0, True, 1, remote_jid) + + def on_cancel_button_clicked(self, _widget): + self.dialog.destroy() + + def on_ok_button_clicked(self, _widget): + model = self.contacts_treeview.get_model() + iter_ = model.get_iter_first() + while iter_: + if model[iter_][0]: + # it is selected + remote_jid = model[iter_][1] + message = _('I’m synchronizing my contacts from my account at ' + '"%s". Could you please add this address to your ' + 'contact list?' % app.get_hostname_from_account( + self._remote_account)) + remote_contact = self._remote_client.get_module( + 'Contacts').get_contact(remote_jid) + + # keep same groups and same nickname + self._local_client.get_module('Presence').subscribe( + remote_jid, + msg=message, + name=remote_contact.name, + groups=remote_contact.groups, + auto_auth=True) + + iter_ = model.iter_next(iter_) + self.dialog.destroy() View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/commit/b9f3ff425d493b8856c23a3f297e879a…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/commit/b9f3ff425d493b8856c23a3f297e879a…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][mainwindow] 2 commits: ConversationRow: Improve timestamp margin
by Daniel Brötzmann (@wurstsalat)
02 Sep '21
02 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: b33b78fc by wurstsalat at 2021-09-03T00:19:20+02:00 ConversationRow: Improve timestamp margin - - - - - 14862f20 by wurstsalat at 2021-09-03T00:27:23+02:00 ChatList: Improve close button margins and transition - - - - - 2 changed files: - gajim/data/gui/chat_list_row.ui - gajim/gtk/conversation/rows/base.py Changes: ===================================== gajim/data/gui/chat_list_row.ui ===================================== @@ -230,14 +230,14 @@ <property name="can-focus">False</property> <property name="halign">end</property> <property name="transition-type">crossfade</property> - <property name="transition-duration">100</property> + <property name="transition-duration">50</property> <child> <object class="GtkButton" id="close_button"> <property name="visible">True</property> <property name="can-focus">True</property> <property name="receives-default">True</property> <property name="valign">center</property> - <property name="margin-end">3</property> + <property name="margin-end">6</property> <child> <object class="GtkImage"> <property name="visible">True</property> ===================================== gajim/gtk/conversation/rows/base.py ===================================== @@ -71,6 +71,7 @@ def create_timestamp_widget(timestamp: datetime) -> Gtk.Label: label = Gtk.Label(label=timestamp_formatted) label.set_halign(Gtk.Align.START) label.set_valign(Gtk.Align.END) + label.set_margin_end(6) label.get_style_context().add_class('conversation-meta') label.set_tooltip_text(timestamp.strftime('%a, %d %b %Y - %X')) return label View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/57340decb181f53fa4d578ecd3a85f5…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/57340decb181f53fa4d578ecd3a85f5…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][mainwindow] 7 commits: CallWidget: Ignore jingle file requests
by Daniel Brötzmann (@wurstsalat)
02 Sep '21
02 Sep '21
Daniel Brötzmann pushed to branch mainwindow at gajim / gajim Commits: e55803ca by wurstsalat at 2021-08-31T20:11:47+02:00 CallWidget: Ignore jingle file requests - - - - - 6cafceaa by wurstsalat at 2021-08-31T20:14:36+02:00 Tooltips: Fix FileTransfersTooltip - - - - - f31a04ca by wurstsalat at 2021-08-31T20:33:21+02:00 JingleSession: Don't rely on 'initiator' in session-initiate - - - - - b5e487f4 by wurstsalat at 2021-09-02T22:38:00+02:00 Add ResourceSelector widget - - - - - a1e80261 by wurstsalat at 2021-09-02T23:23:47+02:00 Interface: Adapt to Contact changes - - - - - 2038a0d9 by wurstsalat at 2021-09-02T23:25:05+02:00 Jingle: Remove PM file transfers - - - - - 57340dec by wurstsalat at 2021-09-02T23:26:05+02:00 Jingle File Transfer: Adapt to Contact changes - - - - - 13 changed files: - gajim/common/jingle_ft.py - gajim/common/jingle_session.py - gajim/common/modules/jingle.py - + gajim/data/gui/file_transfer_send.ui - − gajim/data/gui/filetransfers_send_file_dialog.ui - gajim/data/style/gajim.css - gajim/gtk/call_widget.py - gajim/gtk/controls/chat.py - + gajim/gtk/file_transfer_send.py - gajim/gtk/filetransfer.py - + gajim/gtk/resource_selector.py - gajim/gtk/tooltips.py - gajim/gui_interface.py Changes: ===================================== gajim/common/jingle_ft.py ===================================== @@ -101,13 +101,6 @@ def __init__(self, session, transport=None, file_props=None, self.session = session self.media = 'file' self.nominated_cand = {} - if app.contacts.is_gc_contact(session.connection.name, - session.peerjid): - roomjid = session.peerjid.split('/')[0] - dstaddr = hashlib.sha1(('%s%s%s' % (self.file_props.sid, - session.ourjid, roomjid)) - .encode('utf-8')).hexdigest() - self.file_props.dstaddr = dstaddr self.state = State.NOT_STARTED self.states = { State.INITIALIZED : StateInitialized(self), ===================================== gajim/common/jingle_session.py ===================================== @@ -516,7 +516,17 @@ def __on_session_initiate(self, stanza, jingle, error, action): """ if self.state != JingleStates.ENDED: raise OutOfOrder - self.initiator = jingle['initiator'] + + # In "session-initiate", the <jingle/> element SHOULD possess an + # 'initiator' attribute. If it differs from the JID 'from', jingle + # 'initiator' SHOULD be ignored. + if jingle['initiator'] is None: + self.initiator = stanza['from'] + elif jingle['initiator'] != stanza['from']: + self.initiator = stanza['from'] + else: + self.initiator = jingle['initiator'] + self.responder = self.ourjid self.peerjid = self.initiator self.accepted = False # user did not accept this session yet ===================================== gajim/common/modules/jingle.py ===================================== @@ -207,16 +207,7 @@ def start_audio_video(self, jid): def start_file_transfer(self, jid, file_props, request=False): logger.info("start file transfer with file: %s", file_props) - contact = app.contacts.get_contact_with_highest_priority( - self._account, app.get_jid_without_resource(jid)) - if app.contacts.is_gc_contact(self._account, jid): - gcc = jid.split('/') - if len(gcc) == 2: - contact = app.contacts.get_gc_contact(self._account, - gcc[0], - gcc[1]) - if contact is None: - return None + contact = self._con.get_module('Contacts').get_contact(jid) use_security = contact.supports(Namespace.JINGLE_XTLS) jingle = JingleSession(self._con, weinitiate=True, ===================================== gajim/data/gui/file_transfer_send.ui ===================================== @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface> + <requires lib="gtk+" version="3.24"/> + <object class="GtkStack" id="send_stack"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="transition-type">slide-left-right</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="border-width">18</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Files to send</property> + <property name="xalign">0</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hscrollbar-policy">never</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkListBox" id="listbox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="vexpand">True</property> + <property name="selection-mode">multiple</property> + <property name="activate-on-single-click">False</property> + <signal name="add" handler="_on_files_changed" swapped="no"/> + <signal name="remove" handler="_on_files_changed" swapped="no"/> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkToolbar"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="show-arrow">False</property> + <property name="icon_size">1</property> + <child> + <object class="GtkToolButton"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Add Files</property> + <property name="use-underline">True</property> + <property name="icon-name">list-add-symbolic</property> + <signal name="clicked" handler="_select_files" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <object class="GtkToolButton"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Remove Files</property> + <property name="use-underline">True</property> + <property name="icon-name">list-remove-symbolic</property> + <signal name="clicked" handler="_remove_files" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <style> + <class name="inline-toolbar"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Description</property> + <property name="xalign">0</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow"> + <property name="height-request">70</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hscrollbar-policy">never</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkTextView" id="description"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="wrap-mode">word-char</property> + <property name="left-margin">6</property> + <property name="right-margin">6</property> + <property name="top-margin">6</property> + <property name="bottom-margin">6</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkButton" id="files_send"> + <property name="label" translatable="yes">_Send Files</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="halign">center</property> + <property name="use-underline">True</property> + <signal name="clicked" handler="_on_send_clicked" swapped="no"/> + <style> + <class name="suggested-action"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> + </child> + </object> + <packing> + <property name="name">file_selection</property> + </packing> + </child> + <child> + <object class="GtkBox" id="resource_box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="border-width">18</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Choose Device</property> + <style> + <class name="large-header"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="resource_send"> + <property name="label" translatable="yes">_Send Files</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="halign">center</property> + <property name="use-underline">True</property> + <signal name="clicked" handler="_on_send_to_resource" swapped="no"/> + <style> + <class name="suggested-action"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack-type">end</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="resource_instructions"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="justify">center</property> + <property name="wrap">True</property> + <property name="max-width-chars">50</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="name">resource_selection</property> + <property name="position">1</property> + </packing> + </child> + </object> +</interface> ===================================== gajim/data/gui/filetransfers_send_file_dialog.ui deleted ===================================== @@ -1,175 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.36.0 --> -<interface> - <requires lib="gtk+" version="3.22"/> - <object class="GtkGrid" id="send_file_grid"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="border_width">18</property> - <property name="row_spacing">6</property> - <property name="column_spacing">12</property> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Description</property> - <property name="xalign">0</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">2</property> - <property name="width">2</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow"> - <property name="height_request">70</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">never</property> - <property name="shadow_type">in</property> - <child> - <object class="GtkTextView" id="description"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="wrap_mode">word-char</property> - <property name="left_margin">6</property> - <property name="right_margin">6</property> - <property name="top_margin">6</property> - <property name="bottom_margin">6</property> - </object> - </child> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">3</property> - <property name="width">2</property> - </packing> - </child> - <child> - <object class="GtkButton"> - <property name="label" translatable="yes">_Send Files</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="tooltip_text" translatable="yes">Send</property> - <property name="halign">center</property> - <property name="use_underline">True</property> - <signal name="clicked" handler="_send" swapped="no"/> - <style> - <class name="suggested-action"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">4</property> - <property name="width">2</property> - </packing> - </child> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Files to send</property> - <property name="xalign">0</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">0</property> - <property name="width">2</property> - </packing> - </child> - <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <property name="orientation">vertical</property> - <child> - <object class="GtkScrolledWindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">never</property> - <property name="shadow_type">in</property> - <child> - <object class="GtkViewport"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkListBox" id="listbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="vexpand">True</property> - <property name="selection_mode">multiple</property> - <property name="activate_on_single_click">False</property> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkToolbar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="show_arrow">False</property> - <property name="icon_size">1</property> - <child> - <object class="GtkToolButton"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="tooltip_text" translatable="yes">Add Files</property> - <property name="use_underline">True</property> - <property name="icon_name">list-add-symbolic</property> - <signal name="clicked" handler="_select_files" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="homogeneous">False</property> - </packing> - </child> - <child> - <object class="GtkToolButton"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="tooltip_text" translatable="yes">Remove Files</property> - <property name="use_underline">True</property> - <property name="icon_name">list-remove-symbolic</property> - <signal name="clicked" handler="_remove_files" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="homogeneous">True</property> - </packing> - </child> - <style> - <class name="inline-toolbar"/> - </style> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> - <property name="width">2</property> - </packing> - </child> - </object> -</interface> ===================================== gajim/data/style/gajim.css ===================================== @@ -426,6 +426,11 @@ .search-view-header label { color: @insensitive_fg_color; } +.resource-selector list row { + padding: 6px; + border-bottom: 1px solid @borders; +} + /* Profile window */ #NicknameEntry:disabled { font-size: 28px; ===================================== gajim/gtk/call_widget.py ===================================== @@ -184,6 +184,9 @@ def _on_jingle_request(self, event): content_types = [] for item in event.contents: content_types.append(item.media) + if not any(item in ('audio', 'video') for item in content_types): + return + if 'audio' in content_types: self._set_jingle_state( 'audio', ===================================== gajim/gtk/controls/chat.py ===================================== @@ -245,7 +245,9 @@ def update_actions(self): # Send file (Jingle) jingle_support = self.contact.supports(Namespace.JINGLE_FILE_TRANSFER_5) - jingle_conditions = jingle_support and self.contact.is_available + jingle_conditions = bool(jingle_support and + self.contact.is_available and + not self.contact.is_pm_contact) jingle = self._get_action('send-file-jingle-') jingle.set_enabled(online and jingle_conditions) ===================================== gajim/gtk/file_transfer_send.py ===================================== @@ -0,0 +1,134 @@ +# Copyright (C) 2003-2014 Yann Leboulanger <asterix AT
lagaule.org
> +# Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT
gmail.com
> +# Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT
gmail.com
> +# Copyright (C) 2006 Travis Shirk <travis AT
pobox.com
> +# +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see <
http://www.gnu.org/licenses/
>. + +from pathlib import Path + +from gi.repository import Gdk +from gi.repository import Gtk +from gi.repository import Pango + +from nbxmpp import Namespace + +from gajim.common import app +from gajim.common.i18n import _ + +from .filechoosers import FileChooserDialog +from .resource_selector import ResourceSelector +from .util import get_builder + + +class SendFileDialog(Gtk.ApplicationWindow): + def __init__(self, contact, send_callback, transient_for): + Gtk.ApplicationWindow.__init__(self) + self.set_application(app.app) + self.set_show_menubar(False) + self.set_resizable(True) + self.set_default_size(500, 350) + self.set_type_hint(Gdk.WindowTypeHint.DIALOG) + self.set_transient_for(transient_for) + self.set_title(_('Choose a File to Send…')) + self.set_destroy_with_parent(True) + + self._contact = contact + self._send_callback = send_callback + + self._ui = get_builder('file_transfer_send.ui') + self.add(self._ui.send_stack) + + self._ui.resource_instructions.set_text( + _('%s is online with multiple devices.\n' + 'Choose the device you would like to send the ' + 'file to.') % self._contact.name) + + self._resource_selector = ResourceSelector( + contact, + constraints=[Namespace.JINGLE_FILE_TRANSFER_5]) + self._resource_selector.connect( + 'selection-changed', self._on_resource_selection) + self._ui.resource_box.pack_start(self._resource_selector, 1, 0, 0) + + self.connect('key-press-event', self._key_press_event) + self._ui.connect_signals(self) + self.show_all() + + def _on_files_changed(self, _listbox, _row): + sensitive = bool(len(self._ui.listbox.get_children()) > 0) + self._ui.files_send.set_sensitive(sensitive) + + def _on_resource_selection(self, _selector, state): + self._ui.resource_send.set_sensitive(state) + + def _on_send_to_resource(self, _button): + resource_jid = self._resource_selector.get_jid() + self._send_files(resource_jid) + + def _on_send_clicked(self, _button): + count = len(self._contact.get_resources()) + if count == 0 or count > 1: + self._ui.send_stack.set_visible_child_name('resource_selection') + return + + resource_jid = self._contact.get_resources()[0].jid + self._send_files(resource_jid) + + def _send_files(self, resource_jid): + for file in self._ui.listbox.get_children(): + self._send_callback( + resource_jid, str(file.path), self._get_description()) + self.destroy() + + def _select_files(self, _button): + FileChooserDialog(self._set_files, + select_multiple=True, + transient_for=self, + path=app.settings.get('last_send_dir')) + + def _remove_files(self, _button): + selected = self._ui.listbox.get_selected_rows() + for item in selected: + self._ui.listbox.remove(item) + + def _set_files(self, file_names): + for file in file_names: + row = FileRow(file) + if row.path.is_dir(): + continue + last_dir = row.path.parent + self._ui.listbox.add(row) + self._ui.listbox.show_all() + app.settings.set('last_send_dir', str(last_dir)) + + def _get_description(self): + buffer_ = self._ui.description.get_buffer() + start, end = buffer_.get_bounds() + return buffer_.get_text(start, end, False) + + def _key_press_event(self, _widget, event): + if event.keyval == Gdk.KEY_Escape: + self.destroy() + + +class FileRow(Gtk.ListBoxRow): + def __init__(self, path): + Gtk.ListBoxRow.__init__(self) + self.path = Path(path) + label = Gtk.Label(label=self.path.name) + label.set_ellipsize(Pango.EllipsizeMode.END) + label.set_xalign(0) + self.add(label) ===================================== gajim/gtk/filetransfer.py ===================================== @@ -21,7 +21,6 @@ import time import logging from functools import partial -from pathlib import Path from enum import IntEnum, unique from datetime import datetime @@ -30,6 +29,8 @@ from gi.repository import GLib from gi.repository import Pango +from nbxmpp import JID + from gajim import gtkgui_helpers from gajim.common import app @@ -47,7 +48,7 @@ from .dialogs import InformationDialog from .dialogs import ErrorDialog from .filechoosers import FileSaveDialog -from .filechoosers import FileChooserDialog +from .file_transfer_send import SendFileDialog from .tooltips import FileTransfersTooltip from .util import get_builder @@ -228,10 +229,11 @@ def on_open(widget, file_props): sectext = _('File name: %s') % GLib.markup_escape_text(file_name) sectext += '\n' + _('Size: %s') % GLib.format_size_full( file_props.size, self.units) + client = app.get_client(file_props.tt_account) if file_props.type_ == 'r': - jid = file_props.sender.split('/')[0] - sender_name = app.contacts.get_first_contact_from_jid( - file_props.tt_account, jid).get_shown_name() + jid = JID.from_string(file_props.sender) + sender_name = client.get_module('Contacts').get_contact( + jid.bare).name sender = sender_name else: # You is a reply of who sent a file @@ -239,9 +241,9 @@ def on_open(widget, file_props): sectext += '\n' + _('Sender: %s') % sender sectext += '\n' + _('Recipient: ') if file_props.type_ == 's': - jid = file_props.receiver.split('/')[0] - receiver_name = app.contacts.get_first_contact_from_jid( - file_props.tt_account, jid).get_shown_name() + jid = JID.from_string(file_props.receiver) + receiver_name = client.get_module('Contacts').get_contact( + jid.bare).name recipient = receiver_name else: # You is a reply of who received a file @@ -250,7 +252,7 @@ def on_open(widget, file_props): if file_props.type_ == 'r': sectext += '\n' + _('Saved in: %s') % file_path - dialog = HigDialog(app.interface.roster.window, + dialog = HigDialog(app.window, Gtk.MessageType.INFO, Gtk.ButtonsType.NONE, _('File transfer completed'), @@ -298,17 +300,10 @@ def show_stopped(self, jid, file_props, error_msg=''): self._ui.transfers_list.get_selection().unselect_all() def show_hash_error(self, jid, file_props, account): - - def on_yes(dummy, fjid, file_props, account): + def _on_yes(): # Delete old file os.remove(file_props.file_name) - jid, resource = app.get_room_and_nick_from_fjid(fjid) - if resource: - contact = app.contacts.get_contact(account, jid, resource) - else: - contact = app.contacts.get_contact_with_highest_priority( - account, jid) - fjid = contact.get_full_jid() + # Request the file to the sender sid = helpers.get_random_string() new_file_props = FilesProp.getNewFileProp(account, sid) @@ -319,12 +314,15 @@ def on_yes(dummy, fjid, file_props, account): new_file_props.date = file_props.date new_file_props.hash_ = file_props.hash_ new_file_props.type_ = 'r' - tsid = app.connections[account].get_module( - 'Jingle').start_file_transfer(fjid, new_file_props, True) + tsid = client.get_module('Jingle').start_file_transfer( + jid, new_file_props, True) new_file_props.transport_sid = tsid self.add_transfer(account, contact, new_file_props) + client = app.get_client(account) + contact = client.get_module('Contacts').get_contact(jid) + if file_props.type_ == 'r': file_name = os.path.basename(file_props.file_name) else: @@ -339,16 +337,14 @@ def on_yes(dummy, fjid, file_props, account): text=_('_No')), DialogButton.make('Accept', text=_('_Download Again'), - callback=on_yes, - args=[jid, - file_props, - account])]).show() + callback=_on_yes)]).show() def show_file_send_request(self, account, contact): send_callback = partial(self.send_file, account, contact) - SendFileDialog(send_callback, self.window) + SendFileDialog(contact, send_callback, self.window) - def send_file(self, account, contact, file_path, file_desc=''): + def send_file(self, account, contact, resource_jid, file_path, + file_desc=''): """ Start the real transfer(upload) of the file """ @@ -358,20 +354,15 @@ def send_file(self, account, contact, file_path, file_desc=''): ErrorDialog(pritext, sextext) return - if isinstance(contact, str): - if contact.find('/') == -1: - return - (jid, resource) = contact.split('/', 1) - contact = app.contacts.create_contact(jid=jid, account=account, - resource=resource) file_name = os.path.split(file_path)[1] file_props = self.get_send_file_props(account, contact, file_path, file_name, file_desc) if file_props is None: return False - app.connections[account].get_module('Jingle').start_file_transfer( - contact.get_full_jid(), file_props) + client = app.get_client(account) + client.get_module('Jingle').start_file_transfer( + str(resource_jid), file_props) self.add_transfer(account, contact, file_props) return True @@ -383,8 +374,8 @@ def _start_receive(self, file_path, account, contact, file_props): file_props.file_name = file_path file_props.type_ = 'r' self.add_transfer(account, contact, file_props) - app.connections[account].get_module('Bytestream').send_file_approval( - file_props) + client = app.get_client(account) + client.get_module('Bytestream').send_file_approval(file_props) def on_file_request_accepted(self, account, contact, file_props): def _on_accepted(account, contact, file_props, file_path): @@ -417,7 +408,7 @@ def _on_replace(): file_path, account, contact, file_props) def _on_cancel(): - con.get_module('Bytestream').send_file_rejection( + client.get_module('Bytestream').send_file_rejection( file_props) ConfirmationDialog( @@ -447,10 +438,10 @@ def _on_cancel(): self._start_receive(file_path, account, contact, file_props) # Show file save as dialog - con = app.connections[account] + client = app.get_client(account) accept_cb = partial(_on_accepted, account, contact, file_props) - cancel_cb = partial(con.get_module('Bytestream').send_file_rejection, - file_props) + cancel_cb = partial( + client.get_module('Bytestream').send_file_rejection, file_props) FileSaveDialog(accept_cb, cancel_cb, path=app.settings.get('last_save_dir'), @@ -478,12 +469,12 @@ def _on_ok(): self.on_file_request_accepted(account, contact, file_props) def _on_cancel(): - app.connections[account].get_module( + app.get_client(account).get_module( 'Bytestream').send_file_rejection(file_props) ConfirmationDialog( _('File Transfer Request'), - _('%s wants to send you a file') % contact.get_shown_name(), + _('%s wants to send you a file') % contact.name, sectext, [DialogButton.make('Cancel', callback=_on_cancel), @@ -518,13 +509,13 @@ def set_status(self, file_props, status): text += received_size + '/' + full_size self.model.set(iter_, Column.PROGRESS, text) - def pulse(): - p = self.model.get(iter_, Column.PULSE)[0] - if p == GLib.MAXINT32: + def _pulse(): + pulse_value = self.model.get(iter_, Column.PULSE)[0] + if pulse_value == GLib.MAXINT32: return False - self.model.set(iter_, Column.PULSE, p + 1) + self.model.set(iter_, Column.PULSE, pulse_value + 1) return True - GLib.timeout_add(100, pulse) + GLib.timeout_add(100, _pulse) elif status == 'hash_error': text = _('File error') + '\n' received_size = GLib.format_size_full( @@ -569,6 +560,9 @@ def _get_eta_and_speed(self, full_size, transfered_size, file_props): if not file_props.transfered_size: return 0., 0. + if file_props.elapsed_time == 0: + return 0., 0. + if len(file_props.transfered_size) == 1: speed = round(float(transfered_size) / file_props.elapsed_time) else: @@ -595,7 +589,7 @@ def _remove_transfer(self, iter_, sid, file_props): account = file_props.tt_account if account in app.connections: # there is a connection to the account - app.connections[account].get_module( + app.get_client(account).get_module( 'Bytestream').remove_transfer(file_props) if file_props.type_ == 'r': # we receive a file other = file_props.sender @@ -611,8 +605,7 @@ def _remove_transfer(self, iter_, sid, file_props): for event in app.events.get_events(account, jid, [ev_type]): if event.file_props.sid == file_props.sid: app.events.remove_events(account, jid, event) - app.interface.roster.draw_contact(jid, account) - app.interface.roster.show_title() + FilesProp.deleteFileProp(file_props) del file_props @@ -678,8 +671,9 @@ def set_progress(self, typ, sid, transfered_size, iter_=None): self.model.set(iter_, 0, self.icons[status]) if transfered_size == full_size: # If we are receiver and this is a jingle session - if file_props.type_ == 'r' and \ - file_props.session_type == 'jingle' and file_props.hash_: + if (file_props.type_ == 'r' and + file_props.session_type == 'jingle' and + file_props.hash_): # Show that we are computing the hash self.set_status(file_props, 'computing') else: @@ -753,7 +747,7 @@ def add_transfer(self, account, contact, file_props): else: file_name = file_props.name text_props = GLib.markup_escape_text(file_name) + '\n' - text_props += contact.get_shown_name() + text_props += contact.name self.model.set(iter_, 1, text_labels, @@ -916,11 +910,11 @@ def _on_cancel_button_clicked(self, widget): account = file_props.tt_account if account not in app.connections: return - con = app.connections[account] + client = app.get_client(account) # Check if we are in a IBB transfer if file_props.direction: - con.get_module('IBB').send_close(file_props) - con.get_module('Bytestream').disconnect_transfer(file_props) + client.get_module('IBB').send_close(file_props) + client.get_module('Bytestream').disconnect_transfer(file_props) self.set_status(file_props, 'stop') def _on_notify_ft_complete_toggled(self, widget, *args): @@ -1028,70 +1022,3 @@ def _on_remove_menuitem_activate(self, widget): def _on_file_transfers_window_key_press_event(self, widget, event): if event.keyval == Gdk.KEY_Escape: # ESCAPE self.window.hide() - - -class SendFileDialog(Gtk.ApplicationWindow): - def __init__(self, send_callback, transient_for): - Gtk.ApplicationWindow.__init__(self) - self.set_name('SendFileDialog') - self.set_application(app.app) - self.set_show_menubar(False) - self.set_resizable(True) - self.set_default_size(500, 350) - self.set_type_hint(Gdk.WindowTypeHint.DIALOG) - self.set_transient_for(transient_for) - self.set_title(_('Choose a File to Send…')) - self.set_destroy_with_parent(True) - - self._send_callback = send_callback - - self._ui = get_builder('filetransfers_send_file_dialog.ui') - self.add(self._ui.send_file_grid) - self.connect('key-press-event', self._key_press_event) - self._ui.connect_signals(self) - self.show_all() - - def _send(self, button): - for file in self._ui.listbox.get_children(): - self._send_callback(str(file.path), self._get_description()) - self.destroy() - - def _select_files(self, button): - FileChooserDialog(self._set_files, - select_multiple=True, - transient_for=self, - path=app.settings.get('last_send_dir')) - - def _remove_files(self, button): - selected = self._ui.listbox.get_selected_rows() - for item in selected: - self._ui.listbox.remove(item) - - def _set_files(self, filenames): - for file in filenames: - row = FileRow(file) - if row.path.is_dir(): - continue - last_dir = row.path.parent - self._ui.listbox.add(row) - self._ui.listbox.show_all() - app.settings.set('last_send_dir', str(last_dir)) - - def _get_description(self): - buffer_ = self._ui.description.get_buffer() - start, end = buffer_.get_bounds() - return buffer_.get_text(start, end, False) - - def _key_press_event(self, widget, event): - if event.keyval == Gdk.KEY_Escape: - self.destroy() - - -class FileRow(Gtk.ListBoxRow): - def __init__(self, path): - Gtk.ListBoxRow.__init__(self) - self.path = Path(path) - label = Gtk.Label(label=self.path.name) - label.set_ellipsize(Pango.EllipsizeMode.END) - label.set_xalign(0) - self.add(label) ===================================== gajim/gtk/resource_selector.py ===================================== @@ -0,0 +1,138 @@ +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see <
http://www.gnu.org/licenses/
>. + +import locale +import logging + +from gi.repository import GObject +from gi.repository import Gtk + +from gajim.common import app +from gajim.common.i18n import _ + +log = logging.getLogger('gajim.gui.resource_selector') + + +class ResourceSelector(Gtk.ScrolledWindow): + + __gsignals__ = { + 'selection-changed': ( + GObject.SignalFlags.RUN_LAST, + None, + (bool, )), + } + + def __init__(self, contact, constraints=None): + Gtk.ScrolledWindow.__init__(self) + self.set_shadow_type(Gtk.ShadowType.IN) + self.set_size_request(-1, 200) + self.get_style_context().add_class('resource-selector') + + self._listbox = Gtk.ListBox() + self._listbox.set_sort_func(self._sort_func) + self._listbox.connect('row-selected', self._on_row_selected) + self.add(self._listbox) + + self._contact = contact + self._contact.connect('presence-update', self._on_update) + self._contact.connect('caps-update', self._on_update) + + # Constraints include Namespaces a resource has to support + self._constraints = constraints or [] + + self._set_placeholder() + self._add_entries() + + self.show_all() + + @staticmethod + def _sort_func(row1, row2): + return locale.strcoll( + row1.device_text.lower(), row2.device_text.lower()) + + def _on_row_selected(self, _listbox, row): + state = bool(row is not None) + self.emit('selection-changed', state) + + def _set_placeholder(self): + image = Gtk.Image.new_from_icon_name( + 'dialog-warning-symbolic', Gtk.IconSize.DND) + label = Gtk.Label(label=_('No devices online')) + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + box.set_valign(Gtk.Align.CENTER) + box.get_style_context().add_class('dim-label') + box.add(image) + box.add(label) + box.show_all() + self._listbox.set_placeholder(box) + + def _add_entries(self): + for resource in self._contact.iter_resources(): + self._listbox.add(ResourceRow(resource, self._constraints)) + + def _on_update(self, _contact, _signal_name): + for child in self._listbox.get_children(): + self._listbox.remove(child) + self._add_entries() + + def get_jid(self): + return self._listbox.get_selected_row().jid + + +class ResourceRow(Gtk.ListBoxRow): + def __init__(self, resource_contact, constraints): + Gtk.ListBoxRow.__init__(self) + + self.jid = resource_contact.jid + + icon_name = 'computer-symbolic' + tooltip_text = _('Computer') + self.device_text = resource_contact.jid.resource + + disco_info = app.storage.cache.get_last_disco_info( + resource_contact.jid) + if disco_info is not None: + name, type_ = self._get_client_identity(disco_info) + if name is not None: + self.device_text = f'{name} ({resource_contact.jid.resource})' + if type_ is not None: + if type_ == 'phone': + icon_name = 'phone-symbolic' + tooltip_text = _('Phone') + + image = Gtk.Image() + image.set_from_icon_name(icon_name, Gtk.IconSize.DND) + image.set_tooltip_text(tooltip_text) + + name_label = Gtk.Label() + name_label.set_text(self.device_text) + + box = Gtk.Box(spacing=12) + box.add(image) + box.add(name_label) + + for constraint in constraints: + if not resource_contact.supports(constraint): + self.set_sensitive(False) + self.set_tooltip_text(_('This devices is not compatible.')) + + self.add(box) + self.show_all() + + @staticmethod + def _get_client_identity(disco_info): + for identity in disco_info.identities: + if identity.category == 'client': + return identity.name, identity.type + return None, None ===================================== gajim/gtk/tooltips.py ===================================== @@ -33,6 +33,8 @@ from gi.repository import GLib from gi.repository import Pango +from nbxmpp import JID + from gajim.common import app from gajim.common import helpers from gajim.common.const import AvatarSize @@ -304,20 +306,19 @@ def _create_tooltip(self, file_props, _sid): file_name = file_props.name properties.append((_('File Name: '), GLib.markup_escape_text(file_name))) + + client = app.get_client(file_props.tt_account) if file_props.type_ == 'r': type_ = Q_('?Noun:Download') actor = _('Sender: ') - sender = file_props.sender.split('/')[0] - name = app.contacts.get_first_contact_from_jid( - file_props.tt_account, sender).get_shown_name() + sender = JID.from_string(file_props.sender) + name = client.get_module('Contacts').get_contact(sender.bare).name else: type_ = Q_('?Noun:Upload') actor = _('Recipient: ') - receiver = file_props.receiver - if hasattr(receiver, 'name'): - name = receiver.get_shown_name() - else: - name = receiver.split('/')[0] + receiver = JID.from_string(file_props.receiver) + name = client.get_module('Contacts').get_contact( + receiver.bare).name properties.append((Q_('?transfer type:Type: '), type_)) properties.append((actor, GLib.markup_escape_text(name))) ===================================== gajim/gui_interface.py ===================================== @@ -757,14 +757,10 @@ def handle_event_file_request_error(self, event): text=event.file_props.name) def handle_event_file_request(self, event): - # TODO account = event.conn.name - if event.jid not in app.contacts.get_jid_list(account): - contact = app.contacts.create_not_in_roster_contact( - jid=event.jid, account=account) - app.contacts.add_contact(account, contact) - self.roster.add_contact(event.jid, account) - contact = app.contacts.get_first_contact_from_jid(account, event.jid) + client = app.get_client(account) + contact = client.get_module('Contacts').get_contact(event.jid) + if event.file_props.session_type == 'jingle': request = \ event.stanza.getTag('jingle').getTag('content').getTag( @@ -781,8 +777,7 @@ def handle_event_file_request(self, event): file_event = events.FileRequestEvent(event.file_props) self.add_event(account, event.jid, file_event) if helpers.allow_showing_notification(account): - txt = _('%s wants to send you a file.') % app.get_name_from_jid( - account, event.jid) + txt = _('%s wants to send you a file.') % contact.name event_type = _('File Transfer Request') app.notification.popup( event_type, @@ -823,11 +818,11 @@ def handle_event_file_rcv_completed(self, account, file_props): if file_props.error == 0: file_transfers.set_progress( file_props.type_, file_props.sid, file_props.received_len) - jid = app.get_jid_without_resource(str(file_props.receiver)) + jid = JID.from_string(file_props.receiver) app.nec.push_incoming_event( NetworkEvent('file-transfer-completed', file_props=file_props, - jid=jid)) + jid=jid.bare)) else: file_transfers.set_status(file_props, 'stop') @@ -947,56 +942,55 @@ def _popup_ft_result(self, account, jid, file_props): self.add_event(account, jid, event) if file_props is not None: + client = app.get_client(account) + if file_props.type_ == 'r': - # Get the name of the sender, as it is in the roster - sender = file_props.sender.split('/')[0] - name = app.contacts.get_first_contact_from_jid( - account, sender).get_shown_name() + jid = JID.from_string(file_props.sender) + contact = client.get_module('contacts').get_contact(jid.bare) filename = os.path.basename(file_props.file_name) if event_type == _('File Transfer Completed'): txt = _('%(filename)s received from %(name)s.') % { 'filename': filename, - 'name': name} + 'name': contact.name} icon_name = 'emblem-default' elif event_type == _('File Transfer Stopped'): txt = _('File transfer of %(filename)s from %(name)s ' 'stopped.') % { 'filename': filename, - 'name': name} + 'name': contact.name} icon_name = 'process-stop' else: # File transfer hash error txt = _('File transfer of %(filename)s from %(name)s ' 'failed.') % { 'filename': filename, - 'name': name} + 'name': contact.name} icon_name = 'process-stop' else: receiver = file_props.receiver if hasattr(receiver, 'jid'): receiver = receiver.jid - receiver = receiver.split('/')[0] - # Get the name of the contact, as it is in the roster - name = app.contacts.get_first_contact_from_jid( - account, receiver).get_shown_name() + + jid = JID.from_string(receiver) + contact = client.get_module('contacts').get_contact(jid.bare) filename = os.path.basename(file_props.file_name) if event_type == _('File Transfer Completed'): txt = _('You successfully sent %(filename)s to ' '%(name)s.') % { 'filename': filename, - 'name': name} + 'name': contact.name} icon_name = 'emblem-default' elif event_type == _('File Transfer Stopped'): txt = _('File transfer of %(filename)s to %(name)s ' 'stopped.') % { 'filename': filename, - 'name': name} + 'name': contact.name} icon_name = 'process-stop' else: # File transfer hash error txt = _('File transfer of %(filename)s to %(name)s ' 'failed.') % { 'filename': filename, - 'name': name} + 'name': contact.name} icon_name = 'process-stop' else: txt = '' View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/20f38c5069a7878340edc4964c4508c…
-- View it on GitLab:
https://dev.gajim.org/gajim/gajim/-/compare/20f38c5069a7878340edc4964c4508c…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
← Newer
1
2
3
4
5
Older →
Jump to page:
1
2
3
4
5
Results per page:
10
25
50
100
200