001    // This file is part of the program FRYSK.
002    //
003    // Copyright 2005, Red Hat Inc.
004    //
005    // FRYSK is free software; you can redistribute it and/or modify it
006    // under the terms of the GNU General Public License as published by
007    // the Free Software Foundation; version 2 of the License.
008    //
009    // FRYSK is distributed in the hope that it will be useful, but
010    // WITHOUT ANY WARRANTY; without even the implied warranty of
011    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012    // General Public License for more details.
013    // type filter text
014    // You should have received a copy of the GNU General Public License
015    // along with FRYSK; if not, write to the Free Software Foundation,
016    // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
017    // 
018    // In addition, as a special exception, Red Hat, Inc. gives You the
019    // additional right to link the code of FRYSK with code not covered
020    // under the GNU General Public License ("Non-GPL Code") and to
021    // distribute linked combinations including the two, subject to the
022    // limitations in this paragraph. Non-GPL Code permitted under this
023    // exception must only link to the code of FRYSK through those well
024    // defined interfaces identified in the file named EXCEPTION found in
025    // the source code files (the "Approved Interfaces"). The files of
026    // Non-GPL Code may instantiate templates or use macros or inline
027    // functions from the Approved Interfaces without causing the
028    // resulting work to be covered by the GNU General Public
029    // License. Only Red Hat, Inc. may make changes or additions to the
030    // list of Approved Interfaces. You must obey the GNU General Public
031    // License in all respects for all of the FRYSK code and other code
032    // used in conjunction with FRYSK except the Non-GPL Code covered by
033    // this exception. If you modify this file, you may extend this
034    // exception to your version of the file, but you are not obligated to
035    // do so. If you do not wish to provide this exception without
036    // modification, you must delete this exception statement from your
037    // version and license this file solely under the GPL without
038    // exception.
039    
040    package frysk.gui.monitor.eventviewer;
041    
042    
043    import java.util.Iterator;
044    import java.util.Observable;
045    import java.util.Observer;
046    
047    import org.freedesktop.cairo.Point;
048    import org.gnu.gdk.Color;
049    import org.gnu.gdk.EventMask;
050    import org.gnu.gdk.GdkCairo;
051    import org.gnu.gtk.Adjustment;
052    import org.gnu.gtk.EventBox;
053    import org.gnu.gtk.HBox;
054    import org.gnu.gtk.Justification;
055    import org.gnu.gtk.Label;
056    import org.gnu.gtk.SizeGroup;
057    import org.gnu.gtk.SizeGroupMode;
058    import org.gnu.gtk.StateType;
059    import org.gnu.gtk.VBox;
060    import org.gnu.gtk.Viewport;
061    import org.gnu.gtk.Widget;
062    import org.gnu.gtk.event.ExposeEvent;
063    import org.gnu.gtk.event.ExposeListener;
064    import org.gnu.gtk.event.MouseEvent;
065    import org.gnu.gtk.event.MouseListener;
066    
067    import com.redhat.ftk.CustomAtkObject;
068    import com.redhat.ftk.CustomDrawingArea;
069    
070    import frysk.gui.monitor.GuiObservable;
071    import frysk.gui.srcwin.SourceWindowFactory;
072    
073    public abstract class TimeLine
074        extends HBox implements MouseListener
075    {
076      
077      public final GuiObservable selected;
078      public final GuiObservable unSelected;
079      
080      static int eventSpacing = 30;
081      
082      private static SizeGroup labelsSizeGroup = new SizeGroup(SizeGroupMode.HORIZONTAL);
083      private static SizeGroup drawingAreaSizeGroup = new SizeGroup(SizeGroupMode.HORIZONTAL);
084      
085      private static final int MINIMUM_HEIGHT = 15;
086      private static int MINIMUM_WIDTH = 0 ;
087      
088      String labelString;
089      String name;
090      
091      private Viewport viewport;
092      private boolean isSelected;
093      
094      private boolean isDead;
095      Label label;
096      
097      int startIndex = 0;
098      int endIndex;
099      
100      protected static Color SELECTED_COLOR = new Color(55535, 55535, 55535);
101      private static Color UNSELECTED_COLOR = Color.WHITE;
102      
103      public TimeLine(String name, TimeLineSelectionManager timeLineSelectionManager){
104        super(false,0);
105      
106        this.selected = new GuiObservable();
107        this.unSelected = new GuiObservable();
108        
109        this.setBorderWidth(0);
110        
111        this.name = name;
112        this.labelString = "";
113        
114        label = new Label(name);
115        label.setAlignment(0.4,0.5);
116        label.setJustification(Justification.RIGHT);
117        
118        EventBox labelEventBox = new EventBox();
119        labelEventBox.add(label);
120        label.getParent().setBackgroundColor(StateType.NORMAL, UNSELECTED_COLOR);
121        
122        labelEventBox.addListener((MouseListener)this);
123        this.addListener((MouseListener)this);
124        Viewport labeViewport = new Viewport(null,null);
125        labeViewport.add(labelEventBox);
126    //    labeViewport.setShadowType(ShadowType.NONE);
127        addToLabelsSizeGroup(label);
128        
129        final TimeLineDrawingArea drawingArea = getTimeLineDrawingArea();
130        
131        viewport = new Viewport(null,null);
132        viewport.add(drawingArea);
133        viewport.setMinimumSize(0, drawingArea.getMinimumHeight());
134    //    viewport.setShadowType(ShadowType.NONE);
135        
136        VBox vBox = new VBox(false,0);
137        
138        this.packStart(labeViewport, false, false, 0);
139        this.packStart(viewport,true,true,0);
140        this.packEnd(vBox, false, false, 0);
141        
142        timeLineSelectionManager.addTimeLine(this);
143        
144        EventManager.theManager.getEventsList().itemAdded.addObserver(redrawObserver);
145      }
146      
147      public void setStartIdnex(int index){
148        this.startIndex = index;
149      }
150      
151      public void setEndIndex(int index){
152        this.endIndex = index;
153      }
154      
155      public void setHAdjustment(Adjustment adjustment){
156        this.viewport.setHAdjustment(adjustment);
157      }
158      
159      protected TimeLineDrawingArea getTimeLineDrawingArea(){
160        return new TimeLineDrawingArea();
161      }
162      
163      public void setName(String name){
164        this.name = name;
165        this.label.setMarkup(name + " "+ this.labelString);
166      }
167      
168      protected class TimeLineDrawingArea extends CustomDrawingArea implements ExposeListener, MouseListener{
169        
170        public TimeLineDrawingArea ()
171        {
172          TimeLine.addToDrawingAreaSizeGroup(this);
173          
174          CustomAtkObject atkObject = new CustomAtkObject(this);
175          
176          atkObject.setName(name+" TimeLine");
177          atkObject.setDescription("TimeLine");
178          
179          this.setAcessible(atkObject);
180          
181          
182          this.addListener((ExposeListener)this);
183          this.addListener((MouseListener) this);
184          this.setEvents(EventMask.ALL_EVENTS_MASK);
185              
186          this.setMinimumSize(MINIMUM_WIDTH , MINIMUM_HEIGHT);
187        }
188        
189        public int getMinimumHeight ()
190        {
191          return MINIMUM_HEIGHT;
192        }
193    
194        /**
195         * Given x and y coordinates returns the Event which
196         * has been drawn there.
197         */
198        private Event xy2Event(double x, double y){
199          int index = (int) ((x)/(Event.getWidth() + eventSpacing));
200          return EventManager.theManager.eventAtIndex(index);
201        }
202        
203        public boolean mouseEvent (MouseEvent mouseEvent)
204        {
205          if(mouseEvent.isOfType(MouseEvent.Type.BUTTON_PRESS)){
206            Event event = this.xy2Event(mouseEvent.getX(), mouseEvent.getY());
207            if(event != null && ownsEvent(event)){
208              event.select();
209            }
210          }
211          
212          if(mouseEvent.getClickType() == MouseEvent.DOUBLE_CLICK && mouseEvent.isOfType(MouseEvent.Type.BUTTON_PRESS)){
213            Event event = this.xy2Event(mouseEvent.getX(), mouseEvent.getY());
214            if(event != null && event.getStackFrame()!=null && ownsEvent(event)){
215               SourceWindowFactory.createSourceWindow(event.getStackFrame());
216              return true;
217            }
218          }
219          return false;
220        }
221    
222        public boolean exposeEvent(ExposeEvent exposeEvent) {
223    //      if(exposeEvent.isOfType(ExposeEvent.Type.NO_EXPOSE) || !exposeEvent.getWindow().equals(this.getWindow()))
224    //        return false;
225        
226          GdkCairo cairo = new GdkCairo(this.getWindow());
227          
228          int x = 0;
229          int y = 0;
230          int w = this.getWindow().getWidth();
231          int h = exposeEvent.getArea().getHeight();  
232          
233          // White background
234          if(isSelected){
235            cairo.setSourceColor(SELECTED_COLOR);
236          }else{
237            cairo.setSourceColor(UNSELECTED_COLOR);
238          }
239          
240          cairo.rectangle(new Point(x,y), new Point(w, this.getWindow().getHeight()));
241          cairo.fill();
242          
243          
244          cairo.setSourceColor(SELECTED_COLOR);
245          cairo.rectangle(new Point(x,y+h), new Point(w,y+h-1));
246          cairo.fill();
247          
248          // draw events
249          Iterator iterator = EventManager.theManager.getEventsList().iterator();
250          
251          int eventX = 0;
252          int eventY = 0;
253          
254          while (iterator.hasNext())
255            {
256              Event event = (Event) iterator.next();
257              
258              if(TimeLine.this.ownsEvent(event)){
259                eventX = x + eventSpacing/2 + (event.getIndex())*(Event.getWidth() + eventSpacing);
260                eventY = y + h - Event.getHeight();
261                event.setXY(eventX, eventY);
262                event.draw(cairo);
263              }
264            }
265          
266          if(eventX >= w){
267            MINIMUM_WIDTH = w + MINIMUM_HEIGHT;
268            //this.setMinimumSize(MINIMUM_WIDTH , MINIMUM_HEIGHT);
269          }
270         
271          if(isDead){
272            // White background
273            cairo.setSourceRGBA(1,1,1, 0.5);
274            cairo.rectangle(new Point(x,y), new Point(w, this.getWindow().getHeight()));
275            cairo.fill();
276          }
277          
278          this.setMinimumSize(MINIMUM_WIDTH , MINIMUM_HEIGHT);
279          
280          return false;
281        }
282      }
283        
284      public boolean mouseEvent(MouseEvent event){
285        if(event.isOfType(MouseEvent.Type.BUTTON_PRESS)){
286          this.select();
287        }
288        
289        return false;
290      }
291      /**
292       * Returns wether this time line is associated with the given
293       * event
294       * @param event the event to be checked
295       * @return true of the given event is associated with this TimeLine
296       * false otherwise.
297       */
298      public abstract boolean ownsEvent(Event event);
299      
300     public void select(){
301        this.isSelected = true;
302        this.label.getParent().setBackgroundColor(StateType.NORMAL, SELECTED_COLOR);
303        this.selected.notifyObservers();
304        this.draw();
305      }
306      
307      public void unselect(){
308        this.isSelected = false;
309        this.label.getParent().setBackgroundColor(StateType.NORMAL, UNSELECTED_COLOR);
310        this.unSelected.notifyObservers();
311        this.draw();
312      }
313      
314      public boolean isSelected(){
315        return this.isSelected;
316      }
317      
318      public void timeLineDead(){
319        this.isDead = true;
320        
321        int grayFactor = 3;
322        this.label.setForegroundColor(StateType.NORMAL, new Color(65535/grayFactor, 65535/grayFactor, 65535/grayFactor));
323        this.labelString = this.labelString + "\n<i>terminated</i>";
324        this.label.setMarkup(name + " " + labelString);
325      }
326      
327      public static void addToLabelsSizeGroup(Widget widget){
328        labelsSizeGroup.addWidget(widget);
329      }
330      
331      public static void addToDrawingAreaSizeGroup(Widget widget){
332        drawingAreaSizeGroup.addWidget(widget);
333      }
334      
335      private Observer redrawObserver = new Observer(){
336        private int count;
337        
338        public void update (Observable observable, Object arg)
339        {
340          if(count++ == 2){
341            draw();
342            count=0;
343          }
344        }
345      };
346      
347      protected void finalize () throws Throwable
348      {
349        EventManager.theManager.getEventsList().itemAdded.deleteObserver(redrawObserver);
350        super.finalize();
351      }
352      
353    }