# # $Id: scrollarea.icn,v 1.5 2004/11/06 00:28:13 rparlett Exp $ # # This file is in the public domain. # # Author: Robert Parlett (parlett@dial.pipex.com) # package gui link graphics $include "guih.icn" # # This is a base class for displaying an arbitrarily large object # using a pair of scroll bars. This specification of the object is # provided by implementing methods in a subclass. # # No event handling is done; see BasicScrollArea for the necessary # requirement for the subclass. # class ScrollArea : Component( hsb, # vsb, view, last_refresh_x, last_refresh_y ) # # The x offset into the object we are viewing # method get_areax() return (\self.hsb).get_value() | 0 end # # The y offset into the object we are viewing # method get_areay() return (\self.vsb).get_value() | 0 end # # Recompute the scrollbars and redisplay the object. # method compute_and_invalidate() if is_dialog_open() then self.set_internal_fields() self.invalidate() end method resize() self.Component.compute_absolutes() self.set_internal_fields() end method on_vsb() self.refresh() end method on_hsb() self.refresh() end # # For a scrollarea with a border, return the number of pixels # between the left of the component and the view component. # # Different subclasses may override this; by default it returns # DEFAULT_SP_X_PADDING. # method get_view_x_padding() return DEFAULT_SP_X_PADDING end # # For a scrollarea with a border, return the number of pixels # between the top of the component and the view component. # # Different subclasses may override this; by default it returns # DEFAULT_SP_Y_PADDING. # method get_view_y_padding() return DEFAULT_SP_Y_PADDING end # # Called on resize, buttons resized, or contents amended # # @p method set_internal_fields() local line_count, subject_width, subject_height # # Position and size of scrolling region # if \self.draw_border_flag then { view.set_pos(get_view_x_padding(), get_view_y_padding()) max_th := self.h - 2 * get_view_y_padding() max_tw := self.w - 2 * get_view_x_padding() } else { view.set_pos(0, 0) max_th := self.h max_tw := self.w } # # Compute maximum width # subject_width := get_subject_width() subject_height := get_subject_height() # # Compute max/min heights/widths for text; max if no scroll bar # needed; min otherwise. # min_th := max_th - SB_SIZE min_tw := max_tw - SB_SIZE # # Set flags indicating whether scroll bars needed. 0 => definitely not # 1 => yes if opposite scroll bar needed; 2 => definitely yes. # if min_th >= subject_height then need_vsb := 0 else if max_th >= subject_height then need_vsb := 1 else need_vsb := 2 if min_tw >= subject_width then need_hsb := 0 else if max_tw >= subject_width then need_hsb := 1 else need_hsb := 2 # # Case analysis on flags to set up correct scroll bars, text width # and height fields. # if (need_vsb < 2) & (need_hsb < 2) then { # # No scroll bars. # view.set_size(max_tw, max_th) (\self.vsb).finally() (\self.hsb).finally() self.remove(\self.vsb) self.remove(\self.hsb) self.vsb := self.hsb := &null } else if (need_hsb + need_vsb > 2) then { # # Two scroll bars. # if /self.vsb := ScrollBar() then new_vsb := 1 if /self.hsb := ScrollBar() then { self.hsb.set_is_horizontal() new_hsb := 1 } view.set_size(min_tw, min_th) if \self.draw_border_flag then { self.vsb.set_pos(self.w - SB_SIZE - BORDER_WIDTH, BORDER_WIDTH) self.vsb.set_size(SB_SIZE, self.h - SB_SIZE - BORDER_WIDTH * 2) self.hsb.set_pos(BORDER_WIDTH, self.h - SB_SIZE - BORDER_WIDTH) self.hsb.set_size(self.w - SB_SIZE - BORDER_WIDTH * 2, SB_SIZE) } else { self.vsb.set_pos(self.w - SB_SIZE, 0) self.vsb.set_size(SB_SIZE, self.h - SB_SIZE) self.hsb.set_pos(0, self.h - SB_SIZE) self.hsb.set_size(self.w - SB_SIZE, SB_SIZE) } } else if (need_hsb = 0) & (need_vsb = 2) then { # # One vertical scroll bar. # if /self.vsb := ScrollBar() then new_vsb := 1 (\self.hsb).finally() self.remove(\self.hsb) self.hsb := &null view.set_size(min_tw, max_th) if \self.draw_border_flag then { self.vsb.set_pos(self.w - SB_SIZE - BORDER_WIDTH, BORDER_WIDTH) self.vsb.set_size(SB_SIZE, self.h - BORDER_WIDTH * 2) } else { self.vsb.set_pos(self.w - SB_SIZE, 0) self.vsb.set_size(SB_SIZE, self.h) } } else if (need_hsb = 2) & (need_vsb = 0) then { # # One horizontal scroll bar. # if /self.hsb := ScrollBar() then { self.hsb.set_is_horizontal() new_hsb := 1 } (\self.vsb).finally() self.remove(\self.vsb) self.vsb := &null view.set_size(max_tw, min_th) if \self.draw_border_flag then { self.hsb.set_pos(BORDER_WIDTH, self.h - SB_SIZE - BORDER_WIDTH) self.hsb.set_size(self.w - BORDER_WIDTH * 2, SB_SIZE) } else { self.hsb.set_pos(0, self.h - SB_SIZE) self.hsb.set_size(self.w, SB_SIZE) } } view.resize() # # Initialize scroll bars. # if \self.vsb then { if view.h > 0 then self.vsb.set_page_size(view.h) else self.vsb.set_page_size(1) self.vsb.set_total_size(subject_height) if \new_vsb then { self.vsb.set_increment_size(get_subject_vertical_increment()) every self.vsb.connect(self, "on_vsb", SCROLLBAR_PRESSED_EVENT | SCROLLBAR_DRAGGED_EVENT) self.vsb.set_value(0) self.add(self.vsb) self.vsb.init() self.vsb.resize() self.vsb.firstly() } else self.vsb.resize() } if \self.hsb then { if view.w > 0 then self.hsb.set_page_size(view.w) else self.hsb.set_page_size(1) self.hsb.set_total_size(subject_width) if \new_hsb then { self.hsb.set_increment_size(get_subject_horizontal_increment()) every self.hsb.connect(self, "on_hsb", SCROLLBAR_PRESSED_EVENT | SCROLLBAR_DRAGGED_EVENT) self.hsb.set_value(0) self.add(self.hsb) self.hsb.init() self.hsb.resize() self.hsb.firstly() } else self.hsb.resize() } end # # Re-draw the view area. # # @p method refresh(redraw) local currx, curry currx := self.get_areax() curry := self.get_areay() # # Do nothing unless have to # if /redraw & (\self.last_refresh_x = currx) & (\self.last_refresh_y = curry) then return # # Save present co-ordinates # self.last_refresh_x := currx self.last_refresh_y := curry view.invalidate() end method display(buffer_flag) EraseRectangle(self.cbwin, self.x, self.y, self.w, self.h) if \self.draw_border_flag then DrawSunkenRectangle(self.cbwin, self.x, self.y, self.w, self.h) view.display(1) (\self.vsb).display(1) (\self.hsb).display(1) self.do_shading(self.cbwin) if /buffer_flag then CopyArea(self.cbwin, self.cwin, self.x, self.y, self.w, self.h, self.x, self.y) end # # Return the width of the subject object # abstract method get_subject_width() # # Return the height of the subject object # abstract method get_subject_height() # # Return the increment on a line-up/line-down # abstract method get_subject_vertical_increment() # # Return the increment on a line-left/line-right # abstract method get_subject_horizontal_increment() # # Create the view component # abstract method create_view() initially() self.Component.initially() self.set_draw_border() self.view := create_view() add(view) end