patch #7247
[mldonkey.git] / src / gtk2 / gui / guiRooms.ml
blob1dea44585191618228fe1320c6878ac8e2ccc83f
1 (* Copyright 2004 b8_bavard, INRIA *)
2 (*
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 (* The rooms window of MLgui *)
22 open GuiTypes2
23 open GuiTypes
24 open CommonTypes
26 open GuiTools
27 open GuiGlobal
28 open GuiColumns
29 open Md4
30 open GuiProto
32 module M = GuiMessages
33 module Mi = GuiMisc
34 module O = GuiOptions
35 module G = GuiGlobal
36 module A = GuiArt
37 module U = GuiUtf8
39 let (!!) = Options.(!!)
40 let (=:=) = Options.(=:=)
41 let (<:>) = GuiTools.(<:>)
43 let verbose = O.gtk_verbose_rooms
45 let lprintf' fmt =
46 Printf2.lprintf ("GuiRooms: " ^^ fmt)
48 (*************************************************************************)
49 (* *)
50 (* room_num *)
51 (* *)
52 (*************************************************************************)
54 let room_num key =
55 try int_of_string key with _ -> raise Not_found
57 (*************************************************************************)
58 (* *)
59 (* room_of_key *)
60 (* *)
61 (*************************************************************************)
63 let room_of_key key =
64 try
65 let num = room_num key in
66 Hashtbl.find G.rooms num
67 with _ -> raise Not_found
69 (*************************************************************************)
70 (* *)
71 (* keys_to_rooms *)
72 (* *)
73 (*************************************************************************)
75 let keys_to_rooms keys =
76 let l = ref [] in
77 List.iter (fun k ->
78 try
79 let s = room_of_key k in
80 l := s :: !l
81 with _ -> ()) keys;
84 (*************************************************************************)
85 (* *)
86 (* room_key *)
87 (* *)
88 (*************************************************************************)
90 let room_key room_num =
91 Printf.sprintf "%d" room_num
93 (*************************************************************************)
94 (* *)
95 (* Global tables *)
96 (* *)
97 (*************************************************************************)
99 let (dialogs : (int * GuiTemplates.chat_buffer) list ref) = ref []
101 (*************************************************************************)
102 (* *)
103 (* Global variables *)
104 (* *)
105 (*************************************************************************)
107 let (current_room : int option ref) = ref None
108 let (chat_box : GuiTemplates.chat_view option ref) = ref None
110 let (room_label : GMisc.label option ref) = ref None
111 let (user_label : GMisc.label option ref) = ref None
113 let nrooms_opened = ref 0
114 let (view_context : GPango.context option ref) = ref None
116 (*************************************************************************)
117 (* *)
118 (* Templates *)
119 (* *)
120 (*************************************************************************)
122 module RoomUsers = GuiUsers.UserList (struct
124 let columns = O.rooms_users_columns
125 let view_context = view_context
126 let module_name = "RoomUsers"
128 end)
130 let userstore = new RoomUsers.g_user ()
132 module Rooms = GuiTemplates.Gview(struct
134 module Column = GuiColumns.Room
136 type item = room_info
138 let columns = O.rooms_columns
139 let get_key = (fun r -> room_key r.room_num)
140 let module_name = "Rooms"
142 end)
144 class g_room () =
145 let room_cols = new GTree.column_list in
146 let room_network_str = room_cols#add Gobject.Data.string in
147 let room_network_pixb = room_cols#add Gobject.Data.gobject_option in
148 let room_name = room_cols#add Gobject.Data.string in
149 let room_name_pixb = room_cols#add Gobject.Data.gobject_option in
150 let room_state_str = room_cols#add Gobject.Data.string in
151 let room_nusers = room_cols#add Gobject.Data.int in
152 object (self)
153 inherit Rooms.g_list room_cols
155 (*************************************************************************)
156 (* *)
157 (* from_item *)
158 (* *)
159 (*************************************************************************)
161 method from_item row ro =
162 store#set ~row ~column:room_network_str (Mi.network_name ro.room_network);
163 store#set ~row ~column:room_network_pixb (Mi.network_pixb ro.room_network ~size:A.SMALL ());
164 store#set ~row ~column:room_name (U.utf8_of ro.room_name);
165 store#set ~row ~column:room_name_pixb (Mi.room_state_to_icon ro.room_state ~size:A.SMALL);
166 store#set ~row ~column:room_state_str (Mi.room_state_to_string ro.room_state);
167 store#set ~row ~column:room_nusers ro.room_nusers
169 (*************************************************************************)
170 (* *)
171 (* from_new_item *)
172 (* *)
173 (*************************************************************************)
175 method from_new_item row ro ro_new =
176 if ro.room_state <> ro_new.room_state
177 then begin
178 store#set ~row ~column:room_name_pixb (Mi.room_state_to_icon ro_new.room_state ~size:A.SMALL);
179 store#set ~row ~column:room_state_str (Mi.room_state_to_string ro_new.room_state);
180 end;
181 if ro.room_nusers <> ro_new.room_nusers
182 then begin
183 store#set ~row ~column:room_nusers ro_new.room_nusers
186 (*************************************************************************)
187 (* *)
188 (* content *)
189 (* *)
190 (*************************************************************************)
192 method content col c =
193 let autosize = match col#sizing with `AUTOSIZE -> true | _ -> false in
194 match c with
195 Col_room_name ->
196 begin
197 if !!O.gtk_look_use_icons
198 then begin
199 let renderer = GTree.cell_renderer_pixbuf [`XALIGN 0.;`XPAD 4] in
200 col#pack ~expand:false renderer;
201 col#add_attribute renderer "pixbuf" room_name_pixb
202 end;
203 let renderer = GTree.cell_renderer_text [`XALIGN 0.] in
204 col#pack ~expand:false renderer;
205 if autosize
206 then col#add_attribute renderer "text" room_name
207 else col#set_cell_data_func renderer
208 (fun model row ->
209 match !view_context with
210 Some context when col#width > 0 ->
211 begin
212 let width =
213 if !!O.gtk_look_use_icons
214 then (col#width - 4 - !!O.gtk_look_lists_icon_size) - 4 * !G.char_width
215 else col#width - 4 * !G.char_width
217 let name = model#get ~row ~column:room_name in
218 let s = GuiTools.fit_string_to_pixels name ~context ~pixels:width in
219 renderer#set_properties [ `TEXT s ]
221 | _ -> renderer#set_properties [ `TEXT "" ]
225 | Col_room_nusers ->
226 begin
227 let renderer = GTree.cell_renderer_text [`XALIGN 0.] in
228 col#pack renderer;
229 col#add_attribute renderer "text" room_nusers
232 | Col_room_state ->
233 begin
234 let renderer = GTree.cell_renderer_text [`XALIGN 0.] in
235 col#pack renderer;
236 col#add_attribute renderer "text" room_state_str
239 | Col_room_network ->
240 begin
241 if !!O.gtk_look_use_icons
242 then begin
243 let renderer = GTree.cell_renderer_pixbuf [`XALIGN 0.] in
244 col#pack renderer;
245 col#add_attribute renderer "pixbuf" room_network_pixb
246 end else begin
247 let renderer = GTree.cell_renderer_text [`XALIGN 0.] in
248 col#pack renderer;
249 col#add_attribute renderer "text" room_network_str
253 (*************************************************************************)
254 (* *)
255 (* sort_items *)
256 (* *)
257 (*************************************************************************)
259 method sort_items c k1 k2 =
261 let ro1 = room_of_key k1 in
262 let ro2 = room_of_key k2 in
263 match c with
264 Col_room_name -> compare (String.lowercase ro1.room_name) (String.lowercase ro2.room_name)
265 | Col_room_nusers -> compare ro1.room_nusers ro2.room_nusers
266 | Col_room_state -> compare ro1.room_state ro2.room_state
267 | Col_room_network -> compare ro1.room_network ro2.room_network
268 with _ -> 0
270 (*************************************************************************)
271 (* *)
272 (* force_update_icons *)
273 (* *)
274 (*************************************************************************)
276 method force_update_icons () =
277 let f k row =
278 let ro = room_of_key k in
279 store#set ~row ~column:room_network_pixb (Mi.network_pixb ro.room_network ~size:A.SMALL ());
280 store#set ~row ~column:room_name_pixb (Mi.room_state_to_icon ro.room_state ~size:A.SMALL);
282 List.iter (fun k ->
284 let row = self#find_row k in
285 Gaux.may ~f:(f k) row
286 with _ -> ()
287 ) (self#all_items ())
291 let roomstore = new g_room ()
294 (*************************************************************************)
295 (* *)
296 (* update_rooms_label *)
297 (* *)
298 (*************************************************************************)
300 let update_rooms_label () =
301 match !room_label with
302 Some label ->
303 begin
304 let markup =
305 create_default_bold_markup
306 (Printf.sprintf "%s (%d / %d)" !M.rT_lb_rooms !nrooms_opened roomstore#nitems)
308 label#set_label markup
310 | _ -> ()
312 (*************************************************************************)
313 (* *)
314 (* update_users_label *)
315 (* *)
316 (*************************************************************************)
318 let update_users_label () =
319 match !user_label with
320 Some label ->
321 begin
322 let markup =
323 create_default_bold_markup
324 (Printf.sprintf "%s (%d)" !M.rT_lb_users userstore#nitems)
326 label#set_label markup
328 | _ -> ()
330 (*************************************************************************)
331 (* *)
332 (* message to the core *)
333 (* *)
334 (*************************************************************************)
336 let close_open_room sel () =
337 let l = keys_to_rooms sel in
338 List.iter (fun ro ->
339 match ro.room_state with
340 RoomOpened -> GuiCom.send (SetRoomState (ro.room_num, RoomClosed))
341 | _ -> GuiCom.send (SetRoomState (ro.room_num, RoomOpened))
344 let on_entry_return num s =
345 GuiCom.send (SendMessage (num, PublicMessage (0, s)))
347 let get_user_info user_num =
348 GuiCom.send (GetUser_info user_num)
350 (*************************************************************************)
351 (* *)
352 (* room_menu *)
353 (* *)
354 (*************************************************************************)
356 let room_menu sel =
357 match sel with
358 [] -> []
359 | _ ->
361 `I (!M.rT_me_close_open_room, close_open_room sel) ;
364 (*************************************************************************)
365 (* *)
366 (* on_select_room *)
367 (* *)
368 (*************************************************************************)
370 let on_select_room sel =
372 userstore#clear ();
373 update_users_label ();
374 (if !!verbose then lprintf' "Searching box for chats\n");
375 let box =
376 match !chat_box with
377 Some w -> (w#clear (); w)
378 | _ -> raise Exit
380 match sel with
381 [] -> (if !!verbose then lprintf' "No room selected\n")
382 | k :: tail ->
383 begin
385 let ro = room_of_key k in
386 current_room := Some ro.room_num;
387 match ro.room_state with
388 RoomOpened ->
389 begin
390 List.iter (fun user_num ->
392 let u = Hashtbl.find G.users user_num in
393 userstore#add_item u ~f:update_users_label ();
394 with _ -> get_user_info user_num
395 ) ro.room_users;
397 let chat_buf = List.assoc ro.room_num !dialogs in
398 box#set_buffer chat_buf
399 with _ -> (if !!verbose then lprintf' "No chat dialog availabale\n")
401 | _ -> (if !!verbose then lprintf' "room_users empty\n")
402 with _ -> ()
404 with _ -> (if !!verbose then lprintf' "No chat_box found\n")
406 (*************************************************************************)
407 (* *)
408 (* on_double_click_room *)
409 (* *)
410 (*************************************************************************)
412 let on_double_click_room k =
413 close_open_room [k] ()
415 (*************************************************************************)
416 (* *)
417 (* filter_room *)
418 (* *)
419 (*************************************************************************)
421 let filter_room k =
423 let ro = room_of_key k in
424 not (List.memq ro.room_network !G.networks_filtered)
425 with _ -> true
427 (*************************************************************************)
428 (* *)
429 (* Templates initialization *)
430 (* *)
431 (*************************************************************************)
433 let _ =
434 roomstore#set_filter filter_room
436 (*************************************************************************)
437 (* *)
438 (* clear *)
439 (* *)
440 (*************************************************************************)
442 let clear () =
443 List.iter (fun (_, chat_buf) ->
444 chat_buf#clear ()
445 ) !dialogs;
446 let _ =
447 match !chat_box with
448 None -> ()
449 | Some box -> box#clear ()
451 dialogs := [];
452 current_room := None;
453 userstore#clear ();
454 roomstore#clear ();
455 nrooms_opened := 0;
456 update_rooms_label ();
457 update_users_label ()
459 (*************************************************************************)
460 (* *)
461 (* message from the core *)
462 (* *)
463 (*************************************************************************)
465 let hashtbl_rooms_update ro ro_new =
466 ro.room_state <- ro_new.room_state;
467 ro.room_users <- ro_new.room_users;
468 ro.room_messages <- ro_new.room_messages;
469 ro.room_nusers <- ro_new.room_nusers
471 let add_chat_to_room ro =
472 match ro.room_state with
473 RoomOpened ->
474 begin
475 let chat_buf = GuiTemplates.chat_buffer ~on_entry:(on_entry_return ro.room_num) () in
476 dialogs := (ro.room_num, chat_buf) :: !dialogs;
477 incr nrooms_opened;
478 update_rooms_label ()
480 | _ ->
481 begin
483 let chat_buf = List.assoc ro.room_num !dialogs in
484 chat_buf#clear ();
485 dialogs := List.remove_assoc ro.room_num !dialogs;
486 List.iter (fun user_num ->
487 Hashtbl.remove G.users user_num
488 ) ro.room_users;
489 let num = match !current_room with Some n -> n | _ -> assert false in
490 let box = match !chat_box with Some w -> w | _ -> assert false in
491 if num = ro.room_num
492 then begin
493 box#clear ();
494 userstore#clear ();
495 decr nrooms_opened;
496 update_rooms_label ();
497 update_users_label ()
499 with _ -> ()
502 let add_room ro =
503 if ro.room_num <> 0
504 then begin
505 (if !!verbose then lprintf' "Adding room %s num: %d\n" ro.room_name ro.room_num);
506 add_chat_to_room ro;
507 roomstore#add_item ro ~f:update_rooms_label ();
508 Hashtbl.add G.rooms ro.room_num ro
511 let room_info r =
513 (if !!verbose then lprintf' "Room_info of %s\n" r.room_name);
514 let ro = Hashtbl.find G.rooms r.room_num in
515 let row = roomstore#find_row (room_key r.room_num) in
516 (* no need to keep ro.room_messages, it is stored in dialogs *)
517 let ro_new = {r with room_users = ro.room_users} in
518 (if ro_new.room_state <> ro.room_state
519 then add_chat_to_room ro_new);
520 Gaux.may ~f:(fun r -> roomstore#update_item r ro ro_new) row;
521 hashtbl_rooms_update ro ro_new
522 with _ -> add_room r
524 let find_user_name user_num =
526 let u = Hashtbl.find G.users user_num in
527 u.GuiTypes.user_name
528 with _ -> raise Not_found
530 let message_from_server s =
531 let len = String.length s in
532 if len > 0
533 then begin
534 match s.[0] with
535 '<' ->
536 begin
537 try
538 let pos = String.index s '>' in
539 let u = String.sub s 1 (pos - 1) in
540 let mes = String.sub s (pos + 1) (len - pos - 1) in
541 (mes, u, false)
542 with _ -> (s, "From server", false)
544 | _ -> (s, "From server", false)
546 end else ("", "From server", false)
548 let add_room_message room_num msg =
549 (if !!verbose then lprintf' "Adding message to room %d\n" room_num);
551 let chat_buf = List.assoc room_num !dialogs in
552 let (mes, name, priv) =
553 match msg with
554 ServerMessage s -> message_from_server s
555 | PublicMessage (n, s) -> (s, (find_user_name n), false)
556 | PrivateMessage (n, s) -> (s, (find_user_name n), true)
558 chat_buf#insert_text mes name ~priv ();
559 (* no need to store [msg] in the record field room_messages, it is stored in dialogs *)
560 with _ -> ()
562 let remove_room_user room_num user_num =
563 (if !!verbose then lprintf' "Removing user to room %d\n" room_num);
565 let ro = Hashtbl.find G.rooms room_num in
566 let _row = roomstore#find_row (room_key room_num) in
567 (if List.mem user_num ro.room_users
568 then ro.room_users <- List.filter (fun n -> n <> user_num) ro.room_users);
569 let _ =
570 match (!current_room, ro.room_state) with
571 (Some n, RoomOpened) when n = room_num ->
572 begin
574 let _u = Hashtbl.find G.users user_num in
575 userstore#remove_item (GuiUsers.user_key user_num);
576 update_users_label ();
577 with _ -> ()
579 | _ -> ()
581 Hashtbl.remove G.users user_num (* remove the user from G.users if it exists *)
582 with _ ->
583 begin
584 (if !!verbose then lprintf' "room not found in remove_user ... removing user %d\n" user_num);
585 Hashtbl.remove G.users user_num (* Anyway remove the user if the room does'nt exist *)
588 let add_room_user room_num user_num =
589 (if !!verbose then lprintf' "Adding user to room %d\n" room_num);
591 let ro = Hashtbl.find G.rooms room_num in
592 let _row = roomstore#find_row (room_key room_num) in
593 (if not (List.mem user_num ro.room_users)
594 then ro.room_users <- user_num :: ro.room_users);
595 match (!current_room, ro.room_state) with
596 (Some n, RoomOpened) when n = room_num ->
597 begin
599 let u = Hashtbl.find G.users user_num in
600 userstore#add_item u ~f:update_users_label ()
601 with _ -> ()
603 | _ -> ()
605 with _ ->
606 begin
607 (if !!verbose then lprintf' "room not found in add_user ... removing user %d\n" user_num);
608 Hashtbl.remove G.users user_num (* remove the user if the room does'nt exist *)
611 let update_user_info u_new =
612 match !current_room with
613 Some room_num ->
614 begin
616 let ro = Hashtbl.find G.rooms room_num in
617 if List.mem u_new.user_num ro.room_users
618 then begin
620 let u = Hashtbl.find G.users u_new.user_num in
621 let row = userstore#find_row (GuiUsers.user_key u_new.user_num) in
622 Gaux.may ~f:(fun r -> userstore#update_item r u u_new) row;
623 GuiUsers.hashtbl_users_update u u_new
624 with _ -> ()
626 with _ -> ()
628 | _ -> ()
630 (*************************************************************************)
631 (* *)
632 (* message from GuiNetwoks *)
633 (* *)
634 (*************************************************************************)
636 let reset_rooms_filter () =
637 roomstore#refresh_filter ()
639 (*************************************************************************)
640 (* *)
641 (* rooms window *)
642 (* *)
643 (*************************************************************************)
645 open GMain
647 let rooms_box gui =
648 let hpaned_rooms =
649 GPack.paned `HORIZONTAL ()
651 let hpaned_users =
652 GPack.paned `HORIZONTAL
653 ~packing:hpaned_rooms#add2 ()
655 ignore (hpaned_rooms#connect#destroy ~callback:
656 (fun _ ->
657 view_context := None;
658 chat_box := None;
659 current_room := None;
660 userstore#clear ();
661 room_label := None;
662 user_label := None;
664 let vbox_rooms =
665 GPack.vbox ~homogeneous:false ~border_width:6 ~spacing:6
666 ~packing:hpaned_rooms#add1 ()
668 let vbox_users =
669 GPack.vbox ~homogeneous:false ~border_width:6 ~spacing:6
670 ~packing:hpaned_users#add1 ()
672 let vbox_chat =
673 GPack.vbox ~border_width:6 ~spacing:6
674 ~packing:hpaned_users#add2 ()
677 let rooms_evbox =
678 GBin.event_box ~packing:(vbox_rooms#pack ~expand:false ~fill:true) ()
680 rooms_evbox#misc#modify_bg [(`NORMAL, (`NAME "#AFAFF4"))];
681 let rooms_label =
682 GMisc.label ~xalign:0. ~yalign:0.
683 ~xpad:3 ~ypad:3 ~packing:rooms_evbox#add ()
685 let users_evbox =
686 GBin.event_box ~packing:(vbox_users#pack ~expand:false ~fill:true) ()
688 users_evbox#misc#modify_bg [(`NORMAL, (`NAME "#AFAFF4"))];
689 let users_label =
690 GMisc.label ~xalign:0. ~yalign:0.
691 ~xpad:3 ~ypad:3 ~packing:users_evbox#add ()
693 let chat_evbox =
694 GBin.event_box ~packing:(vbox_chat#pack ~expand:false ~fill:true) ()
696 chat_evbox#misc#modify_bg [(`NORMAL, (`NAME "#AFAFF4"))];
697 let markup = GuiTools.create_default_bold_markup !M.rT_lb_chat in
698 let _chat_label =
699 GMisc.label ~xalign:0. ~yalign:0. ~markup
700 ~xpad:3 ~ypad:3 ~packing:chat_evbox#add ()
703 let roomview =
704 Rooms.treeview ~mode:`MULTIPLE
705 ~packing:(vbox_rooms#pack ~expand:true ~fill:true) ()
707 view_context := Some roomview#view#misc#pango_context;
708 roomview#set_model roomstore#gmodel;
709 roomview#set_menu room_menu;
710 roomview#set_on_select on_select_room;
711 roomview#set_on_double_click on_double_click_room;
712 let userview =
713 RoomUsers.treeview ~mode:`MULTIPLE
714 ~packing:(vbox_users#pack ~expand:true ~fill:true) ()
716 userview#set_model userstore#gmodel;
717 userview#set_menu GuiUsers.user_menu;
718 let chat_view =
719 GuiTemplates.chat_view ~extended:true ~my_name:!G. client_name
720 ~packing:(vbox_chat#pack ~expand:true ~fill:true) ()
723 GuiTools.set_hpaned hpaned_rooms O.rooms_hpane_left;
724 GuiTools.get_hpaned hpaned_rooms O.rooms_hpane_left;
725 GuiTools.set_hpaned hpaned_users O.rooms_hpane2_left;
726 GuiTools.get_hpaned hpaned_users O.rooms_hpane2_left;
728 chat_box := Some chat_view;
730 rooms_label#set_use_markup true;
731 room_label := Some rooms_label;
732 update_rooms_label ();
734 users_label#set_use_markup true;
735 user_label := Some users_label;
736 update_users_label ();
738 hpaned_rooms#coerce