Source file component.icn |
# $Id: component.icn,v 1.1 2003-05-31 06:09:03 jeffery Exp $
##
# This is the parent class of all the GUI components. All of
# its methods and variables therefore apply to its sub-classes.
#
class Component : Ticker : MetaComponent(
##
# x position as specified by {set_pos()}, eg "50%"
#
x_spec,
##
# y position as specified by set_pos().
#
y_spec,
##
# width specifier as specified in set_size(), eg "100%"
#
w_spec,
##
# height specifier as specified in set_size(),
#
h_spec,
##
# x alignment as specified in set_align(), eg "l".
#
x_align,
##
# y alignment as specified in set_align(), eg "b".
#
y_align,
##
# Absolute x position in pixels computed from x_spec and the
# dimensions of the enclosing object or window.
#
x,
##
# Absolute y position.
#
y,
##
# Absolute width in pixels computed from w_spec and the
# dimensions of the enclosing object or window.
w,
##
# Absolute height in pixels.
#
h,
##
# The {_Dialog} class instance of which this {Component} is a part.
#
parent_Dialog,
##
# A list of strings being the Icon graphics attributes, eg
# ["bg=blue", "resize=on"].
attribs,
##
# Flag indicating whether the {Component} currently has keyboard
# focus; {&null} means it hasn't.
has_focus,
##
# Flag indicating whether the {Component} currently is shaded;
# {&null} means it isn't.
is_shaded_flag,
##
# A cloned window created by combining the Dialog's canvas
# with the Component's attributes, so drawing into this window
# will draw straight to the Dialog window with the correct
# attributes.
cwin,
##
# A cloned window created by combining a buffer window with
# the {Component's} attributes. This is used solely for
# double-buffering purposes.
cbwin,
##
# Flag indicating whether the {Component} accepts keyboard focus
# by way of the tab key being pressed; {&null} means it doesn't.
accepts_tab_focus_flag,
##
# Flag indicating whether the Component should have a border
# drawn around it; {&null} means no. Many {Components} (such as
# {TextButtons}) ignore this flag.
draw_border_flag,
no_updates_count,
##
# Tooltip string for use with {ToolBar} objects.
#
tooltip,
##
# Reference to enclosing {Component} object.
#
parent_Component,
#
# tells whether ordinary tab characters are used by the component;
# if not, they shift focus to the next component. shift-tab should
# always shift focus to the next component.
#
keeps_tabs
)
##
# Set a tooltip string. This is presently only used by
# the {Toolbar} class.
#
method set_tooltip(x)
return self.tooltip := x
end
method handle_notify(e)
self.parent_Component$handle_notify(e)
end
method set_no_updates()
return no_updates_count +:= 1
end
method clear_no_updates()
no_updates_count -:= 1
redisplay()
return no_updates_count
end
method get_x_reference()
return self.x
end
method get_y_reference()
return self.y
end
method get_w_reference()
return self.w
end
method get_h_reference()
return self.h
end
method get_cwin_reference()
return self.cwin
end
method get_cbwin_reference()
return self.cbwin
end
method get_visible_reference()
return self
end
method redisplay()
if \(\self.parent_Dialog).is_open & (no_updates_count = 0) & is_unhidden() then
self$display()
end
method error(s)
stop("gui.icn : error processing object " || object_class_name(self) || " : " || s)
end
##
# Succeed if the component is hidden; for example if it is
# within a tabbed pane not presently visible.
#
method is_hidden()
return self.parent_Component$is_hidden()
end
##
# Succeed if the component is not hidden.
#
method is_unhidden()
return self.parent_Component$is_unhidden()
end
##
# Succeeds if the component is shaded; fails otherwise. A
# shaded component, such as a button, may be displayed
# differently, and will not generate events.
#
method is_shaded()
return \self.is_shaded_flag | self.parent_Component$is_shaded()
end
##
# Succeed if the component is not shaded.
#
method is_unshaded()
return /self.is_shaded_flag & self.parent_Component$is_unshaded()
end
##
# Called from a component's {display()} method, this method
# filters the component to give a shaded appearance, if the
# {is_shaded_flag} is set. {W} is the window to draw into
# (normally {self.cwin}).
#
method do_shading(W)
if is_shaded() then
FilterRectangle(W, self.x, self.y, self.w, self.h)
end
method accepts_tab_focus()
return is_unshaded() & \self.accepts_tab_focus_flag
end
method unique_start()
self.parent_Dialog$set_unique(self)
end
method unique_end(x)
self.parent_Dialog$clear_unique(x)
end
##
# This method is over-ridden by all this class's subclasses.
# It is the method which handles an Icon event e. It would
# not normally be called by a user program. It should either
# fail, or return an {_Event} structure. This will then be
# passed to the {dialog_event()} method of the dialog. The
# first two fields of the _Event structure should be the Icon
# event e and the object itself. The third field is the code,
# which can be any integer.
#
method handle_event(e)
error("handle_event() method must be over-ridden in Component sub-class")
end
##
# Swap the shaded status of the component.
#
method toggle_is_shaded()
if /self.is_shaded_flag then
self.is_shaded_flag := 1
else
self.is_shaded_flag := &null
self$redisplay()
end
##
# Set the shaded status of the component to shaded.
#
method set_is_shaded()
self.is_shaded_flag := 1
self$redisplay()
end
##
# Set the shaded status of the component to not shaded.
#
method clear_is_shaded()
self.is_shaded_flag := &null
self$redisplay()
end
##
# Toggle whether or not to draw a border around the component.
# Different objects respond differently to this flag being
# set; some ignore it altogether.
#
method toggle_draw_border()
if /self.draw_border_flag then
self.draw_border_flag := 1
else
self.draw_border_flag := &null
self$redisplay()
end
##
# Set the component such that a border is drawn.
#
method set_draw_border()
self.draw_border_flag := 1
self$redisplay()
end
##
# Set the component such that a border is not drawn.
#
method clear_draw_border()
self.draw_border_flag := &null
self$redisplay()
end
method set_accepts_tab_focus()
return self.accepts_tab_focus_flag := 1
end
method clear_accepts_tab_focus()
return self.accepts_tab_focus_flag := &null
end
##
# This draws, or re-draws, the component in the dialog
# window.
# @param buffer_flag If this parameter is not null, then
# @ the component is displayed into the buffer window, not
# @ the dialog window (this is used for double-buffering purposes).
#
method display(buffer_flag)
error("display() method must be over-ridden in Component sub-class")
end
##
# Set the Icon attribs of the component to the given parameter
# @example
# @ w.attrib("font=helvetica", "bg=pale blue")
# @ w.set_attribs("font=helvetica", "bg=pale blue")
#
method attrib(x[])
if *x=1 & type(x[1])=="list" then x := x[1]
return set_attribs_list(x)
end
method set_attribs(x[])
return set_attribs_list(x)
end
##
# Equivalent to {set_attribs()}, above, but takes a list as a
# parameter.
# @param l The list of attribs.
# @example
# @ w$set_attribs_list(["font=helvetica", "bg=pale blue"])
#
method set_attribs_list(l)
if \ (self.cwin) then { WAttrib ! push(l, self.cwin); pop(l) }
if \ (self.cbwin) then { WAttrib ! push(l, self.cbwin); pop(l) }
# need to add check, and *replace* attributes if already on the list,
# instead of blindly concatenating.
return self.attribs |||:= l
end
##
# Succeeds if the component is not shaded and the values of {&x}
# and {&y} lie within the component.
#
method in_region()
if is_unshaded() & ((self.x <= &x < self.x + self.w) & (self.y <= &y < self.y + self.h)) then
return self
end
##
# Method called when the component gets the keyboard focus; may be extended.
#
method got_focus()
self.has_focus := 1
redisplay()
end
##
# Return the Icon window of the dialog in which the component resides.
#
method get_parent_win()
return self.parent_Dialog$get_win()
end
method get_parent_buffer_win()
return self.parent_Dialog$get_buffer_win()
end
##
# Method called when the component loses the keyboard focus; may be extended.
#
method lost_focus()
self.has_focus := &null
redisplay()
end
##
# Set the x and y position of the component. Each coordinate
# can be either an absolute pixel position, or can be given in
# the form of a percentage plus or minus an offset.
# @param x_spec The x specification.
# @param y_spec The y specification.
# @example
# @ c$set_pos(100, "25%")
# @ c$set_pos("50%-20", "25%+100")
#
method set_pos(x_spec, y_spec)
self.x_spec := x_spec
self.y_spec := y_spec
return
end
##
# Set the size of the component. The parameters are in the
# same format as for {set_pos()}
# above. Some components will
# set sensible default sizes, but for others the size must be
# set explicitly.
#
method set_size(w_spec, h_spec)
self.w_spec := w_spec
self.h_spec := h_spec
return
end
##
# Set the alignment of the component. Options for
# {x_align} are ``l'', ``c'' and ``r'', for left, centre, and right
# alignment. Options for {y_align} are ``t'', ``c'' and ``b'',
# for top centre and bottom alignment. The default alignment is ``l'', ``t''.
#
# @param x_align The x alignment
# @param y_align The y alignment
#
method set_align(x_align, y_align)
if x_align == "t" then runerr()
self.x_align := x_align
self.y_align := y_align
end
method set_abs_coords(x, y)
self.x := x
self.y := y
return
end
method set_abs_size(w, h)
self.w := w
self.h := h
return
end
method final_setup(x, y)
self.parent_Dialog := x
self.parent_Component := y
if self.cwin :=
(Clone!([self.parent_Component$get_cwin_reference()]|||self.attribs))
then {
self.cbwin :=
(Clone ! ([self.parent_Component$get_cbwin_reference()] |||
self.attribs))
}
else {
write(&errout, "clone failed, discarding attributes:")
every write(&errout, "\t", ! self.attribs)
self.cwin := (Clone ! ([self.parent_Component$get_cwin_reference()]))
if / (self.cwin) then error("window system resource error")
self.cbwin :=(Clone ! ([self.parent_Component$get_cbwin_reference()]))
if / (self.cbwin) then error("window system resource error #2")
}
return x
end
##
# Returns the dialog holding the component.
#
method get_parent_Dialog()
return self.parent_Dialog
end
##
# Sets the owning _Dialog of the component. This method
# needs to be extended for a component which contains other
# components.
#
# @param c The parent dialog.
#
method set_parent_Dialog(c)
return self.parent_Dialog := c
end
##
# This method may be extended. It is invoked after the
# position of the object has been computed and the window has
# been opened, but before the object has been displayed in the
# window.
#
method firstly()
end
##
# This method may be extended. It is invoked just before the
# window is closed.
#
method finally()
stop_ticker()
Uncouple(\ (self.cwin))
Uncouple(\ (self.cbwin))
self.cwin := self.cbwin := &null
return
end
##
#
# Parse a position specification into an absolute value.
# @param total The total value
# @param s The size specifier
#
method parse_pos(total, s)
local pct, off
s ? {
if pct := 0.01 * tab(upto('%')) then {
move(1)
if ="-" then
off := -integer(tab(0)) | fail
else if ="+" then
off := integer(tab(0)) | fail
else off := 0
} else {
pct := 0
off := integer(tab(0)) | fail
}
}
return integer(pct * total + off)
end
##
# Compute the absolute positions and sizes from the
# specifications given by {set_pos()} and {set_size()}.
# This method needs to be extended for a component which
# contains other components.
#
method resize()
local wh, ww
#
# Check for unspecified fields
#
if /self.x_spec then
error("x position unspecified")
if /self.y_spec then
error("y position unspecified")
if /self.w_spec then
error("width unspecified")
if /self.h_spec then
error("height unspecified")
wh := self.parent_Component$get_h_reference()
ww := self.parent_Component$get_w_reference()
self.x := self.parent_Component$get_x_reference() + parse_pos(ww, self.x_spec) | error("invalid x position specification")
self.y := self.parent_Component$get_y_reference() + parse_pos(wh, self.y_spec) | error("invalid y position specification")
self.w := parse_pos(ww, self.w_spec) | error("invalid width specification")
self.h := parse_pos(wh, self.h_spec) | error("invalid height specification")
#
# Adjust x, y based on alignments
#
case self.x_align of {
"c" : self.x -:= self.w / 2
"r" : self.x -:= self.w
"l" : &null
default :
error("\nincorrect x alignment specifier " || image(self.x_align))
}
case self.y_align of {
"c" : self.y -:= self.h / 2
"b" : self.y -:= self.h
"t" : &null
default :
error("\nincorrect y alignment specifier " || image(self.y_align))
}
return
end
##
# Generate all the components that are visible in this
# component (which may be a container).
#
method generate_components()
return self
end
##
# Generate all the components, including non-visible ones in
# this component.
#
method generate_all_components()
return self
end
initially(argv[])
/dispatcher := Dispatcher()
self$Ticker.initially()
self.attribs := []
self.x_align := "l"
self.y_align := "t"
no_updates_count := 0
if *argv > 0 then set_fields(argv)
end
This page produced by UniDoc on 2021/04/15 @ 23:59:43.