// --------------------------------------- // Restricted Focus Viewer (RFV) Program // Version 2.1 // Language: Java 2 // October, 2000 // Copyright (C) 2000 Anthony R. Jansen // --------------------------------------- // This program 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 program 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 program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // --------------------------------------- // RFV_Block.java // // This class controls the RFV blocks, // organizing the layout of the display // and other block specific settings. // --------------------------------------- import java.awt.*; import java.util.*; public class RFV_Block { // Block attributes // ---------------- private boolean time_limit; private boolean time_limit_strict; private boolean display_set; private boolean bg_flag; private boolean feedback; private int time_limit_duration; private RFV_Defaults defaults; private Color background_color; private int line_number; private int response_time; private LinkedList button_list; private LinkedList stimulus_list; private LinkedList feedback_list; // Needed for tree that defines display element layout // --------------------------------------------------- private RFV_Display_Element head; // Needed for image loading // ------------------------ private MediaTracker media_tracker; private Toolkit toolkit; // Constructor, which reads in the block information // ------------------------------------------------- public RFV_Block(RFV_Input_File input_file, RFV_Defaults df, Graphics g, MediaTracker mt, Toolkit tk, int rt) { // Initialize variables // -------------------- time_limit = false; time_limit_strict = false; display_set = false; bg_flag = false; button_list = new LinkedList(); stimulus_list = new LinkedList(); feedback_list = new LinkedList(); response_time = rt; if (response_time == -1) feedback = false; else feedback = true; defaults = df; media_tracker = mt; toolkit = tk; background_color = defaults.getBackgroundColor(); head = new RFV_Display_Column(); ((RFV_Display_Column) head).addElement( new RFV_Display_Empty(defaults.getFrameSize().width, 0)); line_number = input_file.getLineNumber(); // Read from input file // -------------------- String token; if (!feedback) input_file.readOpenBracket(); while (!input_file.isCloseBracket()) { token = input_file.readToken(); if (token.equalsIgnoreCase("time_limit")) readTimeLimit(input_file); else if (token.equalsIgnoreCase("display")) readDisplay(input_file, g); else if (token.equalsIgnoreCase("background")) readBackgroundColor(input_file); else if (token.equalsIgnoreCase("feedback")) readFeedback(input_file); else if (feedback) RFV_Error.die(13, input_file.getLineNumber(), token); else RFV_Error.die(12, input_file.getLineNumber(), token); } input_file.readCloseBracket(); checkFeedbackLabels(input_file); setTreeOffsets(); } // Reads in a time limit command // Only one is allowed per block // ----------------------------- private void readTimeLimit(RFV_Input_File input_file) { String token; if (time_limit) RFV_Error.die(14, input_file.getLineNumber()); time_limit = true; input_file.readOpenBracket(); time_limit_duration = input_file.readInteger(); if (!input_file.isCloseBracket()) { token = input_file.readToken(); if (!token.equalsIgnoreCase("strict")) RFV_Error.die(15, input_file.getLineNumber(), token); time_limit_strict = true; } input_file.readCloseBracket(); } // Read in the background color // Allowed only once in defaults // ----------------------------- private void readBackgroundColor(RFV_Input_File input_file) { if (bg_flag) RFV_Error.die(16, input_file.getLineNumber()); bg_flag = true; background_color = input_file.readColor(); } // Reads in the display tree // Only one is allowed per block // ----------------------------- private void readDisplay(RFV_Input_File input_file, Graphics g) { if (display_set) RFV_Error.die(17, input_file.getLineNumber()); display_set = true; readColumn(input_file, (RFV_Display_Column) head, g); } // Reads in a display column // ------------------------- private void readColumn(RFV_Input_File input_file, RFV_Display_Column column, Graphics g) { String token; RFV_Display_Row element; input_file.readOpenBracket(); while (!input_file.isCloseBracket()) { token = input_file.readToken(); if (token.equalsIgnoreCase("text_line")) column.addElement(readTextLine(input_file, g)); else if (token.equalsIgnoreCase("response_time")) column.addElement(readResponseTime(input_file, g)); else if (token.equalsIgnoreCase("button")) column.addElement(readButton(input_file, g)); else if (token.equalsIgnoreCase("image")) column.addElement(readImage(input_file)); else if (token.equalsIgnoreCase("stimulus")) column.addElement(readStimulus(input_file)); else if (token.equalsIgnoreCase("v_space")) column.addElement(readVSpace(input_file)); else if (token.equalsIgnoreCase("row")) { element = new RFV_Display_Row(); column.addElement(element); readRow(input_file, element, g); } else RFV_Error.die(18, input_file.getLineNumber(), token); } input_file.readCloseBracket(); } // Reads in a display row // ---------------------- private void readRow(RFV_Input_File input_file, RFV_Display_Row row, Graphics g) { String token; RFV_Display_Column element; input_file.readOpenBracket(); while (!input_file.isCloseBracket()) { token = input_file.readToken(); if (token.equalsIgnoreCase("text_line")) row.addElement(readTextLine(input_file, g)); else if (token.equalsIgnoreCase("response_time")) row.addElement(readResponseTime(input_file, g)); else if (token.equalsIgnoreCase("button")) row.addElement(readButton(input_file, g)); else if (token.equalsIgnoreCase("image")) row.addElement(readImage(input_file)); else if (token.equalsIgnoreCase("stimulus")) row.addElement(readStimulus(input_file)); else if (token.equalsIgnoreCase("h_space")) row.addElement(readHSpace(input_file)); else if (token.equalsIgnoreCase("column")) { element = new RFV_Display_Column(); row.addElement(element); readColumn(input_file, element, g); } else RFV_Error.die(19, input_file.getLineNumber(), token); } input_file.readCloseBracket(); } // Reads in a line of text // ----------------------- private RFV_Text_Line readTextLine(RFV_Input_File input_file, Graphics g) { Font font; FontMetrics font_metrics; Color color; String label; String token; boolean font_flag = false; boolean color_flag = false; input_file.readOpenBracket(); label = input_file.readLabel(); font = defaults.getTextFont(); font_metrics = defaults.getTextFontMetrics(); color = defaults.getTextForeground(); while (!input_file.isCloseBracket()) { token = input_file.readToken(); if (token.equalsIgnoreCase("font")) { if (!font_flag) { font = readFont(input_file); font_metrics = g.getFontMetrics(font); font_flag = true; } else RFV_Error.die(20, input_file.getLineNumber()); } else if (token.equalsIgnoreCase("foreground")) { if (!color_flag) { color = input_file.readColor(); color_flag = true; } else RFV_Error.die(21, input_file.getLineNumber()); } else RFV_Error.die(22, input_file.getLineNumber(), token); } input_file.readCloseBracket(); return new RFV_Text_Line(label, font, font_metrics, color); } // Method to create a text line for the response time // -------------------------------------------------- private RFV_Text_Line readResponseTime(RFV_Input_File input_file, Graphics g) { Font font; FontMetrics font_metrics; Color color; String rt_string; String token; boolean font_flag = false; boolean color_flag = false; // Is only valid in a feedback block // --------------------------------- if (!feedback) RFV_Error.die(23, input_file.getLineNumber()); rt_string = "" + response_time + "ms"; input_file.readOpenBracket(); font = defaults.getTextFont(); font_metrics = defaults.getTextFontMetrics(); color = defaults.getTextForeground(); while (!input_file.isCloseBracket()) { token = input_file.readToken(); if (token.equalsIgnoreCase("font")) { if (!font_flag) { font = readFont(input_file); font_metrics = g.getFontMetrics(font); font_flag = true; } else RFV_Error.die(24, input_file.getLineNumber()); } else if (token.equalsIgnoreCase("foreground")) { if (!color_flag) { color = input_file.readColor(); color_flag = true; } else RFV_Error.die(25, input_file.getLineNumber()); } else RFV_Error.die(26, input_file.getLineNumber(), token); } input_file.readCloseBracket(); return new RFV_Text_Line(rt_string, font, font_metrics, color); } // Reads in a button // ----------------- private RFV_Button readButton(RFV_Input_File input_file, Graphics g) { Font font; FontMetrics font_metrics; Color fg; Color bg; String label; String token; RFV_Button button; boolean font_flag = false; boolean fg_flag = false; boolean bg_flag = false; // Cannot have a button in a feedback block // ---------------------------------------- if (feedback) RFV_Error.die(27, input_file.getLineNumber()); input_file.readOpenBracket(); label = input_file.readLabel(); font = defaults.getButtonFont(); font_metrics = defaults.getButtonFontMetrics(); fg = defaults.getButtonForeground(); bg = defaults.getButtonBackground(); while (!input_file.isCloseBracket()) { token = input_file.readToken(); if (token.equalsIgnoreCase("font")) { if (!font_flag) { font = readFont(input_file); font_metrics = g.getFontMetrics(font); font_flag = true; } else RFV_Error.die(28, input_file.getLineNumber()); } else if (token.equalsIgnoreCase("foreground")) { if (!fg_flag) { fg = input_file.readColor(); fg_flag = true; } else RFV_Error.die(29, input_file.getLineNumber()); } else if (token.equalsIgnoreCase("background")) { if (!bg_flag) { bg = input_file.readColor(); bg_flag = true; } else RFV_Error.die(30, input_file.getLineNumber()); } else RFV_Error.die(31, input_file.getLineNumber()); } input_file.readCloseBracket(); button = new RFV_Button(label, font, font_metrics, fg, bg); button_list.add(button); return button; } // Reads in a font // --------------- private Font readFont(RFV_Input_File input_file) { String font_type; String font_style; int font_size; int font_style_num; input_file.readOpenBracket(); font_type = input_file.readLabel().trim(); font_style = input_file.readToken(); font_size = input_file.readInteger(); input_file.readCloseBracket(); // Set the font // ------------ font_style_num = Font.PLAIN; if (font_style.equalsIgnoreCase("bold")) font_style_num = Font.BOLD; else if (font_style.equalsIgnoreCase("italic")) font_style_num = Font.ITALIC; else if (font_style.equalsIgnoreCase("bold_italic")) font_style_num = Font.BOLD + Font.ITALIC; return new Font(font_type, font_style_num, font_size); } // Read in a static image // ---------------------- private RFV_Image readImage(RFV_Input_File input_file) { String filename; input_file.readOpenBracket(); filename = input_file.readLabel(); input_file.readCloseBracket(); return new RFV_Image(toolkit, media_tracker, filename); } // Read in a Stimulus // ------------------ private RFV_Stimulus readStimulus(RFV_Input_File input_file) { String filenames[]; String token; RFV_Focus_Window stimulus_focus_window; double stimulus_mbss; int speed; boolean focus_w = false; boolean motion_b = false; RFV_Stimulus stimulus; // Cannot have a stimulus in a feedback block // ------------------------------------------ if (feedback) RFV_Error.die(32, input_file.getLineNumber()); // Read in image filenames // ----------------------- filenames = new String[RFV_Focus_Window.NUM_REGIONS + 1]; input_file.readOpenBracket(); for (int i = 0; i < filenames.length; i++) filenames[i] = input_file.readLabel(); // See if focus window or motion blur speed are not default // -------------------------------------------------------- stimulus_focus_window = defaults.getFocusWindow(); stimulus_mbss = defaults.getMotionBlurSpeedSqrd(); while (!input_file.isCloseBracket()) { token = input_file.readToken(); if (token.equalsIgnoreCase("focus_window")) { if (focus_w) RFV_Error.die(33, input_file.getLineNumber()); focus_w = true; stimulus_focus_window = new RFV_Focus_Window(input_file); } else if (token.equalsIgnoreCase("motion_blur_speed")) { if (motion_b) RFV_Error.die(34, input_file.getLineNumber()); motion_b = true; input_file.readOpenBracket(); speed = input_file.readInteger(); input_file.readCloseBracket(); stimulus_mbss = (double) speed * (double) speed; } else RFV_Error.die(35, input_file.getLineNumber(), token); } input_file.readCloseBracket(); stimulus = new RFV_Stimulus(stimulus_focus_window, toolkit, media_tracker, stimulus_mbss, filenames); stimulus_list.add(stimulus); return stimulus; } // Reads in a vertical space that may be // either flexible, or of a fixed height // ------------------------------------- private RFV_V_Space readVSpace(RFV_Input_File input_file) { boolean flexible; int height = 0; input_file.readOpenBracket(); if (input_file.isCloseBracket()) flexible = true; else { flexible = false; height = input_file.readInteger(); } input_file.readCloseBracket(); if (flexible) return new RFV_V_Space(); else return new RFV_V_Space(height); } // Reads in a horizontal space that may be // either flexible, or of a fixed width // --------------------------------------- private RFV_H_Space readHSpace(RFV_Input_File input_file) { boolean flexible; int width = 0; input_file.readOpenBracket(); if (input_file.isCloseBracket()) flexible = true; else { flexible = false; width = input_file.readInteger(); } input_file.readCloseBracket(); if (flexible) return new RFV_H_Space(); else return new RFV_H_Space(width); } // Reads in a feedback label and record the file index // --------------------------------------------------- private void readFeedback(RFV_Input_File input_file) { String token = " "; // Cannot have feedback in a feedback block // ---------------------------------------- if (feedback) RFV_Error.die(36, input_file.getLineNumber()); input_file.readOpenBracket(); if (input_file.isLabel()) token = input_file.readLabel(); else { token = input_file.readToken(); if (token.equalsIgnoreCase("time_limit_expired")) token = RFV.TIME_LIMIT_LABEL; else if (token.equalsIgnoreCase("no_buttons")) token = RFV.NO_BUTTONS_LABEL; else RFV_Error.die(91, input_file.getLineNumber(), token); } checkLabelDuplication(token, input_file); feedback_list.add(input_file.getFileIndex(token)); input_file.matchOpenBrackets(1); } // Method to ensure each component label is unique // ----------------------------------------------- private void checkLabelDuplication(String new_label, RFV_Input_File input_file) { RFV_File_Index index; try { for (int i = 0; i < feedback_list.size(); i++) { index = (RFV_File_Index) feedback_list.get(i); if ((index.label).equals(new_label)) RFV_Error.die(37, input_file.getLineNumber(), new_label); } } catch(IndexOutOfBoundsException e) { RFV_Error.die(38); } } // Checks to see if the feedback labels match the button labels // ------------------------------------------------------------ private void checkFeedbackLabels(RFV_Input_File input_file) { RFV_File_Index index; RFV_Button button; boolean match; try { for (int i = 0; i < feedback_list.size(); i++) { index = (RFV_File_Index) feedback_list.get(i); if ((index.label).equals(RFV.TIME_LIMIT_LABEL)) { if (time_limit == false) RFV_Error.warn(1, input_file.getLineNumber()); } else if ((index.label).equals(RFV.NO_BUTTONS_LABEL)) { if (button_list.size() != 0) RFV_Error.warn(2, input_file.getLineNumber()); } else { match = false; for (int j = 0; j < button_list.size(); j++) { button = (RFV_Button) button_list.get(j); if ((index.label).equals(button.getLabel())) { match = true; break; } } if (!match) RFV_Error.warn(3, input_file.getLineNumber(), index.label); } } } catch(IndexOutOfBoundsException e) { RFV_Error.die(38); } } // Sets the tree offsets so that all display elements // are in the correct position for being drawn // -------------------------------------------------- private void setTreeOffsets() { // Calculate dimensions of elements and // make sure frame has enough room // ------------------------------------ ((RFV_Display_Column) head).calculateDimensions(); if (defaults.getFrameSize().width < head.getWidth() || defaults.getFrameSize().height < head.getHeight()) RFV_Error.die(39); // Calculate the flexible spaces // and the top left offsets // ----------------------------- ((RFV_Display_Column) head).extendHeightTo(defaults.getFrameSize().height); ((RFV_Display_Column) head).calculateOffsets(); } // Returns a linked list of the buttons in this block // -------------------------------------------------- public LinkedList getButtonList() { return button_list; } // Returns a linked list of the stimuli in this block // -------------------------------------------------- public LinkedList getStimulusList() { return stimulus_list; } // Returns a linked list of the feedback file indexes // -------------------------------------------------- public LinkedList getFeedbackList() { return feedback_list; } // Draws the block according to the display tree layout // ---------------------------------------------------- public void drawBlock(Graphics g) { g.setColor(background_color); g.fillRect(0, 0, defaults.getFrameSize().width, defaults.getFrameSize().height); head.draw(g); } // Returns true if time limit is present, false otherwise // ------------------------------------------------------ public boolean isTimeLimit() { return time_limit; } // Returns true if a strict time limit is present, false otherwise // --------------------------------------------------------------- public boolean isTimeLimitStrict() { return time_limit_strict; } // Returns the time limit duration // ------------------------------- public int getTimeLimitDuration() { return time_limit_duration; } // Destroy all stimulus images // --------------------------- public void removeStimulusImages() { RFV_Stimulus stimulus; try { for (int i = 0; i < stimulus_list.size(); i++) { stimulus = (RFV_Stimulus) stimulus_list.get(i); stimulus.removeImages(); } } catch(IndexOutOfBoundsException e) { RFV_Error.die(40); } } }