# # $Id: tabset.icn,v 1.5 2004/11/05 19:24:55 rparlett Exp $ # # This file is in the public domain. # # Author: Robert Parlett (parlett@dial.pipex.com) # package gui link graphics $include "guih.icn" # # This class holds the several {TabItems}, and represents a tabbed pane. # # A SELECTION_CHANGED_EVENT is fired whenever the tab is changed via # user interaction. # class TabSet : Component( which_one, # tab_h, # lines, # line_h, # line_break # ) method get_y_reference() return self.y + self.tab_h end method get_h_reference() return self.h - self.tab_h end method display(buffer_flag) local last_on_a_line, hw # # Erase all and display outline of tabbed pane area. # EraseRectangle(self.cbwin, self.x, self.y, self.w, self.h) DrawRaisedRectangle(self.cbwin, self.x, self.y, self.w, self.h) # # Display all tabs. # every (!!self.line_break).display_tab() last_on_a_line := (!self.line_break)[-1] === self.which_one # # Display line under tabs. # hw := get_hilite_win(self.cbwin) DrawLine(hw, self.x, self.y + self.tab_h - 2, self.which_one.label_x, self.y + self.tab_h - 2) if /last_on_a_line then DrawLine(hw, self.which_one.label_x + self.which_one.label_w, self.y + self.tab_h - 2, self.x + self.w - 1, self.y + self.tab_h - 2) DrawLine(hw, self.x, self.y + self.tab_h - 1, self.which_one.label_x, self.y + self.tab_h - 1) if /last_on_a_line then DrawLine(hw, self.which_one.label_x + self.which_one.label_w, self.y + self.tab_h - 1, self.x + self.w - 2, self.y + self.tab_h - 1) # # Display contents of current tab into buffer # which_one.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 # # Determine which tab if any mouse is over. # # @p method which_tab() l := (&y - self.y) / self.line_h + 1 every c := !self.line_break[l] do { if c.is_unshaded() & (c.label_x <= &x < c.label_x + c.label_w) then return c } end # # Ensure which_one is at front of tab lines # # @p method adjust_lines() l := self.which_one.line_no self.line_break[l] :=: self.line_break[self.lines] every (!self.line_break[l]).line_no := l every (!self.line_break[self.lines]).line_no := self.lines end # # Set which tab is currently on display. # @param x The {TabItem} to be displayed. # method set_which_one(x) if \ (\self.parent_dialog).is_open then { self.which_one := x self.adjust_lines() self.invalidate() } else self.which_one := x return x end # # Return the currently selected tab # method get_which_one() return self.which_one end method handle_event(e) if &meta & m := find_key(e) then { self.set_which_one(m) fire(SELECTION_CHANGED_EVENT, e) } else if e === (&lpress | &rpress | &mpress) & (self.y <= &y < self.y + self.tab_h) then { self.set_which_one(which_tab()) fire(SELECTION_CHANGED_EVENT, e) } which_one.handle_event(e) end # # Find the TabItem with the given accelerator. This isn't done using # the handle_accel method in the TabItem because only the current # TabItem is treated as being unhidden. # @p method find_key(k) local m every m := !self.children do { if m.accel === k & not(m.is_shaded()) then return m } end # # Break the set of tabs up into lines, given the padding within each tab. # Returns a list each element of which is a list of those tabs on one line. # # @p method how_many_lines(pad) t := 0 l := [] cl := [] every c := !self.children do { lw := TextWidth(self.cwin, c.label) + pad if (t > 0) & (t + lw > self.w) then { # # New line required. # t := 0 put(l, cl) cl := [] } t +:= lw put(cl, c) } # # Final line, if any # if t > 0 then put(l, cl) return l end method compute_absolutes() if *self.children = 0 then fatal("no TabItems in TabSet") every (!self.children).check_label() self.Component.compute_absolutes() # # Determine how many lines for minimum padding. # pad := 2 * DEFAULT_TEXT_X_SURROUND self.lines := *(line_break := how_many_lines(pad)) self.line_h := WAttrib(self.cwin, "fheight") + 2 * DEFAULT_TEXT_Y_SURROUND self.tab_h := self.line_h * self.lines # # Expand padding whilst can do so and remain within the original # number of lines. This should even out the tabs. # if 1 < self.lines < *self.children then { while *(l2 := how_many_lines(pad + X_PADDING_INC)) <= self.lines do { self.line_break := l2 pad +:= X_PADDING_INC } } # # Finally, space out the tabs on each line to fill up each line. # n := 1 every curr_line := !self.line_break do { # # Work out total already used. # t := 0 every c := !curr_line do { c.label_w := TextWidth(self.cwin, c.label) + pad c.line_no := n t +:= c.label_w } # # Amount to add to each tab. # d := (self.w - t) / *curr_line # # Add the amount, compute new total # t := 0 every c := !curr_line do { c.label_x := self.x + t c.label_w +:= d t +:= c.label_w } # # Add residual amount to rightmost tab. # curr_line[-1].label_w +:= self.w - t n +:= 1 } /self.which_one := self.children[1] self.adjust_lines() end initially(a[]) self.Component.initially() set_fields(a) end