Toc Gallery Index Tree Gtkada.Canvas

Screenshot

No screeshot

Hierarchy

Description

This package provides an interactive canvas, on which the user can put items, move them with the mouse, etc. The items can be connected together, and the connections remain active while the items are moved.

It also supports scrolling if put in a Gtk_Scrolled_Window. The canvas will be scrolled (and the selected items moved) if an item is selected and the mouse is dragged on a small area on the side of the canvas or even directly outside of the canvas. Scrolling will continue until the mouse is either released or moved back inside the canvas.

The scrolling speed will slightly increase over time if the mouse is kept outside of the canvas. This makes the canvas much more comfortable to use for the user.

All items put in this canvas must inherit from the type Canvas_Item_Record. However, it is your responsability, as a programmer, to provide drawing routines. In fact, all these items should draw in a pixmap, which is then copied automatically to the screen whenever the canvas needs to redraw itself.

The items can also react to mouse events: mouse clicks are transmitted to the item if the mouse did not move more than a given amount of pixels. To decide what their reaction should be, you should override the On_Button_Click subprogram.

This canvas is not intended for cases where you want to put hundreds of items on the screen. For instance, it does not provide any smart double-buffering other than the one provided by gtk+ itself, and thus you would get some flicker if there are too many items.

There are three coordinate systems used by widget. All the subprograms expect a specific coordinate system as input or output. Here are the three systems: - World coordinates The position of an item is reported in pixels, as if the canvas currently had a zoom level of 100%. This is fully independent, at any time, from the current zoom level of the canvas. Since the canvas is considered to expand ad infinitum, the top-left corner doesn't have any specific fixed coordinates. It can be known by checking the current lower value of the adjustments (aka scrollbars).

- Canvas coordinates This is similar to world coordinates, except these depend on the current zoom level of the canvas. This also affect the width and height of the objects in the canvas. The subprograms To_Canvas_Coordinates and To_World_Coordinates can be used to convert lengths from world to canvas coordinates. The same behavior as world coordinates applies for the top-left corner. All drawing to the screen, in particular for Draw_Background, must be done using this coordinate systems

- Item coordinates The position of a point is relative to the top-left corner of the current item. This corner therefore has coordinates (0, 0). This coordinate systems assumes a zoom-level of 100%

Items are selected automatically when they are clicked. If Control is pressed at the same time, multiple items can be selected. If the background is clicked (and control is not pressed), then all items are unselected. Pressing and dragging the mouse in the backgroudn draws a virtual box on the screen. All the items fully included in this box when it is released will be selected (this will replace the current selection if Control was not pressed).

