diff --git a/gajim/data/gui/filetransfers.ui b/gajim/data/gui/filetransfers.ui index bde4547ce..2fe11bf4d 100644 --- a/gajim/data/gui/filetransfers.ui +++ b/gajim/data/gui/filetransfers.ui @@ -1,5 +1,5 @@ - + @@ -101,8 +101,6 @@ - - @@ -247,6 +245,9 @@ + + + File Transfers diff --git a/gajim/filetransfers_window.py b/gajim/filetransfers_window.py index d78617047..2935eaac2 100644 --- a/gajim/filetransfers_window.py +++ b/gajim/filetransfers_window.py @@ -140,7 +140,12 @@ class FileTransfersWindow: self.tree.get_selection().set_mode(Gtk.SelectionMode.SINGLE) self.tree.get_selection().connect('changed', self.selection_changed) + + # Tooltip + self.tree.connect('query-tooltip', self._query_tooltip) + self.tree.set_has_tooltip(True) self.tooltip = tooltips.FileTransfersTooltip() + self.file_transfers_menu = self.xml.get_object('file_transfers_menu') self.open_folder_menuitem = self.xml.get_object('open_folder_menuitem') self.cancel_menuitem = self.xml.get_object('cancel_menuitem') @@ -149,6 +154,33 @@ class FileTransfersWindow: self.remove_menuitem = self.xml.get_object('remove_menuitem') self.xml.connect_signals(self) + def _query_tooltip(self, widget, x_pos, y_pos, keyboard_mode, tooltip): + try: + x_pos, y_pos = widget.convert_widget_to_bin_window_coords( + x_pos, y_pos) + row = widget.get_path_at_pos(x_pos, y_pos)[0] + except TypeError: + self.tooltip.clear_tooltip() + return False + if not row: + self.tooltip.clear_tooltip() + return False + + iter_ = None + try: + model = widget.get_model() + iter_ = model.get_iter(row) + except Exception: + self.tooltip.clear_tooltip() + return False + + sid = self.model[iter_][Column.SID] + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) + + value, widget = self.tooltip.get_tooltip(file_props, sid) + tooltip.set_custom(widget) + return value + def find_transfer_by_jid(self, account, jid): """ Find all transfers with peer 'jid' that belong to 'account' @@ -301,9 +333,7 @@ class FileTransfersWindow: def on_ok(widget): file_dir = None files_path_list = dialog.get_filenames() - text_buffer = desc_entry.get_buffer() - desc = text_buffer.get_text(text_buffer.get_start_iter(), - text_buffer.get_end_iter(), True) + desc = desc_entry.get_text() for file_path in files_path_list: if self.send_file(account, contact, file_path, desc) \ and file_dir is None: @@ -721,7 +751,6 @@ class FileTransfersWindow: """ Add new transfer to FT window and show the FT window """ - self.on_transfers_list_leave_notify_event(None) if file_props is None: return file_props.elapsed_time = 0 @@ -752,45 +781,6 @@ class FileTransfersWindow: self.set_cleanup_sensitivity() self.window.show_all() - def on_transfers_list_motion_notify_event(self, widget, event): - w = self.tree.get_window() - device = w.get_display().get_device_manager().get_client_pointer() - pointer = w.get_device_position(device) - props = widget.get_path_at_pos(int(event.x), int(event.y)) - self.height_diff = pointer[2] - int(event.y) - if self.tooltip.timeout > 0 or self.tooltip.shown: - if not props or self.tooltip.id != props[0]: - self.tooltip.hide_tooltip() - if props: - row = props[0] - iter_ = None - try: - iter_ = self.model.get_iter(row) - except Exception: - self.tooltip.hide_tooltip() - return - sid = self.model[iter_][Column.SID] - file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) - if file_props is not None: - if self.tooltip.timeout == 0 or self.tooltip.id != props[0]: - self.tooltip.id = row - self.tooltip.timeout = GLib.timeout_add(500, - self.show_tooltip, widget) - - def on_transfers_list_leave_notify_event(self, widget=None, event=None): - if event is not None: - self.height_diff = int(event.y) - elif self.height_diff is 0: - return - w = self.tree.get_window() - device = w.get_display().get_device_manager().get_client_pointer() - pointer = w.get_device_position(device) - props = self.tree.get_path_at_pos(pointer[1], - pointer[2] - self.height_diff) - if self.tooltip.timeout > 0 or self.tooltip.shown: - if not props or self.tooltip.id == props[0]: - self.tooltip.hide_tooltip() - def on_transfers_list_row_activated(self, widget, path, col): # try to open the containing folder self.on_open_folder_menuitem_activate(widget) @@ -954,37 +944,11 @@ class FileTransfersWindow: con.disconnect_transfer(file_props) self.set_status(file_props, 'stop') - def show_tooltip(self, widget): - self.tooltip.timeout = 0 - if self.height_diff == 0: - self.tooltip.hide_tooltip() - return - w = self.tree.get_window() - device = w.get_display().get_device_manager().get_client_pointer() - pointer = w.get_device_position(device) - props = self.tree.get_path_at_pos(pointer[1], - pointer[2] - self.height_diff) - # check if the current pointer is at the same path - # as it was before setting the timeout - if props and self.tooltip.id == props[0]: - iter_ = self.model.get_iter(props[0]) - sid = self.model[iter_][Column.SID] - file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) - # bounding rectangle of coordinates for the cell within the treeview - rect = self.tree.get_cell_area(props[0], props[1]) - # position of the treeview on the screen - position = widget.get_window().get_origin()[1:] - self.tooltip.show_tooltip(file_props, rect.height, - position[1] + rect.y + self.height_diff) - else: - self.tooltip.hide_tooltip() - def on_notify_ft_complete_checkbox_toggled(self, widget): app.config.set('notify_on_file_complete', widget.get_active()) def on_file_transfers_dialog_delete_event(self, widget, event): - self.on_transfers_list_leave_notify_event(widget, None) self.window.hide() return True # do NOT destory window @@ -1006,7 +970,6 @@ class FileTransfersWindow: """ When a key is pressed in the treeviews """ - self.tooltip.hide_tooltip() iter_ = None try: iter_ = self.tree.get_selection().get_selected()[1] @@ -1024,7 +987,6 @@ class FileTransfersWindow: def on_transfers_list_button_release_event(self, widget, event): # hide tooltip, no matter the button is pressed - self.tooltip.hide_tooltip() path = None try: path = self.tree.get_path_at_pos(int(event.x), int(event.y))[0] @@ -1037,7 +999,6 @@ class FileTransfersWindow: def on_transfers_list_button_press_event(self, widget, event): # hide tooltip, no matter the button is pressed - self.tooltip.hide_tooltip() path, iter_ = None, None try: path = self.tree.get_path_at_pos(int(event.x), int(event.y))[0] diff --git a/gajim/groupchat_control.py b/gajim/groupchat_control.py index 64e31ae02..cabc21ace 100644 --- a/gajim/groupchat_control.py +++ b/gajim/groupchat_control.py @@ -303,7 +303,6 @@ class GroupchatControl(ChatControlBase): formattings_button = self.xml.get_object('formattings_button') formattings_button.set_sensitive(False) - self.current_tooltip = None if parent_win is not None: # On AutoJoin with minimize Groupchats are created without parent # Tooltip Window and Actions have to be created with parent @@ -474,6 +473,9 @@ class GroupchatControl(ChatControlBase): self.banner_actionbar.pack_end(self.hide_roster_button) self.banner_actionbar.pack_start(self.subject_button) + # GC Roster tooltip + self.gc_tooltip = tooltips.GCTooltip() + self.control_menu = gui_menu_builder.get_groupchat_menu(self.control_id) app.ged.register_event_handler('gc-presence-received', ged.GUI1, @@ -714,8 +716,6 @@ class GroupchatControl(ChatControlBase): if widget.get_tooltip_window(): return widget.set_has_tooltip(True) - widget.set_tooltip_window(tooltips.GCTooltip( - self.account, self.parent_win.window)) id_ = widget.connect('query-tooltip', self.query_tooltip) self.handlers[id_] = widget @@ -723,41 +723,35 @@ class GroupchatControl(ChatControlBase): try: row = self.list_treeview.get_path_at_pos(x_pos, y_pos)[0] except TypeError: + self.gc_tooltip.clear_tooltip() return False if not row: + self.gc_tooltip.clear_tooltip() return False iter_ = None try: iter_ = self.model.get_iter(row) except Exception: + self.gc_tooltip.clear_tooltip() return False typ = self.model[iter_][Column.TYPE] nick = self.model[iter_][Column.NICK] if typ != 'contact': + self.gc_tooltip.clear_tooltip() return False - if self.current_tooltip != row: - # If the row changes we hide the current tooltip - self.current_tooltip = row - return False - - tooltip = widget.get_tooltip_window() - - if tooltip.row == row: - # We already populated the window with the row data - return True - tooltip.row = row - contact = app.contacts.get_gc_contact( self.account, self.room_jid, nick) if not contact: + self.gc_tooltip.clear_tooltip() return False - tooltip.populate(contact) - return True + value, widget = self.gc_tooltip.get_tooltip(contact) + tooltip.set_custom(widget) + return value def fill_column(self, col): for rend in self.renderers_list: diff --git a/gajim/htmltextview.py b/gajim/htmltextview.py index d05d9a4a9..300c7dc91 100644 --- a/gajim/htmltextview.py +++ b/gajim/htmltextview.py @@ -1102,8 +1102,6 @@ if __name__ == '__main__': htmlview = ConversationTextview(None) - tooltip = tooltips.BaseTooltip() - def on_textview_motion_notify_event(widget, event): """ Change the cursor to a hand when we are over a mail or an url @@ -1129,17 +1127,6 @@ if __name__ == '__main__': except Exception: pass - #if line_tooltip.timeout != 0: - # Check if we should hide the line tooltip - # if not over_line: - # line_tooltip.hide_tooltip() - #if over_line and not line_tooltip.win: - # line_tooltip.timeout = GLib.timeout_add(500, - # show_line_tooltip) - # htmlview.tv.get_window(Gtk.TextWindowType.TEXT).set_cursor( - # gtkgui_helpers.get_cursor('LEFT_PTR')) - # change_cursor = tag - htmlview.tv.connect('motion_notify_event', on_textview_motion_notify_event) def handler(texttag, widget, event, iter_, kind): diff --git a/gajim/message_textview.py b/gajim/message_textview.py index f76fdc603..57b938a08 100644 --- a/gajim/message_textview.py +++ b/gajim/message_textview.py @@ -102,6 +102,15 @@ class MessageTextView(Gtk.TextView): text = buf.get_text(start, end, True) return text != self.PLACEHOLDER and text != '' + def get_text(self): + # gets the text if its not PLACEHOLDER + buf = self.get_buffer() + start, end = buf.get_bounds() + text = self.get_buffer().get_text(start, end, True) + if text == self.PLACEHOLDER: + return '' + return text + def is_placeholder(self): buf = self.get_buffer() start, end = buf.get_bounds() diff --git a/gajim/statusicon.py b/gajim/statusicon.py index a321c1f3f..86edee6f0 100644 --- a/gajim/statusicon.py +++ b/gajim/statusicon.py @@ -104,8 +104,7 @@ class StatusIcon: self.make_menu(event_button, event_time) def on_status_icon_query_tooltip(self, widget, x, y, keyboard_mode, tooltip): - self.tooltip.populate() - tooltip.set_custom(self.tooltip.hbox) + tooltip.set_custom(self.tooltip.get_tooltip()) return True def hide_icon(self): diff --git a/gajim/tooltips.py b/gajim/tooltips.py index bd8482d34..ed0823c8c 100644 --- a/gajim/tooltips.py +++ b/gajim/tooltips.py @@ -28,14 +28,14 @@ ## along with Gajim. If not, see . ## +import os +import time +from datetime import datetime + from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GLib from gi.repository import Pango -import os -import time -from datetime import datetime -from datetime import timedelta from gajim import gtkgui_helpers from gajim.common.const import AvatarSize @@ -43,147 +43,6 @@ from gajim.common import app from gajim.common import helpers from gajim.common.i18n import Q_ -class BaseTooltip: - """ - Base Tooltip class - - Usage: - tooltip = BaseTooltip() - .... - tooltip.show_tooltip(data, widget_height, widget_y_position) - .... - if tooltip.timeout != 0: - tooltip.hide_tooltip() - - * data - the text to be displayed (extenders override this argument and - display more complex contents) - * widget_height - the height of the widget on which we want to show tooltip - * widget_y_position - the vertical position of the widget on the screen - - Tooltip is displayed aligned centered to the mouse poiner and 4px below the widget. - In case tooltip goes below the visible area it is shown above the widget. - """ - - def __init__(self): - self.timeout = 0 - self.preferred_position = [0, 0] - self.win = None - self.id = None - self.cur_data = None - self.shown = False - self.position_computed = False - - def populate(self, data): - """ - This method must be overriden by all extenders. This is the most simple - implementation: show data as value of a label - """ - self.create_window() - self.win.add(Gtk.Label(label=data)) - - def create_window(self): - """ - Create a popup window each time tooltip is requested - """ - self.win = Gtk.Window.new(Gtk.WindowType.POPUP) - self.win.set_title('tooltip') - self.win.set_border_width(3) - self.win.set_resizable(False) - self.win.set_name('gtk-tooltips') - self.win.set_type_hint(Gdk.WindowTypeHint.TOOLTIP) - - self.win.set_events(Gdk.EventMask.POINTER_MOTION_MASK) - self.win.connect('size-allocate', self.on_size_allocate) - self.win.connect('motion-notify-event', self.motion_notify_event) - self.screen = self.win.get_screen() - - def _get_icon_name_for_tooltip(self, contact): - """ - Helper function used for tooltip contacts/acounts - - Tooltip on account has fake contact with sub == '', in this case we show - real status of the account - """ - if contact.ask == 'subscribe': - return 'requested' - elif contact.sub in ('both', 'to', ''): - return contact.show - return 'not in roster' - - def motion_notify_event(self, widget, event): - GLib.idle_add(self.hide_tooltip) - - def on_size_allocate(self, widget, rect): - if not self.position_computed: - half_width = rect.width / 2 + 1 - if self.preferred_position[1] + rect.height > \ - self.screen.get_height(): - # flip tooltip up - self.preferred_position[1] -= rect.height + self.widget_height \ - + 8 - if self.preferred_position[1] < 0: - self.preferred_position[1] = self.screen.get_height() - \ - rect.height - 2 - - if self.preferred_position[0] + rect.width + 7 < \ - self.screen.get_width(): - self.preferred_position[0] = self.preferred_position[0]\ - + 7 - else: - self.preferred_position[0] = self.preferred_position[0]\ - - rect.width - 7 - self.win.move(self.preferred_position[0], - self.preferred_position[1]) - return - if self.preferred_position[0] < half_width: - self.preferred_position[0] = 0 - elif self.preferred_position[0] + rect.width > \ - self.screen.get_width() + half_width: - self.preferred_position[0] = self.screen.get_width() - \ - rect.width - else: - self.preferred_position[0] -= half_width - self.position_computed = True - self.win.move(self.preferred_position[0], self.preferred_position[1]) - - def show_tooltip(self, data, widget_height, widget_y_position): - """ - Show tooltip on widget - - Data contains needed data for tooltip contents. - widget_height is the height of the widget on which we show the tooltip. - widget_y_position is vertical position of the widget on the screen. - """ - if self.shown: - return - self.position_computed = False - self.cur_data = data - # set tooltip contents - self.populate(data) - - # get the X position of mouse pointer on the screen - pointer_x = self.screen.get_display().get_device_manager().\ - get_client_pointer().get_position()[1] - - # get the prefered X position of the tooltip on the screen in case this position is > - # than the height of the screen, tooltip will be shown above the widget - preferred_y = widget_y_position + widget_height + 4 - - self.preferred_position = [pointer_x, preferred_y] - self.widget_height = widget_height - self.win.show_all() - self.shown = True - - def hide_tooltip(self): - if self.timeout > 0: - GLib.source_remove(self.timeout) - self.timeout = 0 - if self.win: - self.win.destroy() - self.win = None - self.id = None - self.cur_data = None - self.shown = False class StatusTable: """ @@ -263,13 +122,13 @@ class StatusTable: self.table.attach(lock_image, 4, self.current_row, 1, 1) self.current_row += 1 -class NotificationAreaTooltip(BaseTooltip, StatusTable): + +class NotificationAreaTooltip(StatusTable): """ Tooltip that is shown in the notification area """ def __init__(self): - BaseTooltip.__init__(self) StatusTable.__init__(self) def fill_table_with_accounts(self, accounts): @@ -297,8 +156,7 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable): for line in acct['event_lines']: self.add_text_row(' ' + line, 1) - def populate(self, data=''): - self.create_window() + def get_tooltip(self): self.create_table() accounts = helpers.get_notification_icon_tooltip_dict() @@ -308,40 +166,43 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable): self.hbox.add(self.table) self.hbox.show_all() + return self.hbox -class GCTooltip(Gtk.Window): + +class GCTooltip(): # pylint: disable=E1101 - def __init__(self, account, parent): - Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP, transient_for=parent) - self.account = account - self.row = None - self.set_title('tooltip') - self.set_border_width(3) - self.set_resizable(False) - self.set_name('gtk-tooltips') - self.set_type_hint(Gdk.WindowTypeHint.TOOLTIP) + def __init__(self): + self.contact = None self.xml = gtkgui_helpers.get_gtk_builder('tooltip_gc_contact.ui') for name in ('nick', 'status', 'jid', 'user_show', 'fillelement', - 'resource', 'affiliation', 'avatar', 'resource_label', - 'jid_label', 'tooltip_grid'): + 'resource', 'affiliation', 'avatar', 'resource_label', + 'jid_label', 'tooltip_grid'): setattr(self, name, self.xml.get_object(name)) - self.add(self.tooltip_grid) - self.tooltip_grid.show() - def clear_tooltip(self): + self.contact = None + + def get_tooltip(self, contact): + if self.contact == contact: + return True, self.tooltip_grid + + self._populate_grid(contact) + self.contact = contact + return False, self.tooltip_grid + + def _hide_grid_childs(self): """ Hide all Elements of the Tooltip Grid """ for child in self.tooltip_grid.get_children(): child.hide() - def populate(self, contact): + def _populate_grid(self, contact): """ Populate the Tooltip Grid with data of from the contact """ - self.clear_tooltip() + self._hide_grid_childs() self.nick.set_text(contact.get_shown_name()) self.nick.show() @@ -750,19 +611,28 @@ class RosterTooltip(Gtk.Window, StatusTable): return 'not in roster' -class FileTransfersTooltip(BaseTooltip): - """ - Tooltip that is shown in the notification area - """ - +class FileTransfersTooltip(): def __init__(self): - BaseTooltip.__init__(self) + self.sid = None + self.widget = None - def populate(self, file_props): + def clear_tooltip(self): + self.sid = None + self.widget = None + + def get_tooltip(self, file_props, sid): + if self.sid == sid: + return True, self.widget + + self.widget = self._create_tooltip(file_props, sid) + self.sid = sid + return False, self.widget + + @staticmethod + def _create_tooltip(file_props, sid): ft_table = Gtk.Table(2, 1) ft_table.set_property('column-spacing', 2) current_row = 1 - self.create_window() properties = [] name = file_props.name if file_props.type_ == 'r': @@ -794,18 +664,18 @@ class FileTransfersTooltip(BaseTooltip): status = '' if file_props.started: status = _('Not started') - if file_props.stopped == True: + if file_props.stopped: status = _('Stopped') elif file_props.completed: status = _('Completed') - elif file_props.connected == False: + elif not file_props.connected: if file_props.completed: status = _('Completed') else: - if file_props.paused == True: + if file_props.paused: status = Q_('?transfer status:Paused') - elif file_props.stalled == True: - #stalled is not paused. it is like 'frozen' it stopped alone + elif file_props.stalled: + # stalled is not paused. it is like 'frozen' it stopped alone status = _('Stalled') else: status = _('Transferring') @@ -832,7 +702,8 @@ class FileTransfersTooltip(BaseTooltip): ft_table.attach(label, 2, 3, current_row, current_row + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL, 0, 0) - self.win.add(ft_table) + ft_table.show_all() + return ft_table def colorize_status(status):