1
-----------------------------------------------------------------------
2
-----------------------------------------------------------------------
4
with Ada.Containers; use Ada.Containers;
5
with Ada.Containers.Indefinite_Hashed_Maps;
6
with Ada.Calendar; use Ada.Calendar;
9
with GNAT.Strings; use GNAT.Strings;
11
with GNATCOLL.Symbols; use GNATCOLL.Symbols;
12
with GNATCOLL.Utils; use GNATCOLL.Utils;
13
with GNATCOLL.VFS.GtkAda; use GNATCOLL.VFS.GtkAda;
15
with Gdk.Pixbuf; use Gdk.Pixbuf;
16
with Gdk.Types.Keysyms; use Gdk.Types.Keysyms;
17
with Gtk.Enums; use Gtk.Enums;
18
with Gtk.Tree_Selection; use Gtk.Tree_Selection;
19
with Glib.Convert; use Glib.Convert;
22
with Basic_Types; use Basic_Types;
23
with Entities; use Entities;
24
with GPS.Kernel.Contexts; use GPS.Kernel.Contexts;
25
with GPS.Kernel.Project; use GPS.Kernel.Project;
26
with GPS.Kernel.Standard_Hooks; use GPS.Kernel.Standard_Hooks;
27
with GPS.Intl; use GPS.Intl;
28
with GUI_Utils; use GUI_Utils;
29
with Language.Unknown; use Language.Unknown;
30
with Language.Icons; use Language.Icons;
31
with Language_Handlers; use Language_Handlers;
32
with Projects; use Projects;
33
with String_Utils; use String_Utils;
34
with Traces; use Traces;
36
package body Project_Explorers_Common is
38
Me : constant Debug_Handle := Create ("Project_Explorers_Common");
44
function Columns_Types return GType_Array is
47
(Icon_Column => Gdk.Pixbuf.Get_Type,
48
File_Column => Get_Virtual_File_Type,
49
Display_Name_Column => GType_String,
50
Node_Type_Column => GType_Int,
51
User_Data_Column => GType_Pointer,
52
Line_Column => GType_Int,
53
Column_Column => GType_Int,
54
Project_Column => GType_String,
55
Category_Column => GType_Int,
56
Up_To_Date_Column => GType_Boolean,
57
Entity_Base_Column => GType_String,
58
Timestamp_Column => GType_Int);
65
procedure Init_Graphics (Widget : Gtk_Widget) is
67
function R (Id : String) return Gdk_Pixbuf;
68
-- Convenience function: create the Gdk_Pixbuf from stock Id
74
function R (Id : String) return Gdk_Pixbuf is
76
return Render_Icon (Widget, Id, Icon_Size_Menu);
80
-- If initialization has already been done, exit
81
if Open_Pixbufs (Project_Node) /= null then
85
Language.Icons.Init_Graphics (Widget);
87
Open_Pixbufs (Project_Node) := R ("gps-project-open");
88
Close_Pixbufs (Project_Node) := R ("gps-project-closed");
90
Open_Pixbufs (Modified_Project_Node) := R ("gps-project-modified-open");
91
Close_Pixbufs (Modified_Project_Node) :=
92
R ("gps-project-modified-closed");
94
-- ??? Would be nice to have different pixbufs for these
95
Open_Pixbufs (Extends_Project_Node) := R ("gps-project-open");
96
Close_Pixbufs (Extends_Project_Node) := R ("gps-project-closed");
98
Open_Pixbufs (Directory_Node) := R ("gps-folder-open");
99
Close_Pixbufs (Directory_Node) := R ("gps-folder-closed");
100
Open_Pixbufs (Obj_Directory_Node) := R ("gps-folder-obj-open");
101
Close_Pixbufs (Obj_Directory_Node) := R ("gps-folder-obj-closed");
103
Open_Pixbufs (Exec_Directory_Node) := R ("gps-folder-exec-open");
104
Close_Pixbufs (Exec_Directory_Node) := R ("gps-folder-exec-closed");
105
Open_Pixbufs (File_Node) := R ("gps-file");
106
Close_Pixbufs (File_Node) := R ("gps-file");
108
Open_Pixbufs (Category_Node) := R ("gps-box");
109
Close_Pixbufs (Category_Node) := R ("gps-box");
116
procedure Append_File
117
(Kernel : Kernel_Handle;
118
Model : Gtk_Tree_Store;
119
Base : Gtk_Tree_Iter;
120
File : GNATCOLL.VFS.Virtual_File;
121
Sorted : Boolean := False)
123
Iter : Gtk_Tree_Iter;
124
Iter2 : Gtk_Tree_Iter;
125
Lang : Language_Access;
129
Iter := Children (Model, Base);
132
while Iter /= Null_Iter loop
135
if Get_Node_Type (Model, Iter) = File_Node then
137
Name : constant Filesystem_String :=
138
Get_Base_Name (Model, Iter);
140
if File.Base_Name < Name then
141
Insert_Before (Model, Iter2, Base, Iter);
154
Append (Model, Iter, Base);
158
Append (Model, Iter, Base);
161
Set_File (Model, Iter, File_Column, File);
162
Set (Model, Iter, Display_Name_Column, Display_Base_Name (File));
163
Set (Model, Iter, Icon_Column,
164
Glib.Object.GObject (Close_Pixbufs (File_Node)));
165
Set (Model, Iter, Node_Type_Column, Gint (Node_Types'Pos (File_Node)));
166
Set (Model, Iter, Up_To_Date_Column, False);
168
Lang := Get_Language_From_File (Get_Language_Handler (Kernel), File);
170
if Lang /= Unknown_Lang then
171
Append_Dummy_Iter (Model, Iter);
175
-----------------------
176
-- Append_Dummy_Iter --
177
-----------------------
179
procedure Append_Dummy_Iter
180
(Model : Gtk_Tree_Store;
181
Base : Gtk_Tree_Iter)
183
Iter : Gtk_Tree_Iter;
185
Append (Model, Iter, Base);
186
Set_Node_Type (Model, Iter, Entity_Node, Expanded => False);
187
end Append_Dummy_Iter;
189
--------------------------
190
-- Append_Category_Node --
191
--------------------------
193
function Append_Category_Node
194
(Model : Gtk_Tree_Store;
195
File : GNATCOLL.VFS.Virtual_File;
196
Category : Language_Category;
197
Category_Name : GNATCOLL.Symbols.Symbol;
198
Parent_Iter : Gtk_Tree_Iter) return Gtk_Tree_Iter
200
Name : constant String :=
201
Language.Category_Name (Category, Category_Name);
203
Sibling : Gtk_Tree_Iter;
206
Sibling := Children (Model, Parent_Iter);
208
if Sibling = Null_Iter then
209
Append (Model, N, Parent_Iter);
211
while Sibling /= Null_Iter
212
and then Get_String (Model, Sibling, Display_Name_Column) <= Name
214
Next (Model, Sibling);
217
if Sibling = Null_Iter then
218
Append (Model, N, Parent_Iter);
220
Insert_Before (Model, N, Parent_Iter, Sibling);
224
Set_File (Model, N, File_Column, File);
225
Set (Model, N, Display_Name_Column, Locale_To_UTF8 (Name));
226
Set (Model, N, Icon_Column,
228
(Entity_Icons (False, Visibility_Public) (Category)));
229
Set (Model, N, Node_Type_Column, Gint (Node_Types'Pos (Category_Node)));
230
Set (Model, N, Up_To_Date_Column, True);
231
Set (Model, N, Category_Column, Language_Category'Pos (Category));
234
end Append_Category_Node;
240
function Entity_Name_Of
241
(Construct : Construct_Information;
242
Show_Profiles : Boolean;
243
Max_Profile_Length : Positive := Positive'Last) return String
246
function Escape return String;
247
pragma Inline (Escape);
248
-- Escape Construct.Name.all as a pango markup string.
249
-- The characters which need to be escaped in pango markup language are
250
-- '&', '<', '>', '\', and '"'.
251
-- The code here assumes that Entity names, in any language, can only
252
-- contain '&', '<', '>', or '"' and that if it does, one of these
253
-- characters is necessarily in the first position, for the overloading
254
-- of operators such as '<' or '&&', or for a quoted name.
260
function Escape return String is
262
Str : constant Cst_String_Access := Get (Construct.Name);
268
C := Str (Str'First);
270
if C = '"' or else C = '&' or else C = '<' or else C = '>' then
271
return Escape_Text (Str.all);
277
Name : constant String := Reduce (Escape);
280
if Show_Profiles and then Construct.Profile /= null then
281
return Name & " <span foreground=""#A0A0A0"">"
282
& Escape_Text (Reduce (Construct.Profile.all, Max_Profile_Length))
293
function Entity_Icon_Of
294
(Construct : Construct_Information) return Gdk_Pixbuf is
297
(Construct.Is_Declaration, Construct.Visibility) (Construct.Category);
300
function Entity_Icon_Of
301
(Construct : Simple_Construct_Information) return Gdk.Pixbuf.Gdk_Pixbuf
305
(Construct.Is_Declaration, Construct.Visibility) (Construct.Category);
308
------------------------
309
-- Append_Entity_Node --
310
------------------------
312
function Append_Entity_Node
313
(Model : Gtk_Tree_Store;
314
File : GNATCOLL.VFS.Virtual_File;
315
Construct : Construct_Information;
316
Parent_Iter : Gtk_Tree_Iter) return Gtk_Tree_Iter
319
Sibling : Gtk_Tree_Iter;
322
Sibling := Children (Model, Parent_Iter);
324
if Sibling = Null_Iter then
325
Append (Model, N, Parent_Iter);
327
while Sibling /= Null_Iter
328
and then Get_String (Model, Sibling, Display_Name_Column)
329
<= Get (Construct.Name).all
331
Next (Model, Sibling);
334
if Sibling = Null_Iter then
335
Append (Model, N, Parent_Iter);
337
Insert_Before (Model, N, Parent_Iter, Sibling);
341
Set_File (Model, N, File_Column, File);
342
Set (Model, N, Display_Name_Column, Entity_Name_Of (Construct, True));
343
Set (Model, N, Entity_Base_Column, Reduce (Get (Construct.Name).all));
344
Set (Model, N, Icon_Column,
345
Glib.Object.GObject (Entity_Icon_Of (Construct)));
346
Set (Model, N, Node_Type_Column, Gint (Node_Types'Pos (Entity_Node)));
348
if Construct.Sloc_Entity.Line /= 0 then
349
Set (Model, N, Line_Column, Gint (Construct.Sloc_Entity.Line));
350
Set (Model, N, Column_Column, Gint (Construct.Sloc_Entity.Column));
352
Set (Model, N, Line_Column, Gint (Construct.Sloc_Start.Line));
353
Set (Model, N, Column_Column, Gint (Construct.Sloc_Start.Column));
356
Set (Model, N, Up_To_Date_Column, True);
358
end Append_Entity_Node;
360
----------------------
361
-- Append_File_Info --
362
----------------------
364
procedure Append_File_Info
365
(Kernel : Kernel_Handle;
366
Model : Gtk_Tree_Store;
367
Node : Gtk_Tree_Iter;
368
File_Name : GNATCOLL.VFS.Virtual_File)
370
Languages : constant Language_Handler := Get_Language_Handler (Kernel);
372
N, N2 : Gtk_Tree_Iter;
373
Iter : Gtk_Tree_Iter;
375
Lang : Language_Access;
376
Constructs : Construct_List;
377
Category : Language_Category;
379
package Iter_Map is new Ada.Containers.Indefinite_Hashed_Maps
381
Element_Type => Gtk_Tree_Iter,
382
Hash => Ada.Strings.Hash,
383
Equivalent_Keys => "=");
386
Categories : Iter_Map.Map;
387
Handler : LI_Handler;
389
Node_Appended : Boolean := False;
392
-- Mark the file information as up-to-date
394
Set (Model, Node, Timestamp_Column,
395
Gint (File_Time_Stamp (File_Name) - GNATCOLL.Utils.No_Time));
397
-- Remove any previous information for this file
399
N := Children (Model, Node);
401
while N /= Null_Iter loop
407
Handler := Get_LI_Handler_From_File (Languages, File_Name);
409
if Handler = null then
413
Push_State (Kernel, Busy);
415
Lang := Get_Language_From_File (Languages, File_Name);
418
Parse_File_Constructs (Handler, Languages, File_Name, Constructs);
420
Constructs.Current := Constructs.First;
422
while Constructs.Current /= null loop
423
if Constructs.Current.Name /= No_Symbol then
424
Category := Filter_Category (Constructs.Current.Category);
426
if Category /= Cat_Unknown
427
and then Category /= Cat_Parameter
428
and then Category /= Cat_Field
431
Name : constant String :=
432
Category_Name (Category,
435
Cursor : Iter_Map.Cursor;
436
New_Iter : Gtk_Tree_Iter;
439
Cursor := Iter_Map.Find (Categories, Name);
441
if Cursor = No_Element then
446
Category => Category,
448
Constructs.Current.Category_Name,
449
Parent_Iter => Node);
450
Insert (Categories, Name, New_Iter);
453
New_Iter := Element (Cursor);
456
N := Append_Entity_Node
457
(Model, File_Name, Constructs.Current.all, New_Iter);
460
Node_Appended := True;
464
Constructs.Current := Constructs.Current.Next;
467
-- If no node was appended, add a "no entity" node
469
if not Node_Appended then
470
Append (Model, Iter, Node);
471
Set (Model, Iter, Display_Name_Column,
472
"<span foreground=""#555555"">"
475
Set (Model, Iter, Node_Type_Column,
476
Gint (Node_Types'Pos (Category_Node)));
481
Trace (Me, "No known language for " & Display_Full_Name (File_Name));
485
end Append_File_Info;
487
---------------------
488
-- Filter_Category --
489
---------------------
491
function Filter_Category
492
(Category : Language_Category) return Language_Category is
494
-- No "with", "use", "#include"
495
-- No constructs ("loop", "if", ...)
497
if Category in Dependency_Category
498
or else Category in Construct_Category
499
or else Category = Cat_Representation_Clause
500
or else Category = Cat_Local_Variable
504
-- All subprograms are grouped together
506
elsif Category in Subprogram_Explorer_Category then
507
return Cat_Procedure;
509
elsif Category in Type_Category then
521
overriding function Dnd_Data
522
(Child : access MDI_Explorer_Child_Record; Copy : Boolean)
523
return Gtkada.MDI.MDI_Child
527
if Child.Dnd_From_File = GNATCOLL.VFS.No_File then
528
-- So that we can move the explorer itself
529
return MDI_Child (Child);
533
C := Find_MDI_Child_By_Name
534
(Get_MDI (Child.Kernel),
535
Display_Full_Name (Child.Dnd_From_File));
538
if Copy and then C /= null then
539
return Dnd_Data (C, Copy => True);
542
(Child.Kernel, Child.Dnd_From_File, Line => 0, Column => 0);
545
return Get_Focus_Child (Get_MDI (Child.Kernel));
549
-------------------------
550
-- Child_Drag_Finished --
551
-------------------------
553
overriding procedure Child_Drag_Finished
554
(Child : access MDI_Explorer_Child_Record) is
556
-- So that we can also move the explorer itself
557
Child.Dnd_From_File := GNATCOLL.VFS.No_File;
558
end Child_Drag_Finished;
560
---------------------
561
-- On_Button_Press --
562
---------------------
564
function On_Button_Press
565
(Kernel : Kernel_Handle;
566
Child : access MDI_Explorer_Child_Record'Class;
567
Tree : access Gtk_Tree_View_Record'Class;
568
Model : Gtk_Tree_Store;
570
Add_Dummy : Boolean) return Boolean
572
Iter : Gtk_Tree_Iter;
573
Path : Gtk_Tree_Path;
576
if Get_Button (Event) = 1 then
577
Iter := Find_Iter_For_Event (Tree, Model, Event);
579
if Iter /= Null_Iter then
580
Path := Get_Path (Model, Iter);
581
Set_Cursor (Tree, Path, null, False);
585
(Integer (Get_Int (Model, Iter, Node_Type_Column)))
588
when Directory_Node | Project_Node | Category_Node =>
589
Cancel_Child_Drag (Child);
591
if Get_Event_Type (Event) = Gdk_2button_Press then
593
Path : Gtk_Tree_Path;
595
pragma Unreferenced (Success);
597
Path := Get_Path (Model, Iter);
599
if Row_Expanded (Tree, Path) then
600
Success := Collapse_Row (Tree, Path);
604
Append_Dummy_Iter (Model, Iter);
607
Success := Expand_Row (Tree, Path, False);
617
if Get_Event_Type (Event) = Gdk_2button_Press
618
or else Get_Event_Type (Event) = Gdk_3button_Press
620
Cancel_Child_Drag (Child);
623
Get_File (Model, Iter, File_Column),
628
elsif Get_Event_Type (Event) = Button_Press then
629
-- Drag-and-drop does not work on floating MDI children
631
if Get_State (Child) /= Floating then
632
Child.Kernel := Kernel;
633
Child.Dnd_From_File :=
634
Get_File (Model, Iter, File_Column);
636
Child_Drag_Begin (Child, Event);
641
Cancel_Child_Drag (Child);
645
Cancel_Child_Drag (Child);
647
if Get_Event_Type (Event) = Button_Release then
648
Line := Get_Int (Model, Iter, Line_Column);
649
Column := Get_Int (Model, Iter, Column_Column);
653
Get_File (Model, Iter, File_Column),
654
Line => Natural (Line),
655
Column => Visible_Column_Type (Column));
660
Cancel_Child_Drag (Child);
674
function On_Key_Press
675
(Kernel : Kernel_Handle;
676
Tree : access Gtk_Tree_View_Record'Class;
677
Event : Gdk_Event) return Boolean
679
use type Gdk.Types.Gdk_Key_Type;
681
Iter : Gtk_Tree_Iter;
683
Model : Gtk_Tree_Model;
686
Get_Selected (Get_Selection (Tree), Model, Iter);
688
if Iter = Null_Iter then
692
if Get_Key_Val (Event) = GDK_Return then
694
(Integer (Get_Int (Model, Iter, Node_Type_Column))) is
699
Get_File (Model, Iter, File_Column),
704
Line := Get_Int (Model, Iter, Line_Column);
705
Column := Get_Int (Model, Iter, Column_Column);
709
Get_File (Model, Iter, File_Column),
710
Line => Natural (Line),
711
Column => Visible_Column_Type (Column));
725
function Get_Node_Type
726
(Model : Gtk_Tree_Store;
727
Node : Gtk_Tree_Iter) return Node_Types is
731
(Integer (Get_Int (Model, Node, Node_Type_Column)));
738
procedure Set_Node_Type
739
(Model : Gtk_Tree_Store;
740
Node : Gtk_Tree_Iter;
742
Expanded : Boolean) is
744
Set (Model, Node, Node_Type_Column, Gint (Node_Types'Pos (N_Type)));
746
if N_Type not in Category_Node .. Entity_Node then
748
Set (Model, Node, Icon_Column,
749
Glib.Object.GObject (Open_Pixbufs (N_Type)));
751
Set (Model, Node, Icon_Column,
752
Glib.Object.GObject (Close_Pixbufs (N_Type)));
757
-----------------------
758
-- Get_Category_Type --
759
-----------------------
761
function Get_Category_Type
762
(Model : Gtk_Tree_Store;
763
Node : Gtk_Tree_Iter) return Language_Category is
766
Language_Category'Val
767
(Integer (Get_Int (Model, Node, Category_Column)));
768
end Get_Category_Type;
774
function Is_Up_To_Date
775
(Model : Gtk_Tree_Store;
776
Node : Gtk_Tree_Iter) return Boolean is
778
case Get_Node_Type (Model, Node) is
781
File : constant Virtual_File :=
782
Get_File (Model, Node, File_Column);
784
return Duration (Get_Int (Model, Node, Timestamp_Column)) +
785
GNATCOLL.Utils.No_Time =
786
File_Time_Stamp (File);
790
return Get_Boolean (Model, Node, Up_To_Date_Column);
798
procedure Set_Up_To_Date
799
(Model : Gtk_Tree_Store;
800
Node : Gtk_Tree_Iter;
803
Set (Model, Node, Up_To_Date_Column, State);
810
function Get_Base_Name
811
(Model : Gtk_Tree_Store;
812
Node : Gtk_Tree_Iter) return Filesystem_String is
814
return Get_File (Model, Node, File_Column).Base_Name;
817
-----------------------
818
-- Get_Absolute_Name --
819
-----------------------
821
function Get_Absolute_Name
822
(Model : Gtk_Tree_Store;
823
Node : Gtk_Tree_Iter) return Virtual_File is
825
return Get_File (Model, Node, File_Column);
826
end Get_Absolute_Name;
828
------------------------
829
-- Get_File_From_Node --
830
------------------------
832
function Get_File_From_Node
833
(Model : Gtk_Tree_Store;
834
Node : Gtk_Tree_Iter) return GNATCOLL.VFS.Virtual_File is
836
return Get_File (Model, Node, File_Column);
837
end Get_File_From_Node;
839
-----------------------------
840
-- Get_Directory_From_Node --
841
-----------------------------
843
function Get_Directory_From_Node
844
(Model : Gtk_Tree_Store;
845
Node : Gtk_Tree_Iter) return Virtual_File
847
F : constant Virtual_File := Get_File (Model, Node, File_Column);
849
if F = GNATCOLL.VFS.No_File then
853
if Get_Node_Type (Model, Node) = Directory_Node then
858
end Get_Directory_From_Node;
860
---------------------------
861
-- Get_Project_From_Node --
862
---------------------------
864
function Get_Project_From_Node
865
(Model : Gtk_Tree_Store;
866
Kernel : access GPS.Kernel.Kernel_Handle_Record'Class;
867
Node : Gtk_Tree_Iter;
868
Importing : Boolean) return Project_Type
870
Parent_Iter : Gtk_Tree_Iter;
871
Node_Type : Node_Types;
872
Project : Project_Type;
875
Parent_Iter := Parent (Model, Node);
877
if Parent_Iter = Null_Iter then
878
return Get_Project (Kernel);
885
while Parent_Iter /= Null_Iter loop
886
Node_Type := Get_Node_Type (Model, Parent_Iter);
888
exit when Node_Type = Project_Node
889
or else Node_Type = Extends_Project_Node
890
or else Node_Type = Modified_Project_Node;
892
Parent_Iter := Parent (Model, Parent_Iter);
895
if Parent_Iter /= Null_Iter then
897
N : constant String :=
898
Get_String (Model, Parent_Iter, Project_Column);
901
"Get_Project_From_Node: no project found");
902
Project := Get_Registry (Kernel).Tree.Project_From_Name (N);
906
-- Should we fall back on Get_Project_From_File ?
907
Project := No_Project;
914
Trace (Exception_Handle, E);
916
end Get_Project_From_Node;
918
---------------------
919
-- Context_Factory --
920
---------------------
922
procedure Context_Factory
923
(Context : in out Selection_Context;
924
Kernel : Kernel_Handle;
925
Tree : access Gtk_Tree_View_Record'Class;
926
Model : Gtk_Tree_Store;
930
pragma Unreferenced (Menu);
932
function Entity_Base (Name : String) return String;
933
-- Return the "basename" for the entity, ie convert "parent.name" to
934
-- "name", in the case of Ada parent packages.
935
-- ??? Should this be done by the parser itself
941
function Entity_Base (Name : String) return String is
943
-- ??? Should use standard UTF8 subprogams
944
for C in reverse Name'Range loop
945
if Name (C) = '.' then
946
return Name (C + 1 .. Name'Last);
952
Iter : constant Gtk_Tree_Iter :=
953
Find_Iter_For_Event (Tree, Model, Event);
954
Node_Type : Node_Types;
958
if Iter /= Null_Iter then
959
Node_Type := Get_Node_Type (Model, Iter);
964
if Node_Type = Entity_Node then
965
Set_Entity_Information
967
Entity_Name => Kernel.Symbols.Find
968
(Entity_Base (Get_String (Model, Iter, Entity_Base_Column))),
969
Entity_Column => Visible_Column_Type
970
(Get_Int (Model, Iter, Column_Column)));
971
L := Integer (Get_Int (Model, Iter, Line_Column));
974
if Node_Type = Project_Node
975
or else Node_Type = Extends_Project_Node
976
or else Node_Type = Modified_Project_Node
981
Get_Project_From_Node (Model, Kernel, Iter, False),
983
Get_Project_From_Node (Model, Kernel, Iter, True));
988
Files => (1 => Get_File_From_Node (Model, Iter)),
990
Get_Project_From_Node (Model, Kernel, Iter, False),
992
Get_Project_From_Node (Model, Kernel, Iter, True),
997
end Project_Explorers_Common;