Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Gtk+/Gnome Application Development
Prev Home Next

Drawing

There are three common situations that require a widget to redraw all or part of itself:

  1. Expose events signal that all or part of a widget's GdkWindow has just become visible on the screen and needs repainting (see the section called Expose Events in the chapter called GDK Basics). A widget's expose_event method performs these redraws.

  2. GTK+ sometimes determines that a widget should be redrawn. This might happen when a widget receives a new size allocation different from its previous size allocation, or when a new theme is loaded. A widget's draw method, usually invoked via gtk_widget_queue_draw() or gtk_widget_queue_clear(), handles this case.

  3. Widgets sometimes decide to redraw themselves. For example, if you change the text of a GtkLabel, the label will redraw itself to reflect the new text. Widget implementations are free to handle this case however they like, but most will use the draw method.

There are two special cases of the second situation. The first occurs when a widget receives or loses the keyboard focus; the second occurs when the widget becomes (or unbecomes) the "default" widget. Widgets should indicate these states visually, but they can often do so without a complete redraw. Thus, there are special draw_focus and draw_default signals to handle them. These signals only have to be implemented if a widget can meaningfully receive the focus or default.

Because there is typically little difference between a widget's draw and expose methods, a common convention is to write a static function to handle both of them. This function is standardly called gtk_whatever_paint(). It's also possible to avoid implementing the draw method, because the default draw method synthesizes an expose event covering the widget's entire allocation and invokes the expose method. (Remember that a synthetic expose event will have its send_event flag set to TRUE; you can use this to distinguish synthetic events.)

The primary reason for distinguishing expose events from other draws is that expose events are marked with the window they occurred on; for widgets with multiple windows such as GtkEv, this can increase efficiency. GtkEv implements two private functions, gtk_ev_paint() and gtk_ev_paint_event_window(), which it uses to implement the expose and draw methods.

Here is the draw method:


static void 
gtk_ev_draw           (GtkWidget        *widget,
                       GdkRectangle     *area)
{
  GdkRectangle event_window_area;
  GdkRectangle intersection;
  GtkEv* ev;

  g_return_if_fail(widget != NULL);
  g_return_if_fail(GTK_IS_EV(widget));

  ev = GTK_EV(widget);

  gtk_ev_paint(ev, area);

  event_window_area = *area;

  if (gdk_rectangle_intersect(area, &ev->event_window_rect, &intersection))
    {
      /* Make the intersection relative to the event window */
      intersection.x -= ev->event_window_rect.x;
      intersection.y -= ev->event_window_rect.y;
      
      gtk_ev_paint_event_window(ev, &intersection);
    }
}
      

And the expose method:


static gint 
gtk_ev_expose         (GtkWidget        *widget,
                       GdkEventExpose   *event)
{  
  if (event->window == widget->window)
    gtk_ev_paint(GTK_EV(widget), &event->area);
  else if (event->window == GTK_EV(widget)->event_window)
    gtk_ev_paint_event_window(GTK_EV(widget), &event->area);
  else
    g_assert_not_reached();

  return TRUE;
}
      

Both the draw and expose methods should be self-explanatory. All the work is done in the two paint functions. Here is gtk_ev_paint(), which renders the main widget window:


static void 
gtk_ev_paint          (GtkEv            *ev,
                       GdkRectangle     *area)
{
  GtkWidget* widget;

  g_return_if_fail(ev != NULL);
  g_return_if_fail(GTK_IS_EV(ev));

  widget = GTK_WIDGET(ev);

  if (!GTK_WIDGET_DRAWABLE (widget))
    return;

  gdk_window_clear_area (widget->window,
                         area->x, 
                         area->y,
                         area->width, 
                         area->height);

  gdk_gc_set_clip_rectangle(widget->style->black_gc, area);

  /* Draw a black rectangle around the event window */

  gdk_draw_rectangle(widget->window,
                     widget->style->black_gc,
                     FALSE,
                     ev->event_window_rect.x - 1, 
                     ev->event_window_rect.y - 1,
                     ev->event_window_rect.width + 2,
                     ev->event_window_rect.height + 2);

  gdk_gc_set_clip_rectangle(widget->style->black_gc, NULL);

  /* Draw text in the description area, if applicable */

  if (ev->buffer)
    {
      GdkRectangle intersection;

      if (gdk_rectangle_intersect(area,
                                  &ev->description_rect,
                                  &intersection))
        {
          static const gint space = 2;
          gint line;
          gint step;
          gint first_baseline;          
          GList* tmp;
      
          step  = widget->style->font->ascent + 
            widget->style->font->descent + space;
      
          first_baseline = ev->description_rect.y + 
            widget->style->font->ascent + space;
      
          line = 0;
      
          tmp = ev->buffer;
      
          while (tmp != NULL)
            {
              gchar** this_event = tmp->data;
              gint i = 0;
              while (this_event[i])
                {
                  gtk_paint_string (widget->style, 
                                    widget->window, 
                                    widget->state,
                                    &intersection, widget, "ev", 
                                    ev->description_rect.x,
                                    first_baseline + line*step,
                                    this_event[i]);
                  ++i;
                  ++line;
                }
          
              /* Bail out if we're off the bottom; the "- 2*step" is
               *  needed because the next baseline may be outside the
               *  redraw area but we are interested in the whole row of 
               *  text, not the baseline. The 2* is because line is one 
               *  larger than we've actually drawn.
               */
              if ((first_baseline + line*step - 2*step) > 
                  (intersection.y + intersection.height))
                break;
          
              tmp = g_list_next(tmp);
            }
        }
    }

  if (GTK_WIDGET_HAS_FOCUS (widget))
    {
      gtk_paint_focus (widget->style, widget->window,
                       area, widget, "ev",
                       widget->allocation.x, widget->allocation.y, 
                       widget->allocation.width-1, widget->allocation.height-1);
    }
}
      

Most of gtk_ev_paint() is GtkEv-specific; it simply draws the contents of the window. Notice that it checks GTK_WIDGET_DRAWABLE() at the beginning; this is required because the draw method may invoke the function. Unsynthesized expose events guarantee that a widget's X window is on-screen and thus this check is not really necessary when responding to expose events.

gtk_ev_paint_event_window() paints the small subwindow; it's a very simple function:


static void 
gtk_ev_paint_event_window  (GtkEv            *ev,
                            GdkRectangle     *area)
{
  GtkWidget* widget;
  gint width;
  gint x, y;
  const char* title;

  g_return_if_fail(ev != NULL);
  g_return_if_fail(GTK_IS_EV(ev));

  widget = GTK_WIDGET(ev);

  if (!GTK_WIDGET_DRAWABLE (widget))
    return;

  title = _("Event Window");

  gdk_window_clear_area (ev->event_window,
                         area->x, 
                         area->y,
                         area->width, 
                         area->height);

  gdk_gc_set_clip_rectangle(widget->style->black_gc, area);

  /* Clearly it would be better to cache this */

  width = gdk_string_width(widget->style->font,
                           title);

  x = (ev->event_window_rect.width - width)/2;
  y = widget->style->font->ascent + 2;

  gdk_draw_string(ev->event_window,
                  widget->style->font,
                  widget->style->black_gc,
                  x, y, 
                  title);

  gdk_gc_set_clip_rectangle(widget->style->black_gc, NULL);
}
      
Gtk+/Gnome Application Development
Prev Home Next

 
 
  Published under free license. Design by Interspire