| 20 * along with this program; if not, see <https://www.gnu.org/licenses/>. |
20 * along with this program; if not, see <https://www.gnu.org/licenses/>. |
| 21 */ |
21 */ |
| 22 |
22 |
| 23 #include "pidginconversationwindow.h" |
23 #include "pidginconversationwindow.h" |
| 24 |
24 |
| |
25 #include "gtkconv.h" |
| |
26 |
| |
27 enum { |
| |
28 PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, |
| |
29 PIDGIN_CONVERSATION_WINDOW_COLUMN_ICON, |
| |
30 PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, |
| |
31 }; |
| |
32 |
| |
33 enum { |
| |
34 SIG_CONVERSATION_SWITCHED, |
| |
35 N_SIGNALS, |
| |
36 }; |
| |
37 static guint signals[N_SIGNALS] = {0, }; |
| |
38 |
| 25 struct _PidginConversationWindow { |
39 struct _PidginConversationWindow { |
| 26 GtkApplicationWindow parent; |
40 GtkApplicationWindow parent; |
| 27 |
41 |
| 28 GtkWidget *vbox; |
42 GtkWidget *vbox; |
| |
43 |
| |
44 GtkWidget *view; |
| |
45 GtkTreeStore *model; |
| |
46 |
| |
47 GtkWidget *stack; |
| 29 }; |
48 }; |
| 30 |
49 |
| 31 G_DEFINE_TYPE(PidginConversationWindow, pidgin_conversation_window, |
50 G_DEFINE_TYPE(PidginConversationWindow, pidgin_conversation_window, |
| 32 GTK_TYPE_APPLICATION_WINDOW) |
51 GTK_TYPE_APPLICATION_WINDOW) |
| |
52 |
| |
53 static GtkWidget *default_window = NULL; |
| |
54 |
| |
55 /****************************************************************************** |
| |
56 * Callbacks |
| |
57 *****************************************************************************/ |
| |
58 static void |
| |
59 pidgin_conversation_window_selection_changed(GtkTreeSelection *selection, |
| |
60 gpointer data) |
| |
61 { |
| |
62 PidginConversationWindow *window = PIDGIN_CONVERSATION_WINDOW(data); |
| |
63 GtkTreeModel *model = NULL; |
| |
64 GtkTreeIter iter; |
| |
65 gboolean changed = FALSE; |
| |
66 |
| |
67 if(gtk_tree_selection_get_selected(selection, &model, &iter)) { |
| |
68 gchar *markup = NULL; |
| |
69 |
| |
70 gtk_tree_model_get(model, &iter, |
| |
71 PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, &markup, |
| |
72 -1); |
| |
73 |
| |
74 gtk_stack_set_visible_child_name(GTK_STACK(window->stack), markup); |
| |
75 g_free(markup); |
| |
76 |
| |
77 changed = TRUE; |
| |
78 } |
| |
79 |
| |
80 if(!changed) { |
| |
81 gtk_stack_set_visible_child_name(GTK_STACK(window->stack), "__empty__"); |
| |
82 } |
| |
83 } |
| |
84 |
| |
85 static gboolean |
| |
86 pidgin_conversation_window_foreach_destroy(GtkTreeModel *model, |
| |
87 GtkTreePath *path, |
| |
88 GtkTreeIter *iter, |
| |
89 gpointer data) |
| |
90 { |
| |
91 PurpleConversation *conversation = NULL; |
| |
92 |
| |
93 gtk_tree_model_get(model, iter, |
| |
94 PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, &conversation, |
| |
95 -1); |
| |
96 |
| |
97 if(conversation != NULL) { |
| |
98 pidgin_conversation_detach(conversation); |
| |
99 |
| |
100 gtk_list_store_remove(GTK_LIST_STORE(model), iter); |
| |
101 } |
| |
102 |
| |
103 return FALSE; |
| |
104 } |
| 33 |
105 |
| 34 /****************************************************************************** |
106 /****************************************************************************** |
| 35 * GObjectImplementation |
107 * GObjectImplementation |
| 36 *****************************************************************************/ |
108 *****************************************************************************/ |
| |
109 static void |
| |
110 pidgin_conversation_window_dispose(GObject *obj) { |
| |
111 PidginConversationWindow *window = PIDGIN_CONVERSATION_WINDOW(obj); |
| |
112 |
| |
113 if(GTK_IS_TREE_MODEL(window->model)) { |
| |
114 gtk_tree_model_foreach(GTK_TREE_MODEL(window->model), |
| |
115 pidgin_conversation_window_foreach_destroy, window); |
| |
116 } |
| |
117 |
| |
118 G_OBJECT_CLASS(pidgin_conversation_window_parent_class)->dispose(obj); |
| |
119 } |
| |
120 |
| 37 static void |
121 static void |
| 38 pidgin_conversation_window_init(PidginConversationWindow *window) { |
122 pidgin_conversation_window_init(PidginConversationWindow *window) { |
| 39 GtkBuilder *builder = NULL; |
123 GtkBuilder *builder = NULL; |
| 40 GtkWidget *menubar = NULL; |
124 GtkWidget *menubar = NULL; |
| 41 GMenuModel *model = NULL; |
125 GMenuModel *model = NULL; |
| 48 /* setup our menu */ |
132 /* setup our menu */ |
| 49 builder = gtk_builder_new_from_resource("/im/pidgin/Pidgin3/Conversations/menu.ui"); |
133 builder = gtk_builder_new_from_resource("/im/pidgin/Pidgin3/Conversations/menu.ui"); |
| 50 |
134 |
| 51 model = (GMenuModel *)gtk_builder_get_object(builder, "conversation"); |
135 model = (GMenuModel *)gtk_builder_get_object(builder, "conversation"); |
| 52 menubar = gtk_menu_bar_new_from_model(model); |
136 menubar = gtk_menu_bar_new_from_model(model); |
| |
137 |
| 53 gtk_box_pack_start(GTK_BOX(window->vbox), menubar, FALSE, FALSE, 0); |
138 gtk_box_pack_start(GTK_BOX(window->vbox), menubar, FALSE, FALSE, 0); |
| 54 gtk_widget_show(menubar); |
139 gtk_widget_show(menubar); |
| 55 |
140 |
| 56 g_object_unref(G_OBJECT(builder)); |
141 g_object_unref(G_OBJECT(builder)); |
| 57 } |
142 } |
| 58 |
143 |
| 59 static void |
144 static void |
| 60 pidgin_conversation_window_class_init(PidginConversationWindowClass *klass) { |
145 pidgin_conversation_window_class_init(PidginConversationWindowClass *klass) { |
| |
146 GObjectClass *obj_class = G_OBJECT_CLASS(klass); |
| 61 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); |
147 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); |
| |
148 |
| |
149 obj_class->dispose = pidgin_conversation_window_dispose; |
| |
150 |
| |
151 signals[SIG_CONVERSATION_SWITCHED] = g_signal_new_class_handler( |
| |
152 "conversation-switched", |
| |
153 G_OBJECT_CLASS_TYPE(obj_class), |
| |
154 G_SIGNAL_RUN_LAST, |
| |
155 NULL, |
| |
156 NULL, |
| |
157 NULL, |
| |
158 NULL, |
| |
159 G_TYPE_NONE, |
| |
160 1, |
| |
161 PURPLE_TYPE_CONVERSATION |
| |
162 ); |
| 62 |
163 |
| 63 gtk_widget_class_set_template_from_resource( |
164 gtk_widget_class_set_template_from_resource( |
| 64 widget_class, |
165 widget_class, |
| 65 "/im/pidgin/Pidgin3/Conversations/window.ui" |
166 "/im/pidgin/Pidgin3/Conversations/window.ui" |
| 66 ); |
167 ); |
| 67 |
168 |
| 68 gtk_widget_class_bind_template_child(widget_class, PidginConversationWindow, |
169 gtk_widget_class_bind_template_child(widget_class, PidginConversationWindow, |
| 69 vbox); |
170 vbox); |
| |
171 |
| |
172 gtk_widget_class_bind_template_child(widget_class, PidginConversationWindow, |
| |
173 model); |
| |
174 gtk_widget_class_bind_template_child(widget_class, PidginConversationWindow, |
| |
175 view); |
| |
176 |
| |
177 gtk_widget_class_bind_template_child(widget_class, PidginConversationWindow, |
| |
178 stack); |
| |
179 |
| |
180 gtk_widget_class_bind_template_callback(widget_class, |
| |
181 pidgin_conversation_window_selection_changed); |
| 70 } |
182 } |
| 71 |
183 |
| 72 /****************************************************************************** |
184 /****************************************************************************** |
| 73 * API |
185 * API |
| 74 *****************************************************************************/ |
186 *****************************************************************************/ |
| 75 GtkWidget * |
187 GtkWidget * |
| |
188 pidgin_conversation_window_get_default(void) { |
| |
189 if(!GTK_IS_WIDGET(default_window)) { |
| |
190 default_window = pidgin_conversation_window_new(); |
| |
191 g_object_add_weak_pointer(G_OBJECT(default_window), |
| |
192 (gpointer)&default_window); |
| |
193 } |
| |
194 |
| |
195 return default_window; |
| |
196 } |
| |
197 |
| |
198 GtkWidget * |
| 76 pidgin_conversation_window_new(void) { |
199 pidgin_conversation_window_new(void) { |
| 77 return GTK_WIDGET(g_object_new(PIDGIN_TYPE_CONVERSATION_WINDOW, NULL)); |
200 return GTK_WIDGET(g_object_new(PIDGIN_TYPE_CONVERSATION_WINDOW, NULL)); |
| 78 } |
201 } |
| 79 |
202 |
| 80 GtkWidget * |
203 void |
| 81 pidgin_conversation_window_get_vbox(PidginConversationWindow *window) { |
204 pidgin_conversation_window_add(PidginConversationWindow *window, |
| |
205 PurpleConversation *conversation) |
| |
206 { |
| |
207 PidginConversation *gtkconv = NULL; |
| |
208 GtkTreeIter iter; |
| |
209 const gchar *markup = NULL; |
| |
210 |
| |
211 g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); |
| |
212 g_return_if_fail(PURPLE_IS_CONVERSATION(conversation)); |
| |
213 |
| |
214 markup = purple_conversation_get_name(conversation); |
| |
215 |
| |
216 gtkconv = PIDGIN_CONVERSATION(conversation); |
| |
217 if(gtkconv != NULL) { |
| |
218 GtkWidget *parent = gtk_widget_get_parent(gtkconv->tab_cont); |
| |
219 |
| |
220 if(GTK_IS_WIDGET(parent)) { |
| |
221 g_object_ref(gtkconv->tab_cont); |
| |
222 gtk_container_remove(GTK_CONTAINER(parent), gtkconv->tab_cont); |
| |
223 } |
| |
224 |
| |
225 gtk_stack_add_named(GTK_STACK(window->stack), gtkconv->tab_cont, markup); |
| |
226 gtk_widget_show_all(gtkconv->tab_cont); |
| |
227 |
| |
228 if(GTK_IS_WIDGET(parent)) { |
| |
229 g_object_unref(gtkconv->tab_cont); |
| |
230 } |
| |
231 } |
| |
232 |
| |
233 gtk_tree_store_prepend(window->model, &iter, NULL); |
| |
234 gtk_tree_store_set(window->model, &iter, |
| |
235 PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, conversation, |
| |
236 PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, markup, |
| |
237 -1); |
| |
238 |
| |
239 if(!gtk_widget_is_visible(GTK_WIDGET(window))) { |
| |
240 gtk_widget_show_all(GTK_WIDGET(window)); |
| |
241 } |
| |
242 } |
| |
243 |
| |
244 void |
| |
245 pidgin_conversation_window_remove(PidginConversationWindow *window, |
| |
246 PurpleConversation *conversation) |
| |
247 { |
| |
248 GtkTreeIter iter; |
| |
249 GObject *obj = NULL; |
| |
250 |
| |
251 g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); |
| |
252 g_return_if_fail(PURPLE_IS_CONVERSATION(conversation)); |
| |
253 |
| |
254 if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(window->model), &iter)) { |
| |
255 /* The tree is empty. */ |
| |
256 return; |
| |
257 } |
| |
258 |
| |
259 do { |
| |
260 gtk_tree_model_get(GTK_TREE_MODEL(window->model), &iter, |
| |
261 PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, &obj, |
| |
262 -1); |
| |
263 |
| |
264 if(PURPLE_CONVERSATION(obj) == conversation) { |
| |
265 gtk_tree_store_remove(window->model, &iter); |
| |
266 |
| |
267 g_clear_object(&obj); |
| |
268 |
| |
269 break; |
| |
270 } |
| |
271 |
| |
272 g_clear_object(&obj); |
| |
273 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(window->model), &iter)); |
| |
274 } |
| |
275 |
| |
276 guint |
| |
277 pidgin_conversation_window_get_count(PidginConversationWindow *window) { |
| |
278 GList *children = NULL; |
| |
279 guint count = 0; |
| |
280 |
| |
281 g_return_val_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window), 0); |
| |
282 |
| |
283 children = gtk_container_get_children(GTK_CONTAINER(window)); |
| |
284 while(children != NULL) { |
| |
285 children = g_list_delete_link(children, children); |
| |
286 count++; |
| |
287 } |
| |
288 |
| |
289 return count; |
| |
290 } |
| |
291 |
| |
292 PurpleConversation * |
| |
293 pidgin_conversation_window_get_selected(PidginConversationWindow *window) { |
| |
294 PurpleConversation *conversation = NULL; |
| |
295 GtkTreeSelection *selection = NULL; |
| |
296 GtkTreeIter iter; |
| |
297 |
| 82 g_return_val_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window), NULL); |
298 g_return_val_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window), NULL); |
| 83 |
299 |
| 84 return window->vbox; |
300 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); |
| 85 } |
301 if(gtk_tree_selection_get_selected(selection, NULL, &iter)) { |
| |
302 |
| |
303 gtk_tree_model_get(GTK_TREE_MODEL(window->model), &iter, |
| |
304 PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, &conversation, |
| |
305 -1); |
| |
306 } |
| |
307 |
| |
308 return conversation; |
| |
309 } |
| |
310 |
| |
311 void |
| |
312 pidgin_conversation_window_select(PidginConversationWindow *window, |
| |
313 PurpleConversation *conversation) |
| |
314 { |
| |
315 const gchar *name = NULL; |
| |
316 |
| |
317 g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); |
| |
318 g_return_if_fail(PURPLE_IS_CONVERSATION(conversation)); |
| |
319 |
| |
320 name = purple_conversation_get_name(conversation); |
| |
321 gtk_stack_set_visible_child_name(GTK_STACK(window->stack), name); |
| |
322 } |
| |
323 |
| |
324 void |
| |
325 pidgin_conversation_window_select_previous(PidginConversationWindow *window) { |
| |
326 GtkTreeIter iter; |
| |
327 GtkTreeModel *model = NULL; |
| |
328 GtkTreeSelection *selection = NULL; |
| |
329 gboolean set = FALSE; |
| |
330 |
| |
331 g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); |
| |
332 |
| |
333 model = GTK_TREE_MODEL(window->model); |
| |
334 |
| |
335 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); |
| |
336 if(gtk_tree_selection_get_selected(selection, NULL, &iter)) { |
| |
337 if(gtk_tree_model_iter_previous(model, &iter)) { |
| |
338 gtk_tree_selection_select_iter(selection, &iter); |
| |
339 set = TRUE; |
| |
340 } |
| |
341 } |
| |
342 |
| |
343 if(!set) { |
| |
344 pidgin_conversation_window_select_last(window); |
| |
345 } |
| |
346 } |
| |
347 |
| |
348 |
| |
349 void |
| |
350 pidgin_conversation_window_select_next(PidginConversationWindow *window) { |
| |
351 GtkTreeIter iter; |
| |
352 GtkTreeModel *model = NULL; |
| |
353 GtkTreeSelection *selection = NULL; |
| |
354 gboolean set = FALSE; |
| |
355 |
| |
356 g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); |
| |
357 |
| |
358 model = GTK_TREE_MODEL(window->model); |
| |
359 |
| |
360 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); |
| |
361 if(gtk_tree_selection_get_selected(selection, NULL, &iter)) { |
| |
362 if(gtk_tree_model_iter_next(model, &iter)) { |
| |
363 gtk_tree_selection_select_iter(selection, &iter); |
| |
364 set = TRUE; |
| |
365 } |
| |
366 } |
| |
367 |
| |
368 if(!set) { |
| |
369 pidgin_conversation_window_select_first(window); |
| |
370 } |
| |
371 } |
| |
372 |
| |
373 void |
| |
374 pidgin_conversation_window_select_first(PidginConversationWindow *window) { |
| |
375 GtkTreeIter iter; |
| |
376 GtkTreeModel *model = NULL; |
| |
377 |
| |
378 g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); |
| |
379 |
| |
380 model = GTK_TREE_MODEL(window->model); |
| |
381 |
| |
382 if(gtk_tree_model_get_iter_first(model, &iter)) { |
| |
383 GtkTreeSelection *selection = NULL; |
| |
384 |
| |
385 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); |
| |
386 gtk_tree_selection_select_iter(selection, &iter); |
| |
387 } |
| |
388 } |
| |
389 |
| |
390 void |
| |
391 pidgin_conversation_window_select_last(PidginConversationWindow *window) { |
| |
392 GtkTreeIter iter; |
| |
393 GtkTreeModel *model = NULL; |
| |
394 gint count = 0; |
| |
395 |
| |
396 g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); |
| |
397 |
| |
398 model = GTK_TREE_MODEL(window->model); |
| |
399 count = gtk_tree_model_iter_n_children(model, NULL); |
| |
400 |
| |
401 if(gtk_tree_model_iter_nth_child(model, &iter, NULL, count - 1)) { |
| |
402 GtkTreeSelection *selection = NULL; |
| |
403 |
| |
404 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); |
| |
405 gtk_tree_selection_select_iter(selection, &iter); |
| |
406 } |
| |
407 } |
| |
408 |
| |
409 void |
| |
410 pidgin_conversation_window_select_nth(PidginConversationWindow *window, |
| |
411 guint nth) |
| |
412 { |
| |
413 GtkTreeIter iter; |
| |
414 GtkTreeModel *model = NULL; |
| |
415 |
| |
416 g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); |
| |
417 |
| |
418 model = GTK_TREE_MODEL(window->model); |
| |
419 |
| |
420 if(gtk_tree_model_iter_nth_child(model, &iter, NULL, nth)) { |
| |
421 GtkTreeSelection *selection = NULL; |
| |
422 |
| |
423 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); |
| |
424 gtk_tree_selection_select_iter(selection, &iter); |
| |
425 } |
| |
426 } |
| |
427 |
| |
428 gboolean |
| |
429 pidgin_conversation_window_conversation_is_selected(PidginConversationWindow *window, |
| |
430 PurpleConversation *conversation) |
| |
431 { |
| |
432 const gchar *name = NULL, *visible = NULL; |
| |
433 |
| |
434 g_return_val_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window), FALSE); |
| |
435 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE); |
| |
436 |
| |
437 name = purple_conversation_get_name(conversation); |
| |
438 visible = gtk_stack_get_visible_child_name(GTK_STACK(window->stack)); |
| |
439 |
| |
440 return purple_strequal(name, visible); |
| |
441 } |