| ... |
... |
@@ -22,7 +22,6 @@ |
|
22
|
22
|
import logging
|
|
23
|
23
|
import time
|
|
24
|
24
|
import pickle
|
|
25
|
|
-import operator
|
|
26
|
25
|
|
|
27
|
26
|
from gi.repository import Gio
|
|
28
|
27
|
from gi.repository import Gdk
|
| ... |
... |
@@ -75,7 +74,6 @@ def __init__(self, workspace_id: str) -> None: |
|
75
|
74
|
self._chats: dict[tuple[str, JID], Any] = {}
|
|
76
|
75
|
self._current_filter: str = 'all'
|
|
77
|
76
|
self._current_filter_text: str = ''
|
|
78
|
|
- self._chat_order: dict[tuple[str, JID], int] = {}
|
|
79
|
77
|
|
|
80
|
78
|
self.get_style_context().add_class('chatlist')
|
|
81
|
79
|
self.set_filter_func(self._filter_func)
|
| ... |
... |
@@ -97,12 +95,19 @@ def __init__(self, workspace_id: str) -> None: |
|
97
|
95
|
entries,
|
|
98
|
96
|
Gdk.DragAction.MOVE)
|
|
99
|
97
|
|
|
|
98
|
+ self._drag_row: Optional[ChatRow] = None
|
|
|
99
|
+ self._row_before: Optional[ChatRow] = None
|
|
|
100
|
+ self._row_after: Optional[ChatRow] = None
|
|
|
101
|
+ self._chat_order: list[ChatRow] = []
|
|
|
102
|
+
|
|
100
|
103
|
self.register_events([
|
|
101
|
104
|
('account-enabled', ged.GUI2, self._on_account_changed),
|
|
102
|
105
|
('account-disabled', ged.GUI2, self._on_account_changed),
|
|
103
|
106
|
('bookmarks-received', ged.GUI1, self._on_bookmarks_received),
|
|
104
|
107
|
])
|
|
105
|
108
|
|
|
|
109
|
+ self.connect('drag-motion', self._on_drag_motion)
|
|
|
110
|
+ self.connect('drag-data-received', self._on_drag_data_received)
|
|
106
|
111
|
self.connect('destroy', self._on_destroy)
|
|
107
|
112
|
|
|
108
|
113
|
self._timer_id = GLib.timeout_add_seconds(60, self._update_timer)
|
| ... |
... |
@@ -149,6 +154,100 @@ def emit_unread_changed(self) -> None: |
|
149
|
154
|
self._workspace_id,
|
|
150
|
155
|
count)
|
|
151
|
156
|
|
|
|
157
|
+ def _get_row_before(self, row: ChatRow) -> Optional[ChatRow]:
|
|
|
158
|
+ row = cast(ChatRow, self.get_row_at_index(row.get_index() - 1))
|
|
|
159
|
+ return row
|
|
|
160
|
+
|
|
|
161
|
+ def _get_row_after(self, row: ChatRow) -> Optional[ChatRow]:
|
|
|
162
|
+ row = cast(ChatRow, self.get_row_at_index(row.get_index() + 1))
|
|
|
163
|
+ return row
|
|
|
164
|
+
|
|
|
165
|
+ def _get_last_row(self) -> ChatRow:
|
|
|
166
|
+ row = cast(ChatRow, self.get_row_at_index(len(self.get_children())))
|
|
|
167
|
+ return row
|
|
|
168
|
+
|
|
|
169
|
+ def set_drag_row(self, row: ChatRow) -> None:
|
|
|
170
|
+ self._drag_row = row
|
|
|
171
|
+
|
|
|
172
|
+ def _on_drag_motion(self,
|
|
|
173
|
+ _widget: Gtk.Widget,
|
|
|
174
|
+ _drag_context: Gdk.DragContext,
|
|
|
175
|
+ _x_coord: int,
|
|
|
176
|
+ y_coord: int,
|
|
|
177
|
+ _time: int
|
|
|
178
|
+ ) -> bool:
|
|
|
179
|
+
|
|
|
180
|
+ row = cast(ChatRow, self.get_row_at_y(y_coord))
|
|
|
181
|
+ if row:
|
|
|
182
|
+ alloc = row.get_allocation()
|
|
|
183
|
+ hover_row_y = alloc.y
|
|
|
184
|
+ hover_row_height = alloc.height
|
|
|
185
|
+
|
|
|
186
|
+ if y_coord < hover_row_y + hover_row_height / 2:
|
|
|
187
|
+ self._row_before = self._get_row_before(row)
|
|
|
188
|
+ self._row_after = row
|
|
|
189
|
+ else:
|
|
|
190
|
+ self._row_before = row
|
|
|
191
|
+ self._row_after = self._get_row_after(row)
|
|
|
192
|
+ else:
|
|
|
193
|
+ self._row_before = self._get_last_row()
|
|
|
194
|
+ self._row_after = None
|
|
|
195
|
+
|
|
|
196
|
+ if self._drag_row in (self._row_before, self._row_after):
|
|
|
197
|
+ return False
|
|
|
198
|
+
|
|
|
199
|
+ return True
|
|
|
200
|
+
|
|
|
201
|
+ def _on_drag_data_received(self,
|
|
|
202
|
+ _widget: Gtk.Widget,
|
|
|
203
|
+ _drag_context: Gdk.DragContext,
|
|
|
204
|
+ _x_coord: int,
|
|
|
205
|
+ y_coord: int,
|
|
|
206
|
+ selection_data: Gtk.SelectionData,
|
|
|
207
|
+ _info: int,
|
|
|
208
|
+ _time: int
|
|
|
209
|
+ ) -> None:
|
|
|
210
|
+
|
|
|
211
|
+ item_type = selection_data.get_data_type().name()
|
|
|
212
|
+ if item_type == 'CHAT_LIST_ITEM':
|
|
|
213
|
+ self._change_pinned_order()
|
|
|
214
|
+ else:
|
|
|
215
|
+ log.debug('Unknown item type dropped')
|
|
|
216
|
+
|
|
|
217
|
+ def _change_pinned_order(self) -> None:
|
|
|
218
|
+ assert self._drag_row is not None
|
|
|
219
|
+
|
|
|
220
|
+ if not self._drag_row.is_pinned:
|
|
|
221
|
+ log.debug('Dragged row is not pinned')
|
|
|
222
|
+ return
|
|
|
223
|
+
|
|
|
224
|
+ if self._drag_row in (self._row_before, self._row_after):
|
|
|
225
|
+ log.debug('Dragged row dropped on self')
|
|
|
226
|
+ return
|
|
|
227
|
+
|
|
|
228
|
+ if self._row_after is None:
|
|
|
229
|
+ log.debug('Dragged row dropped in empty space')
|
|
|
230
|
+ return
|
|
|
231
|
+
|
|
|
232
|
+ self._chat_order.remove(self._drag_row)
|
|
|
233
|
+
|
|
|
234
|
+ if self._row_before is None:
|
|
|
235
|
+ self._chat_order.insert(0, self._drag_row)
|
|
|
236
|
+ else:
|
|
|
237
|
+ if self._row_before.position < self._drag_row.position:
|
|
|
238
|
+ offset = 1
|
|
|
239
|
+ else:
|
|
|
240
|
+ offset = 0
|
|
|
241
|
+ self._chat_order.insert(
|
|
|
242
|
+ self._row_before.position + offset, self._drag_row)
|
|
|
243
|
+
|
|
|
244
|
+ for row in cast(list[ChatRow], self.get_children()):
|
|
|
245
|
+ if row in self._chat_order:
|
|
|
246
|
+ row.position = self._chat_order.index(row)
|
|
|
247
|
+
|
|
|
248
|
+ app.window.store_open_chats(self._workspace_id)
|
|
|
249
|
+ self.invalidate_sort()
|
|
|
250
|
+
|
|
152
|
251
|
def _on_destroy(self, _widget: Gtk.Widget) -> None:
|
|
153
|
252
|
GLib.source_remove(self._timer_id)
|
|
154
|
253
|
|
| ... |
... |
@@ -273,10 +372,11 @@ def add_chat(self, |
|
273
|
372
|
jid,
|
|
274
|
373
|
type_,
|
|
275
|
374
|
pinned,
|
|
276
|
|
- self._chat_order)
|
|
|
375
|
+ position)
|
|
277
|
376
|
|
|
278
|
|
- self._chat_order[key] = position
|
|
279
|
377
|
self._chats[key] = row
|
|
|
378
|
+ if position != -1:
|
|
|
379
|
+ self._chat_order.insert(position, row)
|
|
280
|
380
|
|
|
281
|
381
|
self.add(row)
|
|
282
|
382
|
|
| ... |
... |
@@ -353,6 +453,16 @@ def select_chat_number(self, number: int) -> None: |
|
353
|
453
|
def toggle_chat_pinned(self, account: str, jid: JID) -> None:
|
|
354
|
454
|
row = self._chats[(account, jid)]
|
|
355
|
455
|
row.toggle_pinned()
|
|
|
456
|
+
|
|
|
457
|
+ self._chat_order.clear()
|
|
|
458
|
+ chat_rows = cast(list[ChatRow], self.get_children())
|
|
|
459
|
+ for row in chat_rows:
|
|
|
460
|
+ if row.is_pinned:
|
|
|
461
|
+ row.position = chat_rows.index(row)
|
|
|
462
|
+ self._chat_order.append(row)
|
|
|
463
|
+ else:
|
|
|
464
|
+ row.position = -1
|
|
|
465
|
+
|
|
356
|
466
|
self.invalidate_sort()
|
|
357
|
467
|
|
|
358
|
468
|
def remove_chat(self,
|
| ... |
... |
@@ -362,6 +472,8 @@ def remove_chat(self, |
|
362
|
472
|
) -> None:
|
|
363
|
473
|
|
|
364
|
474
|
row = self._chats.pop((account, jid))
|
|
|
475
|
+ if row.position != -1:
|
|
|
476
|
+ self._chat_order.remove(row)
|
|
365
|
477
|
self.remove(row)
|
|
366
|
478
|
row.destroy()
|
|
367
|
479
|
if emit_unread:
|
| ... |
... |
@@ -563,29 +675,6 @@ def _on_bookmarks_received(self, _event: events.BookmarksReceived) -> None: |
|
563
|
675
|
for row in rows:
|
|
564
|
676
|
row.update_name()
|
|
565
|
677
|
|
|
566
|
|
- def _change_pinned_order(self,
|
|
567
|
|
- row: ChatRow,
|
|
568
|
|
- row_after: Optional[ChatRow]) -> None:
|
|
569
|
|
-
|
|
570
|
|
- # Make list ordered by position of tuple[account, JID]
|
|
571
|
|
- order = [item[0] for item in sorted(self._chat_order.items(),
|
|
572
|
|
- key=operator.itemgetter(1))]
|
|
573
|
|
-
|
|
574
|
|
- key = (row.account, row.jid)
|
|
575
|
|
-
|
|
576
|
|
- order.remove(key)
|
|
577
|
|
- if row_after is None or not row_after.is_pinned:
|
|
578
|
|
- order.append(key)
|
|
579
|
|
- else:
|
|
580
|
|
- pos = order.index((row_after.account, row_after.jid))
|
|
581
|
|
- order.insert(pos, key)
|
|
582
|
|
-
|
|
583
|
|
- self._chat_order.clear()
|
|
584
|
|
- for pos, value in enumerate(order):
|
|
585
|
|
- self._chat_order[value] = pos
|
|
586
|
|
-
|
|
587
|
|
- self.invalidate_sort()
|
|
588
|
|
-
|
|
589
|
678
|
|
|
590
|
679
|
class ChatRow(Gtk.ListBoxRow):
|
|
591
|
680
|
def __init__(self,
|
| ... |
... |
@@ -594,7 +683,7 @@ def __init__(self, |
|
594
|
683
|
jid: JID,
|
|
595
|
684
|
type_: str,
|
|
596
|
685
|
pinned: bool,
|
|
597
|
|
- position: dict[tuple[str, JID], int]
|
|
|
686
|
+ position: int
|
|
598
|
687
|
) -> None:
|
|
599
|
688
|
|
|
600
|
689
|
Gtk.ListBoxRow.__init__(self)
|
| ... |
... |
@@ -767,7 +856,11 @@ def is_pinned(self) -> bool: |
|
767
|
856
|
|
|
768
|
857
|
@property
|
|
769
|
858
|
def position(self) -> int:
|
|
770
|
|
- return self._position.get((self.account, self.jid), -1)
|
|
|
859
|
+ return self._position
|
|
|
860
|
+
|
|
|
861
|
+ @position.setter
|
|
|
862
|
+ def position(self, position: int) -> None:
|
|
|
863
|
+ self._position = position
|
|
771
|
864
|
|
|
772
|
865
|
def _on_button_press(self,
|
|
773
|
866
|
_widget: Gtk.Widget,
|
| ... |
... |
@@ -799,6 +892,10 @@ def _on_drag_begin(self, |
|
799
|
892
|
widget: Gtk.Widget,
|
|
800
|
893
|
drag_context: Gdk.DragContext
|
|
801
|
894
|
) -> None:
|
|
|
895
|
+
|
|
|
896
|
+ chat_list = cast(ChatList, self.get_parent())
|
|
|
897
|
+ chat_list.set_drag_row(self)
|
|
|
898
|
+
|
|
802
|
899
|
# Use rendered ChatListRow as drag icon
|
|
803
|
900
|
alloc = self.get_allocation()
|
|
804
|
901
|
surface = cairo.ImageSurface(
|