Types

  • type Arrow_Type is (No_Arrow, -- the link does not have an arrow Start_Arrow, -- the link has an arrow at its beginning End_Arrow, -- the link has an arrow at the end Both_Arrow -- the link has an arrow on both sides );
    Indicate whether the links have an arrow or not.
  • type Buffered_Item is access all Buffered_Item_Record'Class;
    A widget that has a double-buffer associated. You should use this one when drawing items can take a long time, or you do not want to handle the zoom yourself. You only need to update the contents of the double pixmap when the contents of the item changes, since all the drawing and zooming is taken care of automatically. Once the drawing is done, call Item_Updated to force the canvas to refresh the screen. This buffered_item is meant to handle rectangular items. However, it can be used for polygonal items by overriding Draw. The new version should set the clip mask for the GC, then call Draw for the buffered item, and finally reset the clip mask. The clip mask must take into account the current zoom level.
  • type Buffered_Item_Record is new Canvas_Item_Record with private;
  • type Canvas_Item is access all Canvas_Item_Record'Class;
    An item that can be put on the canvas. This is an abstract type, as it does not provide any default drawing routine. You must override the abstract Draw subprogram.
  • type Canvas_Item_Record is abstract new Glib.Graphs.Vertex with private;
  • type Canvas_Link is access all Canvas_Link_Record'Class;
  • type Canvas_Link_Access is access all Canvas_Link_Record;
    A link between two items in the canvas. The implementation provided in this package provides links that can be either straight links or curved links. This type is provided as a tagged type so that you can associated your own user data with it.
  • type Canvas_Link_Record is new Glib.Graphs.Edge with private;
  • type Interactive_Canvas is access all Interactive_Canvas_Record'Class;
    A canvas on which items are put. Each item can be moved interactively by the user, and links can be drawn automatically from an item to another. This widget can be inserted directly in a scrolled window to provide support for scrolling.
  • type Interactive_Canvas_Record is new Gtk.Drawing_Area.Gtk_Drawing_Area_Record with private;
  • type Item_Iterator is private;
  • type Item_Processor is access function (Canvas : access Interactive_Canvas_Record'Class; Item : access Canvas_Item_Record'Class) return Boolean;
  • type Item_Side is (East, West, North, South);
    Each side of an item, along its rectangle bounding box
  • type Layout_Algorithm is access procedure (Canvas : access Interactive_Canvas_Record'Class; Graph : Glib.Graphs.Graph; Force : Boolean; Vertical_Layout : Boolean);
    A general layout algorithm. It should compute the position of all the vertices of the graph, and set them directly in the graph itself. Note: all the vertices in the graph are of type Canvas_Item_Record'Class and you should use that to set the coordinates through a call to Move_To.

    Algorithms are encouraged to preserve the current layout as much as possible, taking into account items that have been moved manually by the user, so that the latter can preserver his mental map of the graph. However, if Force is set to True, then the whole layout should be recomputed as if all items had just been inserted.

    Items that have just been inserted in the graph, but whose position has never been computed, are set at coordinates (Gint'First, Gint'First). Check the result of Get_Coord.

    This function doesn't need to align items, this is done automatically by the canvas if necessary.

  • type Link_Processor is access function (Canvas : access Interactive_Canvas_Record'Class; Link : access Canvas_Link_Record'Class) return Boolean;
  • type Selection_Iterator is private;

Subprograms

    Creating a canvas

  • procedure Gtk_New (Canvas : out Interactive_Canvas; Auto_Layout : Boolean := True);
    Create a new empty Canvas. If Auto_Layout is True, then the items are automatically positioned as they are put in the canvas, if no coordinates are specified.
  • procedure Initialize (Canvas : access Interactive_Canvas_Record'Class; Auto_Layout : Boolean := True);
    Internal function used to initialize the canvas.
  • procedure Configure (Canvas : access Interactive_Canvas_Record; Grid_Size : Glib.Guint := Default_Grid_Size; Annotation_Font : Pango.Font.Pango_Font_Description := Pango.Font.From_String (Default_Annotation_Font);
  • function Get_Vadj (Canvas : access Interactive_Canvas_Record'Class) return Gtk.Adjustment.Gtk_Adjustment;
    Return the vertical adjustment associated with Canvas
  • function Get_Hadj (Canvas : access Interactive_Canvas_Record'Class) return Gtk.Adjustment.Gtk_Adjustment;
    Return the horizontal adjustment associated with Canvas
  • procedure Draw_Area (Canvas : access Interactive_Canvas_Record'Class; Rect : Gdk.Rectangle.Gdk_Rectangle);
    Draw in Canvas the specified area.
  • procedure Draw_Background (Canvas : access Interactive_Canvas_Record; Screen_Rect : Gdk.Rectangle.Gdk_Rectangle);
    Draw the background of the canvas. This procedure should be overriden if you want to draw something else on the background. It must first clear the area on the screen.

    Screen_Rect is the rectangle on the screen that needs to be refreshed. These are canvas coordinates, therefore you must take into account the current zoom level while drawing.

    The default implementation draws a grid.

    An example implementation that draws a background image is shown at the end of this file.

  • procedure Draw_Grid (Canvas : access Interactive_Canvas_Record; GC : Gdk.GC.Gdk_GC; Screen_Rect : Gdk.Rectangle.Gdk_Rectangle);
    Helper function that can be called from Draw_Background. It cannot be used directly as Draw_Background, since it doesn't clear the area first.
  • procedure Set_Orthogonal_Links (Canvas : access Interactive_Canvas_Record; Orthogonal : Boolean);
    If Orthogonal is True, then all the links will be drawn only with vertical and horizontal lines. This is not applied for the second or more link between two items.
  • function Get_Orthogonal_Links (Canvas : access Interactive_Canvas_Record) return Boolean;
    Return True if the links are only drawn horizontally and vertically.
  • procedure Align_On_Grid (Canvas : access Interactive_Canvas_Record; Align : Boolean := True);
    Choose whether the items should be aligned on the grid when moved. Existing items are not moved even if you set this parameter to True, this will only take effect the next time the items are moved.
  • function Get_Align_On_Grid (Canvas : access Interactive_Canvas_Record) return Boolean;
    Return True if items are currently aligned on grid.
  • procedure Move_To (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class; X, Y : Glib.Gint := Glib.Gint'First);
    Move the item in the canvas, to world coordinates (X, Y). Item is assumed to be already in the canvas. If you leave both coordinates X and Y to their default value, then the item's location will be automatically computed when you layout the canvas (it is your responsability to call Layout).
  • procedure Set_Items (Canvas : access Interactive_Canvas_Record; Items : Glib.Graphs.Graph);
    Set the items and links to display in the canvas from Items. All items previously in the canvas are removed, and replaced by the vertices in Items. Note that the vertices in Items must be in Canvas_Item_Record'Class, and the links must be in Canvas_Link_Record'Class. If you do not have an automatic layout set up in Canvas, you need to set the coordinates of all the vertices by calling Move_To separately.

    You mustn't destroy items yourself, this is done automatically when the canvas is destroyed.

  • procedure Put (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class; X, Y : Glib.Gint := Glib.Gint'First);
    Add a new item to the canvas, at world coordinates (X, Y). The item is added at a specific location. If you leave both X and Y to their default value, the item's location will be computed automatically when you call Layout on the canvas, unless Auto_Layout has been set, in which case the position will be computed immediately.
  • function Item_At_Coordinates (Canvas : access Interactive_Canvas_Record; X, Y : Glib.Gint) return Canvas_Item;
    Return the item at world coordinates (X, Y) which is on top of all others. null is returned if there is no such item.
  • function Item_At_Coordinates (Canvas : access Interactive_Canvas_Record; Event : Gdk.Event.Gdk_Event) return Canvas_Item;
    Same as above, but using the canvas coordinates of the event, taking into account the current zoom level and current scrolling
  • procedure Clear (Canvas : access Interactive_Canvas_Record);
    Remove all items from the canvas
  • procedure Remove (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class);
    Remove an item and all the links to and from it from the canvas. The item itself is not freed, but the links are. Nothing is done if the item is not part of the canvas.
  • procedure Item_Updated (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class);
    This should be called when Item has changed the contents of its pixmap, and thus the Canvas should be updated.
  • procedure Refresh_Canvas (Canvas : access Interactive_Canvas_Record);
    Redraw the whole canvas (both in the double buffer and on the screen).
  • procedure Raise_Item (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class);
    Raise the item so that it is displayed on top of all the others The canvas is refreshed as needed to reflect the change. Nothing happens if Item is not part of the canvas.
  • procedure Lower_Item (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class);
    Lower the item so that it is displayed below all the others. The canvas is refreshed as needed to reflect the change. Nothing happens if Item is not part of the canvas.
  • function Is_On_Top (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class) return Boolean;
    Return True if Item is displayed on top of all the others in the canvas.
  • procedure Show_Item (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class);
    Scroll the canvas so that Item is visible. Nothing is done if the item is already visible
  • procedure Align_Item (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class; X_Align : Float := 0.5; Y_Align : Float := 0.5);
    Scroll the canvas so that the Item appears at the given location in the canvas. If X_Align is 0.0, the item is align on the left. With 0.5, it is centered horizontally. If 1.0, it is aligned on the right.
  • function Get_Arrow_Angle (Canvas : access Interactive_Canvas_Record'Class) return Float;
    Return the angle of arrows in the canvas.
  • function Get_Arrow_Length (Canvas : access Interactive_Canvas_Record'Class) return Glib.Gint;
    Return the length of arrows in the canvas.
  • Iterating over items

  • procedure For_Each_Item (Canvas : access Interactive_Canvas_Record; Execute : Item_Processor; Linked_From_Or_To : Canvas_Item := null);
    Execute an action on each of the items contained in the canvas. If Execute returns False, we stop traversing the list of children. It is safe to remove the items in Item_Processor.

    If Linked_From_Or_To is not null, then only the items linked to this one will be processed. It is possible that a given item will be returned twice, if it is both linked to and from the item.

  • function Start (Canvas : access Interactive_Canvas_Record; Linked_From_Or_To : Canvas_Item := null) return Item_Iterator;
    Return the first item in the canvas. The same restriction as above applies if Linked_From_Or_To is not null.
  • procedure Next (Iter : in out Item_Iterator);
    Move the iterator to the next item. All items will eventually be returned if you do not add new items during the iteration and none are removed. However, it is safe to remove items at any time, except the current item
  • function Get (Iter : Item_Iterator) return Canvas_Item;
    Return the item pointed to by the iterator. null is returned when there are no more item in the canvas.
  • function Is_Linked_From (Iter : Item_Iterator) return Boolean;
    Return True if there is a link from: Get (Iter) -> Linked_From_Or_To Linked_From_Or_To is the item passed to Start. False is returned if this item was null.
  • Zooming

  • procedure Zoom (Canvas : access Interactive_Canvas_Record; Percent : Glib.Guint := 100; Steps : Glib.Guint := 1);
    Zoom in or out in the canvas.

    Steps is the number of successive zooms that will be done to provide smooth scrolling.

    Note that one possible use for this function is to refresh the canvas and emit the "zoomed" signal, which might redraw all the items. This can be accomplished by keeping the default 100 value for Percent.

  • function Get_Zoom (Canvas : access Interactive_Canvas_Record) return Glib.Guint;
    Return the current zoom level
  • function To_Canvas_Coordinates (Canvas : access Interactive_Canvas_Record'Class; X : Glib.Gint) return Glib.Gint;
    Scale the scalar X depending by the zoom level (map from world coordinates to canvas coordinates). Substract the coordinates of the top-left corner if you are converting coordinates instead of lengths.
  • function Top_World_Coordinates (Canvas : access Interactive_Canvas_Record'Class) return Glib.Gint;
    Return the world coordinates for the y=0 canvas coordinates (ie for the upper-left corner).
  • function Left_World_Coordinates (Canvas : access Interactive_Canvas_Record'Class) return Glib.Gint;
    Return the world coordinates for the x=0 canvas coordinates (ie for the upper-left corner).
  • function To_World_Coordinates (Canvas : access Interactive_Canvas_Record'Class; X : Glib.Gint) return Glib.Gint;
    Scale the scalar X depending by the zoom level (map from canvas coordinates to world coordinates)
  • procedure Get_World_Coordinates (Canvas : access Interactive_Canvas_Record'Class; X, Y : out Glib.Gint; Width : out Glib.Gint; Height : out Glib.Gint);
    Return the world coordinates of Canvas.
  • Layout of items

  • procedure Set_Layout_Algorithm (Canvas : access Interactive_Canvas_Record; Algorithm : Layout_Algorithm);
    Set the layout algorithm to use to compute the position of the items. Algorithm mustn't be null.
  • procedure Default_Layout_Algorithm (Canvas : access Interactive_Canvas_Record'Class; Graph : Glib.Graphs.Graph; Force : Boolean; Vertical_Layout : Boolean);
    The default algorithm used in the canvas. Basically, items are put next to each other, unless there is a link between two items. In that case, the second item is put below the first, as space allows.
  • procedure Set_Auto_Layout (Canvas : access Interactive_Canvas_Record; Auto_Layout : Boolean);
    If Auto_Layout is true, then every time an item is inserted in the canvas, the layout algorithm is called. If set to False, it is the responsability of the caller to call Layout below to force a recomputation of the layout, preferably after inserting a number of items.
  • procedure Set_Layout_Orientation (Canvas : access Interactive_Canvas_Record; Vertical_Layout : Boolean := False);
    Specify the layout orientation to use for this canvas. The setting is passed as a parameter to the layout algorithm
  • procedure Layout (Canvas : access Interactive_Canvas_Record; Force : Boolean := False);
    Recompute the layout of the canvas. Force can be used to control the layout algorithm, as described above for Layout_Algorithm.
  • Links

  • procedure Configure (Link : access Canvas_Link_Record; Arrow : in Arrow_Type := End_Arrow; Descr : in Glib.UTF8_String := "");
    Configure a link. The link is an oriented bound between two items on the canvas. If Descr is not the empty string, it will be displayed in the middle of the link, and should indicate what the link means. Arrow indicates whether some arrows should be printed as well.
  • function Get_Descr (Link : access Canvas_Link_Record) return Glib.UTF8_String;
    Return the description for the link, or "" if there is none
  • function Get_Arrow_Type (Link : access Canvas_Link_Record) return Arrow_Type;
    Return the location of the arrows on Link
  • procedure Set_Src_Pos (Link : access Canvas_Link_Record; X_Pos, Y_Pos : Glib.Gfloat := 0.5);
    Set the position of the link's attachment in its source item. X_Pos and Y_Pos should be given between 0.0 and 1.0 (from left to right or top to bottom).. By default, all links are considered to be attached to the center of items. However, in some cases it is more convenient to attach it to a specific part of the item. For instance, you can force a link to always start from the top of the item by setting Y_Pos to 0.0.
  • procedure Set_Dest_Pos (Link : access Canvas_Link_Record; X_Pos, Y_Pos : Glib.Gfloat := 0.5);
    Same as Set_Src_Pos for the destination item
  • procedure Get_Src_Pos (Link : access Canvas_Link_Record; X, Y : out Glib.Gfloat);
    Return the attachment position of the link along its source item
  • procedure Get_Dest_Pos (Link : access Canvas_Link_Record; X, Y : out Glib.Gfloat);
    Return the attachment position of the link along its destination item
  • function Has_Link (Canvas : access Interactive_Canvas_Record; From, To : access Canvas_Item_Record'Class; Name : Glib.UTF8_String := "") return Boolean;
    Test whether there is a link from From to To, with the same name. If Name is the empty string "", then no check is done on the name, and True if returned if there is any link between the two items.
  • procedure Add_Link (Canvas : access Interactive_Canvas_Record; Link : access Canvas_Link_Record'Class; Src : access Canvas_Item_Record'Class; Dest : access Canvas_Item_Record'Class; Arrow : in Arrow_Type := End_Arrow; Descr : in Glib.UTF8_String := "");
    Add Link in the canvas. This connects the two items Src and Dest. Simpler procedure to add a standard link. This takes care of memory allocation, as well as adding the link to the canvas.
  • procedure Remove_Link (Canvas : access Interactive_Canvas_Record; Link : access Canvas_Link_Record'Class);
    Remove a link from the canvas. It also destroys the link itself, and free the memory allocated to it. Nothing is done if Link does not belong to canvas.
  • procedure For_Each_Link (Canvas : access Interactive_Canvas_Record; Execute : Link_Processor; From, To : Canvas_Item := null);
    Execute an action on each of the links contained in the canvas. If Execute returns False, we stop traversing the list of links. It is safe to remove the link from the list in Link_Processor.

    (From, To) can be used to limit what links are looked for.

    ??? Would be nicer to give direct access to the Graph iterators

  • procedure Destroy (Link : in out Canvas_Link_Record);
    Method called every time a link is destroyed. You should override this if you define your own link types. Note that the link might already have been removed from the canvas when this subprogram is called. This shouldn't free the link itself, only its fields.
  • Drawing links

    Drawing of links can be controlled at several levels: - Redefining Update_Links gives control at the canvas level. This can be used to implement routing algorithms for the links where the routes must be computed before any link is actually drawn (otherwise it is better to redefine Draw_Link). It can also be used to control in what order the links should be drawn. - Redefining Draw_Link gives the opportunity to draw links any way you need (several bends, ...). It can be used to control the routing of this specific link, for routing algorithms that only rely on the items layout and not on other links. Otherwise see Update_Links. - Redefining Draw_Straight_Line if slightly lower-level. This is called by the default Draw_Link procedure, once the ends of the links have been computed.
  • procedure Update_Links (Canvas : access Interactive_Canvas_Record; GC : Gdk.GC.Gdk_GC; Invert_Mode : Boolean; From_Selection : Boolean);
    Redraw all the links in the canvas, after the items have been laid out. GC is a default graphic context that can be used for drawing. However, any other graphic context will do. If Invert_Mode is true, this graphic context must draw in xor mode. If From_Selection is true, then only the links to or from one of the selected items need to be drawn.
  • procedure Draw_Link (Canvas : access Interactive_Canvas_Record'Class; Link : access Canvas_Link_Record; Invert_Mode : Boolean; GC : Gdk.GC.Gdk_GC; Edge_Number : Glib.Gint);
    Redraw the link on the canvas. Note that this is a primitive procedure of Link, not of Canvas, and thus can easily be overrided for specific links. The default version draws either straight or arc links (the latter when there are multiple links between two given items). This function shouldn't be called if one of the two ends of the link is invisible.

    The link should be drawn directly in Get_Window (Canvas).

    GC is a possible graphic context that could be used to draw the link. You shouldn't destroy it or modify its attributes. However, you can use any other graphic context specific to your application, for instance if you want to draw the link in various colors or shapes. The graphic context you use must be in Invert mode (see Gdk.GC.Set_Function) if and only if Invert_Mode is true, so that when items are moved on the canvas, the links properly follow the items they are attached to. This graphic context is only used to draw links, so you don't need to restore it on exit if your Draw_Link function always sets it at the beginning.

    Edge_Number indicates the index of link in the list of links that join the same source to the same destination. It should be used so that two links do not overlap (for instance, the default is to draw the first link straight, and the others as arcs).

  • procedure Clip_Line (Src : access Canvas_Item_Record; To_X : Glib.Gint; To_Y : Glib.Gint; X_Pos : Glib.Gfloat; Y_Pos : Glib.Gfloat; Side : out Item_Side; X_Out : out Glib.Gint; Y_Out : out Glib.Gint);
    Clip the line that goes from Src at pos (X_Pos, Y_Pos) to (To_X, To_Y) in world coordinates. The intersection between that line and the border of Rect is returned in (X_Out, Y_Out). The result should be in world coordinates. X_Pos and Y_Pos have the same meaning as Src_X_Pos and Src_Y_Pos in the link record. This procedure is called when computing the position for the links within the default Draw_Link procedure. The default implementation only works with rectangular items. The computed coordinates are then passed on directly to Draw_Straight_Line.
  • procedure Draw_Straight_Line (Link : access Canvas_Link_Record; Window : Gdk.Window.Gdk_Window; GC : Gdk.GC.Gdk_GC; Src_Side : Item_Side; X1, Y1 : Glib.Gint; Dest_Side : Item_Side; X2, Y2 : Glib.Gint);
    Draw a straight link between two points. This could be overriden if you need to draw an something along the link. The links goes from (Src, X1, Y1) to (Dest, X2, Y2), in canvas coordinates. The coordinates have already been clipped so that they do not override the item.
  • Selection

  • procedure Clear_Selection (Canvas : access Interactive_Canvas_Record);
    Clear the list of currently selected items.
  • procedure Add_To_Selection (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class);
    Add Item to the selection. This is only meaningful during a drag operation (ie during a button press and the matching button release). Item will be moved at the same time that the selection is moved. Item is not added again if it is already in the selection. This function can be called from the Button_Click subprogram to force moving items. This emits the "item_selected" signal.
  • procedure Remove_From_Selection (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class);
    Remove Item from the selection. This emits the "item_unselected" signal.
  • procedure Select_All (Canvas : access Interactive_Canvas_Record);
    Select all the Item in the canvas.
  • function Is_Selected (Canvas : access Interactive_Canvas_Record; Item : access Canvas_Item_Record'Class) return Boolean;
    Return True if the item is currently selected
  • function Start (Canvas : access Interactive_Canvas_Record) return Selection_Iterator;
    Return the first selected item
  • function Next (Iterator : Selection_Iterator) return Selection_Iterator;
    Move to the next selected item
  • function Get (Iterator : Selection_Iterator) return Canvas_Item;
    Return the current item, or null if there is no more selected item.
  • Items manipulation

  • procedure Selected (Item : access Canvas_Item_Record; Canvas : access Interactive_Canvas_Record'Class; Is_Selected : Boolean);
    Called when the item is selected or unselected. The default is to do nothing.
  • function Point_In_Item (Item : access Canvas_Item_Record; X, Y : Glib.Gint) return Boolean;
    This function should return True if (X, Y) is inside the item. X and Y are in world coordinates. This function is meant to be overriden for non-rectangular items, since the default behavior works for rectangular items. This function is never called for invisible items
  • procedure Set_Screen_Size (Item : access Canvas_Item_Record; Width : Glib.Gint; Height : Glib.Gint);
    Set the size of bounding box for the item in world coordinates. The item itself needn't occupy the whole area of this bounding box, see Point_In_Item. You need to redraw the item, and call Item_Updated to force the canvas to refresh the screen.
  • procedure Draw (Item : access Canvas_Item_Record; Canvas : access Interactive_Canvas_Record'Class; GC : Gdk.GC.Gdk_GC; Xdest, Ydest : Glib.Gint) is abstract;
    This subprogram, that must be overridden, should draw the item on Get_Pixmap (Canvas), at the specific location (Xdest, Ydest). The item must also be drawn at the appropriate zoom level. If you need to change the contents of the item, you should call Item_Updated after having done the drawing.
  • procedure Destroy (Item : in out Canvas_Item_Record);
    Free the memory occupied by the item (not the item itself). You should override this function if you define your own widget type, but always call the parent's Destroy subprogram.
  • procedure On_Button_Click (Item : access Canvas_Item_Record; Event : Gdk.Event.Gdk_Event_Button);
    Function called whenever the item was clicked on. Note that this function is not called when the item is moved, and thus is only called when the click was short. The coordinates (X, Y) in the Event are relative to the top-left corner of Item.
  • function Get_Coord (Item : access Canvas_Item_Record) return Gdk.Rectangle.Gdk_Rectangle;
    Return the coordinates and size of the bounding box for item, in world coordinates. If the item has never been resized, it initially has a width and height of 1.
  • procedure Set_Visibility (Item : access Canvas_Item_Record; Visible : Boolean);
    Set the visibility status of the item. An invisible item will not be visible on the screen, and will not take part in the computation of the the scrollbars for the canvas. The canvas is not refreshed (this is your responsibility to do it after you have finished doing all the modifications).
  • function Is_Visible (Item : access Canvas_Item_Record) return Boolean;
    Return True if the item is currently visible
  • function Is_From_Auto_Layout (Item : access Canvas_Item_Record) return Boolean;
    Return True if the current location of the item is the result from the auto layout algorithm. False is returned if the item was moved manually by the user.
  • Buffered items

  • function Pixmap (Item : access Buffered_Item_Record) return Gdk.Pixmap.Gdk_Pixmap;
    Return the double-buffer. All the drawing on this pixmap must be done at zoom level 100%.

Signals

  • background_click
    procedure Handler (Canvas : access Interactive_Canvas_Record'Class; Event : Gdk.Event.Gdk_Event);
    Called every time the user clicks in the background (ie not on an item, or On_Button_Click would be called). This is called both on Button_Release and Button_Press events. The coordinates (X, Y) in the Event are relative to the top-left corner of Canvas.
  • item_moved
    procedure Handler (Canvas : access Interactive_Canvas_Record'Class; Item : Canvas_Item);
    Emitted when Item has been moved. New coordinates have been assigned to Item. However, the canvas hasn't been refreshed yet. This signal might be called multiple time when the user finishes a drag action, in case there were several selected items.
  • item_selected
    procedure Handler (Canvas : access Interactive_Canvas_Record'Class; Item : Canvas_Item);
    Emitted when the user has clicked on an item to select it, ie before any drag even has occured. This is a good time to add other items to the selection if you need. At thee same time, the primitive operation Selected is called for the item.
  • item_unselected
    procedure Handler (Canvas : access Interactive_Canvas_Record'Class; Item : Canvas_Item);
    Emitted when the Item was unselected. At the same time, the primitive operation Selected is called for the item.
  • zoomed
    procedure Handler (Canvas : access Interactive_Canvas_Record'Class);
    Emitted when the canvas has been zoomed in or out. You do not need to redraw the items yourself, since this will be handled by calls to Draw

Example

-- The following example shows a possible Draw_Background procedure, -- that draws a background image on the canvas's background. It fully -- handles zooming and tiling of the image. Note that drawing a large -- image will dramatically slow down the performances. Background : Gdk.Pixbuf.Gdk_Pixbuf := ...; procedure Draw_Background (Canvas : access Image_Canvas_Record; Screen_Rect : Gdk.Rectangle.Gdk_Rectangle) is X_Left : constant Glib.Gint := Left_World_Coordinates (Canvas); Y_Top : constant Glib.Gint := Top_World_Coordinates (Canvas); X, Y, W, H, Ys : Gint; Xs : Gint := Screen_Rect.X; Bw : constant Gint := Get_Width (Background) * Gint (Get_Zoom (Canvas)) / 100; Bh : constant Gint := Get_Height (Background) * Gint (Get_Zoom (Canvas)) / 100; Scaled : Gdk_Pixbuf := Background; begin if Get_Zoom (Canvas) /= 100 then Scaled := Scale_Simple (Background, Bw, Bh); end if; while Xs < Screen_Rect.X + Screen_Rect.Width loop Ys := Screen_Rect.Y; X := (X_Left + Xs) mod Bw; W := Gint'Min (Screen_Rect.Width + Screen_Rect.X- Xs, Bw - X); while Ys < Screen_Rect.Y + Screen_Rect.Height loop Y := (Y_Top + Ys) mod Bh; H := Gint'Min (Screen_Rect.Height + Screen_Rect.Y - Ys, Bh - Y); Render_To_Drawable (Pixbuf => Scaled, Drawable => Get_Window (Canvas), Gc => Get_Black_GC (Get_Style (Canvas)), Src_X => X, Src_Y => Y, Dest_X => Xs, Dest_Y => Ys, Width => W, Height => H); Ys := Ys + H; end loop; Xs := Xs + W; end loop; if Get_Zoom (Canvas) /= 100 then Unref (Scaled); end if; end Draw_Background;

Testgtk source code

This code is part of testgtk, a demo application packaged with GtkAda. Testgtk demonstrates the various widgets of GtkAda
----------------------------------------------------------------------- -- GtkAda - Ada95 binding for the Gimp Toolkit -- -- -- -- Copyright (C) 2000 -- -- Emmanuel Briot, Joel Brobecker and Arnaud Charlet -- -- -- -- This library is free software; you can redistribute it and/or -- -- modify it under the terms of the GNU General Public -- -- License as published by the Free Software Foundation; either -- -- version 2 of the License, or (at your option) any later version. -- -- -- -- This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- -- General Public License for more details. -- -- -- -- You should have received a copy of the GNU General Public -- -- License along with this library; if not, write to the -- -- Free Software Foundation, Inc., 59 Temple Place - Suite 330, -- -- Boston, MA 02111-1307, USA. -- -- -- -- -- -- -- -- -- -- ----------------------------------------------------------------------- with Ada.Numerics.Discrete_Random; with Gdk.Color; use Gdk.Color; with Gdk.Drawable; use Gdk.Drawable; with Gdk.Event; use Gdk.Event; with Gdk.GC; use Gdk.GC; with Gdk.Pixbuf; use Gdk.Pixbuf; with Gdk.Rectangle; use Gdk.Rectangle; with Gdk.Region; use Gdk.Region; with Glib; use Glib; with Glib.Error; use Glib.Error; with Gtk.Arrow; use Gtk.Arrow; with Gtk.Box; use Gtk.Box; with Gtk.Button; use Gtk.Button; with Gtk.Check_Button; use Gtk.Check_Button; with Gtk.Enums; use Gtk.Enums; with Gtk.Frame; use Gtk.Frame; with Gtk.Handlers; use Gtk.Handlers; with Gtk.Scrolled_Window; use Gtk.Scrolled_Window; with Gtk.Widget; use Gtk.Widget; with Gtkada.Canvas; use Gtkada.Canvas; with Gtk.Spin_Button; use Gtk.Spin_Button; with Gtk.Label; use Gtk.Label; with Gtk.Adjustment; use Gtk.Adjustment; with Pango.Layout; use Pango.Layout; with Gtk.Style; use Gtk.Style; package body Create_Canvas is Max_Size : constant := 400; -- Size of the canvas; Item_Width : constant Gint := 50; Item_Height : constant Gint := 40; ---------------------------------------------------------------- -- Redefine our own item type, since we want to provide our own -- graphics. ---------------------------------------------------------------- type Display_Item_Record is new Buffered_Item_Record with record Canvas : Interactive_Canvas; Color : Gdk.Color.Gdk_Color; W, H : Gint; Num : Positive; end record; type Display_Item is access all Display_Item_Record'Class; procedure Initialize (Item : access Display_Item_Record'Class; Canvas : access Interactive_Canvas_Record'Class); -- Initialize Item with a random size and color. -- Canvas must have been realized procedure Draw_To_Double_Buffer (Item : access Display_Item_Record'Class); -- Draw the item to the double-buffer ----------------------------------------------------------- -- A new non-rectangular item, with a hole in the middle -- ----------------------------------------------------------- type Hole_Item_Record is new Display_Item_Record with null record; procedure Draw (Item : access Hole_Item_Record; Canvas : access Gtkada.Canvas.Interactive_Canvas_Record'Class; GC : Gdk.GC.Gdk_GC; Xdest : Glib.Gint; Ydest : Glib.Gint); function Point_In_Item (Item : access Hole_Item_Record; X, Y : Glib.Gint) return Boolean; -- Override the inherited subprograms ---------------------- -- A resizable item -- ---------------------- type Resize_Type is (Bottom, Top, Left, Right); pragma Unreferenced (Top, Left, Right); type Resizable_Item_Record is new Canvas_Item_Record with record Initial : Gdk_Rectangle; Typ : Resize_Type; -- The two fields are set while handling a resize end record; type Resizable_Item is access all Resizable_Item_Record'Class; procedure Draw (Item : access Resizable_Item_Record; Canvas : access Interactive_Canvas_Record'Class; GC : Gdk.GC.Gdk_GC; Xdest, Ydest : Glib.Gint); procedure On_Button_Click (Item : access Resizable_Item_Record; Event : Gdk.Event.Gdk_Event_Button); -- Override the inherited subprograms ---------------------------------------------------- -- Our own canvas, with optional background image -- ---------------------------------------------------- type Image_Canvas_Record is new Interactive_Canvas_Record with record Background : Gdk_Pixbuf; Draw_Grid : Boolean := True; Grid_GC : Gdk_GC; end record; type Image_Canvas is access all Image_Canvas_Record'Class; procedure Draw_Background (Canvas : access Image_Canvas_Record; Screen_Rect : Gdk.Rectangle.Gdk_Rectangle); -- Draw the background image ----------------------------- -- Misc. types and variables ----------------------------- package Canvas_Cb is new Gtk.Handlers.Callback (Interactive_Canvas_Record); package Canvas_User_Cb is new Gtk.Handlers.User_Callback (Gtk_Widget_Record, Image_Canvas); procedure Add_Canvas_Link (Canvas : access Interactive_Canvas_Record'Class; Item1, Item2 : access Canvas_Item_Record'Class; Text : String := ""); -- Add a link between Item1 and Item2 Max_Colors : constant := 20; Zoom_Levels : constant array (Positive range <>) of Guint := (10, 25, 50, 75, 100, 125, 150, 200, 300, 400); Start_Spin, End_Spin, Num_Spin : Gtk_Spin_Button; Num_Items_Label, Num_Links_Label : Gtk_Label; Layout : Pango_Layout; type Color_Type is range 1 .. Max_Colors; package Color_Random is new Ada.Numerics.Discrete_Random (Color_Type); use Color_Random; package Items_Random is new Ada.Numerics.Discrete_Random (Positive); use Items_Random; subtype Coordinate_Type is Gint range Default_Grid_Size + 1 .. Max_Size; package Coordinate_Random is new Ada.Numerics.Discrete_Random (Coordinate_Type); use Coordinate_Random; subtype Zoom_Type is Gint range 1 .. 2; package Zoom_Random is new Ada.Numerics.Discrete_Random (Zoom_Type); use Zoom_Random; type String_Access is access String; Color_Names : constant array (Color_Type) of String_Access := (new String'("forest green"), new String'("red"), new String'("blue"), new String'("yellow"), new String'("peach puff"), new String'("azure"), new String'("seashell"), new String'("lavender"), new String'("grey"), new String'("turquoise"), new String'("khaki"), new String'("tan"), new String'("orange red"), new String'("MediumPurple"), new String'("ivory1"), new String'("DeepSkyBlue1"), new String'("burlywood1"), new String'("wheat1"), new String'("orange1"), new String'("pink")); Colors : array (Color_Type) of Gdk_Color; Items_List : array (1 .. 500) of Canvas_Item; Last_Item : Positive; Last_Link : Positive; Green_Gc : Gdk.GC.Gdk_GC; Item_Gen : Items_Random.Generator; Gen : Coordinate_Random.Generator; Color_Gen : Color_Random.Generator; Zoom_Gen : Zoom_Random.Generator; -- Note: All the generators above are intentionally not reset, so that -- we can get the same events every time and thus can reproduce behaviors. ---------- -- Help -- ---------- function Help return String is begin return "An @bInteractive_Canvas@B is an interactive widgets, on which" & " you can put items that the user will be able to manipulate" & " dynamically with the mouse." & ASCII.LF & "As you can see in this demo, the items can be linked together, and" & " the items remain connected when they are moved." & ASCII.LF & "The canvas also support @bscrolling@B, if put in a " & " @bGtk_Scrolled_Window@B, as you can see if you move the items" & " outside of the visible part of the canvas." & "There is a small area on each side of the canvas. If you leave the" & " mouse in this area while dragging an item, the canvas will" & " keep scrolling until the mouse is moved outside of this area." & ASCII.LF & "The canvas provides @bzooming@B capabilities. Try clicking on the" & " two arrow buttons at the top of this demo." & ASCII.LF & "The canvas includes a simple @blayout scheme@B, that can be" & " overriden with more complex algorithms. Items are stored in a" & " graph structure, tha includes a number of useful algorithms for" & " layout: topological sort,..." & ASCII.LF & "@bNon-rectangular items@B can also be used, see for instance the" & " two items 2 and 4 in the default layout." & ASCII.LF & "You can also redefine your own @btype of links@B. By default, links" & " are either straight or arc links, that may optionaly have arrows" & " on either end."; end Help; --------------------------- -- Draw_To_Double_Buffer -- --------------------------- procedure Draw_To_Double_Buffer (Item : access Display_Item_Record'Class) is begin Set_Foreground (Green_Gc, Display_Item (Item).Color); Draw_Rectangle (Pixmap (Item), GC => Green_Gc, Filled => True, X => 0, Y => 0, Width => Item.W, Height => Item.H); Set_Foreground (Green_Gc, Black (Get_Default_Colormap)); Set_Text (Layout, "Item" & Positive'Image (Display_Item (Item).Num)); Draw_Layout (Pixmap (Item), Green_Gc, 10, 10, Layout); Draw_Shadow (Style => Get_Style (Item.Canvas), Window => Pixmap (Item), State_Type => State_Normal, Shadow_Type => Shadow_Out, X => 0, Y => 0, Width => Item.W, Height => Item.H); -- We could not make Draw_To_Double_Buffer a primitive operation, since -- it is defined in the body, however it would be cleaner in a real -- application if Item.all in Hole_Item_Record'Class then Draw_Shadow (Style => Get_Style (Item.Canvas), Window => Pixmap (Item), State_Type => State_Normal, Shadow_Type => Shadow_Etched_Out, X => Get_Coord (Item).Width / 2 - 12, Y => Get_Coord (Item).Height / 2 - 12, Width => 24, Height => 24); end if; end Draw_To_Double_Buffer; ---------- -- Draw -- ---------- procedure Draw (Item : access Resizable_Item_Record; Canvas : access Interactive_Canvas_Record'Class; GC : Gdk.GC.Gdk_GC; Xdest, Ydest : Glib.Gint) is Rect : constant Gdk_Rectangle := Get_Coord (Item); W : constant Gint := To_Canvas_Coordinates (Canvas, Rect.Width); H : constant Gint := To_Canvas_Coordinates (Canvas, Rect.Height); Arrow : constant Gint := To_Canvas_Coordinates (Canvas, 3); begin Set_Foreground (GC, Black (Get_Default_Colormap)); Draw_Rectangle (Get_Window (Canvas), GC => GC, Filled => True, X => Xdest, Y => Ydest, Width => W - 2 * Arrow, Height => H); Draw_Line (Get_Window (Canvas), GC, Xdest + W, Ydest, Xdest + W, Ydest + H); Draw_Line (Get_Window (Canvas), GC, Xdest + W, Ydest, Xdest + W - Arrow, Ydest + Arrow); Draw_Line (Get_Window (Canvas), GC, Xdest + W, Ydest, Xdest + W + Arrow, Ydest + Arrow); Draw_Line (Get_Window (Canvas), GC, Xdest + W, Ydest + H, Xdest + W - Arrow, Ydest + H - Arrow); Draw_Line (Get_Window (Canvas), GC, Xdest + W, Ydest + H, Xdest + W + Arrow, Ydest + H - Arrow); Draw_Line (Get_Window (Canvas), GC, Xdest, Ydest, Xdest + W, Ydest); Draw_Line (Get_Window (Canvas), GC, Xdest, Ydest + H, Xdest + W, Ydest + H); end Draw; --------------------- -- On_Button_Click -- --------------------- procedure On_Button_Click (Item : access Resizable_Item_Record; Event : Gdk.Event.Gdk_Event_Button) is Rect : constant Gdk_Rectangle := Get_Coord (Item); begin if Get_Event_Type (Event) = Button_Press then Item.Initial := Rect; if Gint (Get_Y (Event)) > Rect.Height - 3 then Item.Typ := Bottom; end if; elsif Get_Event_Type (Event) = Button_Release then case Item.Typ is when Bottom => Set_Screen_Size (Item, Rect.Width, Gint (Get_Y (Event))); when others => null; end case; -- Item_Updated (Item); end if; end On_Button_Click; --------------------- -- Draw_Background -- --------------------- procedure Draw_Background (Canvas : access Image_Canvas_Record; Screen_Rect : Gdk.Rectangle.Gdk_Rectangle) is X_Left : constant Glib.Gint := Left_World_Coordinates (Canvas); Y_Top : constant Glib.Gint := Top_World_Coordinates (Canvas); begin if Canvas.Background /= null then -- This is slightly complex, since we need to properly handle zooming -- and tiling. declare X, Y, W, H, Ys : Gint; Xs : Gint := Screen_Rect.X; Bw : constant Gint := Get_Width (Canvas.Background) * Gint (Get_Zoom (Canvas)) / 100; Bh : constant Gint := Get_Height (Canvas.Background) * Gint (Get_Zoom (Canvas)) / 100; Scaled : Gdk_Pixbuf := Canvas.Background; begin -- A real application would cache this scaled pixmap, and update -- the cache when the "zoomed" signal is emitted. if Get_Zoom (Canvas) /= 100 then Scaled := Scale_Simple (Canvas.Background, Bw, Bh); end if; while Xs < Screen_Rect.X + Screen_Rect.Width loop Ys := Screen_Rect.Y; X := (X_Left + Xs) mod Bw; W := Gint'Min (Screen_Rect.Width + Screen_Rect.X - Xs, Bw - X); while Ys < Screen_Rect.Y + Screen_Rect.Height loop Y := (Y_Top + Ys) mod Bh; H := Gint'Min (Screen_Rect.Height + Screen_Rect.Y - Ys, Bh - Y); Render_To_Drawable (Pixbuf => Scaled, Drawable => Get_Window (Canvas), Gc => Get_Black_GC (Get_Style (Canvas)), Src_X => X, Src_Y => Y, Dest_X => Xs, Dest_Y => Ys, Width => W, Height => H); Ys := Ys + H; end loop; Xs := Xs + W; end loop; if Get_Zoom (Canvas) /= 100 then Unref (Scaled); end if; end; else Draw_Rectangle (Get_Window (Canvas), Get_Background_GC (Get_Style (Canvas), State_Normal), Filled => True, X => Screen_Rect.X, Y => Screen_Rect.Y, Width => Gint (Screen_Rect.Width), Height => Gint (Screen_Rect.Height)); end if; if Canvas.Draw_Grid then Draw_Grid (Interactive_Canvas (Canvas), Canvas.Grid_GC, Screen_Rect); end if; end Draw_Background; ---------- -- Draw -- ---------- procedure Draw (Item : access Hole_Item_Record; Canvas : access Gtkada.Canvas.Interactive_Canvas_Record'Class; GC : Gdk.GC.Gdk_GC; Xdest : Glib.Gint; Ydest : Glib.Gint) is Region : Gdk_Region; Item_Width : constant Gint := To_Canvas_Coordinates (Canvas, Get_Coord (Item).Width); Item_Height : constant Gint := To_Canvas_Coordinates (Canvas, Get_Coord (Item).Height); Item_Width_10 : constant Gint := To_Canvas_Coordinates (Canvas, Get_Coord (Item).Width / 2 - 10); Item_Height_10 : constant Gint := To_Canvas_Coordinates (Canvas, Get_Coord (Item).Height / 2 - 10); begin -- The trick to drawing non-rectangular items is to change the clip mask -- of the graphic context before calling the inherited subprogram. Region := Rectangle ((0, 0, Item_Width_10, Item_Height)); Union_With_Rect (Region, (0, 0, Item_Width, Item_Height_10)); Union_With_Rect (Region, (To_Canvas_Coordinates (Canvas, Get_Coord (Item).Width / 2 + 10), 0, Item_Width_10, Item_Height)); Union_With_Rect (Region, (0, To_Canvas_Coordinates (Canvas, Get_Coord (Item).Height / 2 + 10), Item_Width, Item_Height_10)); Set_Clip_Region (GC, Region); Set_Clip_Origin (GC, Xdest, Ydest); Draw (Display_Item_Record (Item.all)'Access, Canvas, GC, Xdest, Ydest); Set_Clip_Mask (GC, null); Destroy (Region); end Draw; ------------------- -- Point_In_Item -- ------------------- function Point_In_Item (Item : access Hole_Item_Record; X, Y : Glib.Gint) return Boolean is W : constant Gint := Get_Coord (Item).Width / 2; H : constant Gint := Get_Coord (Item).Height / 2; X2 : constant Gint := X - Get_Coord (Item).X; Y2 : constant Gint := Y - Get_Coord (Item).Y; begin if X2 >= W - 10 and then X2 <= W + 10 and then Y2 >= H - 10 and then Y2 <= H + 10 then return False; else return Point_In_Item (Display_Item_Record (Item.all)'Access, X, Y); end if; end Point_In_Item; ---------------- -- Initialize -- ---------------- procedure Initialize (Item : access Display_Item_Record'Class; Canvas : access Interactive_Canvas_Record'Class) is begin Item.Canvas := Interactive_Canvas (Canvas); Item.Color := Colors (Random (Color_Gen)); Item.W := Item_Width * Random (Zoom_Gen); Item.H := Item_Height * Random (Zoom_Gen); Item.Num := Last_Item; if Last_Item <= Items_List'Last then Items_List (Item.Num) := Canvas_Item (Item); end if; Last_Item := Last_Item + 1; Set_Screen_Size (Item, Item.W, Item.H); Set_Text (Num_Items_Label, Positive'Image (Last_Item - 1) & " items"); Draw_To_Double_Buffer (Item); end Initialize; --------------------- -- Add_Random_Item -- --------------------- procedure Add_Random_Item (Canvas : access Interactive_Canvas_Record'Class) is Item : constant Display_Item := new Display_Item_Record; begin Initialize (Item, Canvas); Put (Canvas, Item, Random (Gen), Random (Gen)); Refresh_Canvas (Canvas); Show_Item (Canvas, Item); end Add_Random_Item; ----------- -- Clear -- ----------- procedure Clear (Canvas : access Interactive_Canvas_Record'Class) is function Remove_Internal (Canvas : access Interactive_Canvas_Record'Class; Item : access Canvas_Item_Record'Class) return Boolean is begin Remove (Canvas, Item); return True; end Remove_Internal; begin For_Each_Item (Canvas, Remove_Internal'Unrestricted_Access); Refresh_Canvas (Canvas); Last_Item := 1; Last_Link := 1; Set_Text (Num_Items_Label, Positive'Image (Last_Item - 1) & " items"); Set_Text (Num_Links_Label, Positive'Image (Last_Link - 1) & " links"); end Clear; --------------- -- Add_Items -- --------------- procedure Add_Items (Canvas : access Interactive_Canvas_Record'Class) is Max : constant Positive := Last_Item + Positive (Get_Value_As_Int (Num_Spin)) - 1; begin for J in Last_Item .. Max loop Add_Random_Item (Canvas); Add_Canvas_Link (Canvas, Items_List (J), Items_List (Random (Item_Gen) mod J + 1)); Add_Canvas_Link (Canvas, Items_List (J), Items_List (Random (Item_Gen) mod J + 1)); end loop; Refresh_Canvas (Canvas); end Add_Items; --------------------- -- Add_Single_Item -- --------------------- procedure Add_Single_Item (Canvas : access Interactive_Canvas_Record'Class; With_Link : Boolean) is Item : constant Display_Item := new Display_Item_Record; Num : constant Positive := Positive (Get_Value_As_Int (Start_Spin)); begin Initialize (Item, Canvas); if With_Link and then Num < Last_Item then Add_Canvas_Link (Canvas, Item, Item, "0"); Add_Canvas_Link (Canvas, Items_List (Num), Item, "1"); Add_Canvas_Link (Canvas, Items_List (Num), Item, "2"); end if; Put (Canvas, Item); Refresh_Canvas (Canvas); Show_Item (Canvas, Item); end Add_Single_Item; ------------------------------- -- Add_Single_Item_With_Link -- ------------------------------- procedure Add_Single_Item_With_Link (Canvas : access Interactive_Canvas_Record'Class) is begin Add_Single_Item (Canvas, True); end Add_Single_Item_With_Link; ----------------------------- -- Add_Single_Item_No_Link -- ----------------------------- procedure Add_Single_Item_No_Link (Canvas : access Interactive_Canvas_Record'Class) is begin Add_Single_Item (Canvas, False); end Add_Single_Item_No_Link; --------------------- -- Add_Canvas_Link -- --------------------- procedure Add_Canvas_Link (Canvas : access Interactive_Canvas_Record'Class; Item1, Item2 : access Canvas_Item_Record'Class; Text : String := "") is Link : constant Canvas_Link := new Canvas_Link_Record; begin Add_Link (Canvas, Link, Item1, Item2, Both_Arrow, Text); Last_Link := Last_Link + 1; Set_Text (Num_Links_Label, Positive'Image (Last_Link - 1) & " links"); end Add_Canvas_Link; ----------------- -- Remove_Link -- ----------------- procedure Remove_Link (Canvas : access Interactive_Canvas_Record'Class) is It1, It2 : Canvas_Item; function Remove_Internal (Canvas : access Interactive_Canvas_Record'Class; Link : access Canvas_Link_Record'Class) return Boolean is pragma Warnings (Off, Canvas); begin if (Canvas_Item (Get_Src (Link)) = It1 and then Canvas_Item (Get_Dest (Link)) = It2) or else (Canvas_Item (Get_Src (Link)) = It2 and then Canvas_Item (Get_Dest (Link)) = It1) then Remove_Link (Canvas, Link); return False; end if; return True; end Remove_Internal; Num1 : constant Positive := Positive (Get_Value_As_Int (Start_Spin)); Num2 : constant Positive := Positive (Get_Value_As_Int (End_Spin)); begin if Num1 < Last_Item and then Num2 < Last_Item then It1 := Canvas_Item (Items_List (Num1)); It2 := Canvas_Item (Items_List (Num2)); For_Each_Link (Canvas, Remove_Internal'Unrestricted_Access); Refresh_Canvas (Canvas); end if; end Remove_Link; ------------- -- Zoom_In -- ------------- procedure Zoom_In (Canvas : access Interactive_Canvas_Record'Class) is begin for J in Zoom_Levels'First .. Zoom_Levels'Last - 1 loop if Zoom_Levels (J) = Get_Zoom (Canvas) then Zoom (Canvas, Zoom_Levels (J + 1), 5); end if; end loop; end Zoom_In; -------------- -- Zoom_Out -- -------------- procedure Zoom_Out (Canvas : access Interactive_Canvas_Record'Class) is begin for J in Zoom_Levels'First + 1 .. Zoom_Levels'Last loop if Zoom_Levels (J) = Get_Zoom (Canvas) then Zoom (Canvas, Zoom_Levels (J - 1), 5); end if; end loop; end Zoom_Out; ------------------- -- Initial_Setup -- ------------------- procedure Initial_Setup (Canvas : access Interactive_Canvas_Record'Class) is Item1, Item2, Item3, Item4 : Display_Item; Item5 : Resizable_Item; Link : Canvas_Link; begin Item1 := new Display_Item_Record; Initialize (Item1, Canvas); Put (Canvas, Item1, 10, 10); Item2 := new Hole_Item_Record; Initialize (Item2, Canvas); Put (Canvas, Item2, 70, 240); Item3 := new Display_Item_Record; Initialize (Item3, Canvas); Put (Canvas, Item3, 200, 10); Item4 := new Hole_Item_Record; Initialize (Item4, Canvas); Put (Canvas, Item4, 280, 170); Item5 := new Resizable_Item_Record; Set_Screen_Size (Item5, 30, 30); Put (Canvas, Item5, 200, 170); Add_Canvas_Link (Canvas, Item1, Item1, "From1->2"); Add_Canvas_Link (Canvas, Item3, Item1, "From3->2"); Add_Canvas_Link (Canvas, Item1, Item4, "From1->4"); Add_Canvas_Link (Canvas, Item1, Item4, "From1->4"); Add_Canvas_Link (Canvas, Item2, Item3, "From2->3"); Add_Canvas_Link (Canvas, Item2, Item4, "From2->4"); Add_Canvas_Link (Canvas, Item3, Item4, "From3->41"); Link := new Canvas_Link_Record; Add_Link (Canvas, Link, Item3, Item4, Start_Arrow, "From3->42"); Link := new Canvas_Link_Record; Add_Link (Canvas, Link, Item4, Item3, End_Arrow, "From3->43"); Link := new Canvas_Link_Record; Add_Link (Canvas, Link, Item3, Item4, Both_Arrow, "From3->44"); Link := new Canvas_Link_Record; Add_Link (Canvas, Link, Item4, Item3, Both_Arrow, "From3->45"); Link := new Canvas_Link_Record; Add_Link (Canvas, Link, Item3, Item4, Both_Arrow, "From3->46"); Link := new Canvas_Link_Record; Add_Link (Canvas, Link, Item2, Item2, No_Arrow, "Self"); Link := new Canvas_Link_Record; Add_Link (Canvas, Link, Item2, Item2, Start_Arrow, "Self2"); Last_Link := Last_Link + 7; Set_Text (Num_Links_Label, Positive'Image (Last_Link - 1) & " links"); end Initial_Setup; ------------------ -- Toggle_Align -- ------------------ procedure Toggle_Align (Align : access Gtk_Widget_Record'Class; Canvas : Image_Canvas) is begin Align_On_Grid (Canvas, Get_Active (Gtk_Check_Button (Align))); end Toggle_Align; ---------------------- -- Toggle_Draw_Grid -- ---------------------- procedure Toggle_Draw_Grid (Align : access Gtk_Widget_Record'Class; Canvas : Image_Canvas) is begin Canvas.Draw_Grid := Get_Active (Gtk_Check_Button (Align)); Refresh_Canvas (Canvas); end Toggle_Draw_Grid; ----------------------- -- Toggle_Orthogonal -- ----------------------- procedure Toggle_Orthogonal (Align : access Gtk_Widget_Record'Class; Canvas : Image_Canvas) is begin Set_Orthogonal_Links (Canvas, Get_Active (Gtk_Check_Button (Align))); Refresh_Canvas (Canvas); end Toggle_Orthogonal; ------------------------ -- Background_Changed -- ------------------------ procedure Background_Changed (Bg_Draw : access Gtk_Widget_Record'Class; Canvas : Image_Canvas) is Error : GError; begin if Get_Active (Gtk_Check_Button (Bg_Draw)) then Gdk_New_From_File (Canvas.Background, Filename => "background.jpg", Error => Error); Canvas.Grid_GC := Get_White_GC (Get_Style (Canvas)); else if Canvas.Background /= null then Unref (Canvas.Background); Canvas.Background := null; end if; Canvas.Grid_GC := Get_Black_GC (Get_Style (Canvas)); end if; Refresh_Canvas (Canvas); end Background_Changed; --------- -- Run -- --------- procedure Run (Frame : access Gtk.Frame.Gtk_Frame_Record'Class) is Canvas : Image_Canvas; Box, Bbox, Bbox2, Bbox3, Spin_Box, Small : Gtk_Box; Button : Gtk_Button; Arrow : Gtk_Arrow; Scrolled : Gtk_Scrolled_Window; Label : Gtk_Label; Adj : Gtk_Adjustment; F : Gtk_Frame; Align : Gtk_Check_Button; begin Last_Item := Items_List'First; Last_Link := 1; Gtk_New_Vbox (Box, Homogeneous => False); Add (Frame, Box); Gtk_New_Hbox (Bbox, Homogeneous => True); Pack_Start (Box, Bbox, Expand => False, Fill => False); Gtk_New_Hbox (Bbox2, Homogeneous => True); Pack_Start (Box, Bbox2, Expand => False, Fill => False); Gtk_New_Hbox (Bbox3, Homogeneous => True); Pack_Start (Box, Bbox3, Expand => False, Fill => False); Gtk_New_Hbox (Spin_Box, Homogeneous => True); Pack_Start (Box, Spin_Box, Expand => False, Fill => False); Gtk_New (F); Pack_Start (Box, F); Gtk_New (Scrolled); Add (F, Scrolled); Canvas := new Image_Canvas_Record; Initialize (Canvas); Add (Scrolled, Canvas); Align_On_Grid (Canvas, False); Gtk_New (Button); Gtk_New (Arrow, Arrow_Up, Shadow_Out); Add (Button, Arrow); Pack_Start (Bbox, Button, Expand => False, Fill => True); Canvas_Cb.Object_Connect (Button, "clicked", Canvas_Cb.To_Marshaller (Zoom_In'Access), Canvas); Gtk_New (Button); Gtk_New (Arrow, Arrow_Down, Shadow_Out); Add (Button, Arrow); Pack_Start (Bbox, Button, Expand => False, Fill => True); Canvas_Cb.Object_Connect (Button, "clicked", Canvas_Cb.To_Marshaller (Zoom_Out'Access), Canvas); Gtk_New (Button, "Random"); Pack_Start (Bbox, Button, Expand => False, Fill => True); Canvas_Cb.Object_Connect (Button, "clicked", Canvas_Cb.To_Marshaller (Add_Random_Item'Access), Canvas); Gtk_New (Button, "Add One"); Pack_Start (Bbox, Button, Expand => False, Fill => True); Canvas_Cb.Object_Connect (Button, "clicked", Canvas_Cb.To_Marshaller (Add_Single_Item_No_Link'Access), Canvas); Gtk_New (Button, "Clear"); Pack_Start (Bbox, Button, Expand => False, Fill => True); Canvas_Cb.Object_Connect (Button, "clicked", Canvas_Cb.To_Marshaller (Clear'Access), Canvas); Gtk_New (Button, "Remove Link Start->End"); Pack_Start (Bbox2, Button, Expand => False, Fill => True); Canvas_Cb.Object_Connect (Button, "clicked", Canvas_Cb.To_Marshaller (Remove_Link'Access), Canvas); Gtk_New (Button, "Add multiple Items"); Pack_Start (Bbox2, Button, Expand => False, Fill => True); Canvas_Cb.Object_Connect (Button, "clicked", Canvas_Cb.To_Marshaller (Add_Items'Access), Canvas); Gtk_New (Button, "Add with Link ->Start"); Pack_Start (Bbox2, Button, Expand => False, Fill => True); Canvas_Cb.Object_Connect (Button, "clicked", Canvas_Cb.To_Marshaller (Add_Single_Item_With_Link'Access), Canvas); Gtk_New (Align, "Align on grid"); Set_Active (Align, Get_Align_On_Grid (Canvas)); Pack_Start (Bbox3, Align, Expand => False, Fill => True); Canvas_User_Cb.Connect (Align, "toggled", Canvas_User_Cb.To_Marshaller (Toggle_Align'Access), Canvas); Gtk_New (Align, "Draw grid"); Set_Active (Align, Canvas.Draw_Grid); Pack_Start (Bbox3, Align, Expand => False, Fill => True); Canvas_User_Cb.Connect (Align, "toggled", Canvas_User_Cb.To_Marshaller (Toggle_Draw_Grid'Access), Canvas); Gtk_New (Align, "Orthogonal links"); Set_Active (Align, Get_Orthogonal_Links (Canvas)); Pack_Start (Bbox3, Align, Expand => False, Fill => True); Canvas_User_Cb.Connect (Align, "toggled", Canvas_User_Cb.To_Marshaller (Toggle_Orthogonal'Access), Canvas); Gtk_New (Align, "draw background"); Set_Active (Align, Canvas.Background /= null); Pack_Start (Bbox3, Align, Expand => True, Fill => True); Canvas_User_Cb.Connect (Align, "toggled", Canvas_User_Cb.To_Marshaller (Background_Changed'Access), Canvas); Background_Changed (Align, Canvas); Gtk_New (Num_Items_Label, "0 items"); Pack_Start (Spin_Box, Num_Items_Label, Expand => False, Fill => False); Gtk_New (Num_Links_Label, "0 links"); Pack_Start (Spin_Box, Num_Links_Label, Expand => False, Fill => False); Gtk_New_Hbox (Small, Homogeneous => False); Gtk_New (Label, "Add:"); Pack_Start (Small, Label, Expand => False, Fill => False); Gtk_New (Adj, 10.0, 1.0, 300.0, 1.0, 30.0, 30.0); Gtk_New (Num_Spin, Adj, 0.5, 0); Pack_Start (Small, Num_Spin, Expand => False, Fill => False); Pack_Start (Spin_Box, Small, Expand => False, Fill => False); Gtk_New_Hbox (Small, Homogeneous => False); Gtk_New (Label, "Start:"); Pack_Start (Small, Label, Expand => False, Fill => False); Gtk_New (Adj, 1.0, 1.0, 300.0, 1.0, 30.0, 30.0); Gtk_New (Start_Spin, Adj, 0.5, 0); Pack_Start (Small, Start_Spin, Expand => False, Fill => False); Pack_Start (Spin_Box, Small, Expand => False, Fill => False); Gtk_New_Hbox (Small, Homogeneous => False); Gtk_New (Label, "End:"); Pack_Start (Small, Label, Expand => False, Fill => False); Gtk_New (Adj, 2.0, 1.0, 300.0, 1.0, 30.0, 30.0); Gtk_New (End_Spin, Adj, 0.5, 0); Pack_Start (Small, End_Spin, Expand => False, Fill => False); Pack_Start (Spin_Box, Small, Expand => False, Fill => False); Realize (Canvas); -- Initialize the colors Gdk_New (Green_Gc, Get_Window (Canvas)); for J in Color_Names'Range loop Colors (J) := Parse (Color_Names (J).all); Alloc (Gtk.Widget.Get_Default_Colormap, Colors (J)); end loop; Layout := Create_Pango_Layout (Frame); Initial_Setup (Canvas); Show_All (Frame); end Run; end Create_Canvas;

Alphabetical Index