tabpopup.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity popup window thing showing windows you can tab to */
00004 
00005 /* 
00006  * Copyright (C) 2001 Havoc Pennington
00007  * Copyright (C) 2002 Red Hat, Inc.
00008  * Copyright (C) 2005 Elijah Newren
00009  * 
00010  * This program is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU General Public License as
00012  * published by the Free Software Foundation; either version 2 of the
00013  * License, or (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * General Public License for more details.
00019  * 
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00023  * 02111-1307, USA.
00024  */
00025 
00026 #include <config.h>
00027 
00028 #include "util.h"
00029 #include "core.h"
00030 #include "tabpopup.h"
00031 /* FIXME these two includes are 100% broken ...
00032  */
00033 #include "../core/workspace.h"
00034 #include "../core/frame-private.h"
00035 #include "draw-workspace.h"
00036 #include <gtk/gtk.h>
00037 #include <math.h>
00038 
00039 #define OUTSIDE_SELECT_RECT 2
00040 #define INSIDE_SELECT_RECT 2
00041 
00042 typedef struct _TabEntry TabEntry;
00043 
00044 struct _TabEntry
00045 {
00046   MetaTabEntryKey  key;
00047   char            *title;
00048   GdkPixbuf       *icon, *dimmed_icon;
00049   GtkWidget       *widget;
00050   GdkRectangle     rect;
00051   GdkRectangle     inner_rect;
00052   guint blank : 1;
00053 };
00054 
00055 struct _MetaTabPopup
00056 {
00057   GtkWidget *window;
00058   GtkWidget *label;
00059   GList *current;
00060   GList *entries;
00061   TabEntry *current_selected_entry;
00062   GtkWidget *outline_window;
00063   gboolean outline;
00064 };
00065 
00066 static GtkWidget* selectable_image_new (GdkPixbuf *pixbuf);
00067 static void       select_image         (GtkWidget *widget);
00068 static void       unselect_image       (GtkWidget *widget);
00069 
00070 static GtkWidget* selectable_workspace_new (MetaWorkspace *workspace);
00071 static void       select_workspace         (GtkWidget *widget);
00072 static void       unselect_workspace       (GtkWidget *widget);
00073 
00074 static gboolean
00075 outline_window_expose (GtkWidget      *widget,
00076                        GdkEventExpose *event,
00077                        gpointer        data)
00078 {
00079   MetaTabPopup *popup;
00080   TabEntry *te;  
00081   
00082   popup = data;
00083 
00084   if (!popup->outline || popup->current_selected_entry == NULL)
00085     return FALSE;
00086 
00087   te = popup->current_selected_entry;
00088   
00089   gdk_draw_rectangle (widget->window,
00090                       widget->style->white_gc,
00091                       FALSE,
00092                       0, 0,
00093                       te->rect.width - 1,
00094                       te->rect.height - 1);
00095 
00096   gdk_draw_rectangle (widget->window,
00097                       widget->style->white_gc,
00098                       FALSE,
00099                       te->inner_rect.x - 1, te->inner_rect.y - 1,
00100                       te->inner_rect.width + 1,
00101                       te->inner_rect.height + 1);
00102 
00103   return FALSE;
00104 }
00105 
00106 static GdkPixbuf*
00107 dimm_icon (GdkPixbuf *pixbuf)
00108 {
00109   int x, y, pixel_stride, row_stride;
00110   guchar *row, *pixels;
00111   int w, h;
00112   GdkPixbuf *dimmed_pixbuf;
00113 
00114   if (gdk_pixbuf_get_has_alpha (pixbuf))
00115     {
00116       dimmed_pixbuf = gdk_pixbuf_copy (pixbuf);
00117     }
00118   else
00119     {
00120       dimmed_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
00121     }
00122 
00123   w = gdk_pixbuf_get_width (dimmed_pixbuf);
00124   h = gdk_pixbuf_get_height (dimmed_pixbuf);      
00125 
00126   pixel_stride = 4;
00127 
00128   row = gdk_pixbuf_get_pixels (dimmed_pixbuf);
00129   row_stride = gdk_pixbuf_get_rowstride (dimmed_pixbuf);
00130 
00131   for (y = 0; y < h; y++)
00132     {
00133       pixels = row;                     
00134       for (x = 0; x < w; x++) 
00135         {
00136           pixels[3] /= 2;                               
00137           pixels += pixel_stride;
00138         }                       
00139       row += row_stride;
00140     }
00141   return dimmed_pixbuf;
00142 }
00143 
00144 static TabEntry*  
00145 tab_entry_new (const MetaTabEntry *entry, 
00146                gint                screen_width,
00147                gboolean            outline)
00148 {
00149   TabEntry *te;
00150   
00151   te = g_new (TabEntry, 1);
00152   te->key = entry->key;
00153   te->title = NULL;
00154   if (entry->title)
00155     {
00156       gchar *str;
00157       gchar *tmp;
00158       gchar *formatter = "%s";
00159 
00160       str = meta_g_utf8_strndup (entry->title, 4096);
00161 
00162       if (entry->hidden)
00163         {
00164           formatter = "[%s]";
00165         }
00166 
00167       tmp = g_markup_printf_escaped (formatter, str);
00168       g_free (str);
00169       str = tmp;
00170 
00171       if (entry->demands_attention) 
00172         {         
00173           /* Escape the whole line of text then markup the text and 
00174            * copy it back into the original buffer.
00175            */
00176           tmp = g_strdup_printf ("<b>%s</b>", str);
00177           g_free (str);
00178           str = tmp;
00179         }
00180 
00181         te->title=g_strdup(str);
00182 
00183       g_free (str);
00184     }
00185   te->widget = NULL;
00186   te->icon = entry->icon;
00187   te->blank = entry->blank;
00188   te->dimmed_icon = NULL;
00189   if (te->icon)
00190     {
00191       g_object_ref (G_OBJECT (te->icon));
00192       if (entry->hidden)
00193         te->dimmed_icon = dimm_icon (entry->icon);
00194     }
00195   
00196   if (outline)
00197     {
00198       te->rect.x = entry->rect.x;
00199       te->rect.y = entry->rect.y;
00200       te->rect.width = entry->rect.width;
00201       te->rect.height = entry->rect.height;
00202 
00203       te->inner_rect.x = entry->inner_rect.x;
00204       te->inner_rect.y = entry->inner_rect.y;
00205       te->inner_rect.width = entry->inner_rect.width;
00206       te->inner_rect.height = entry->inner_rect.height;
00207     }
00208   return te;
00209 }
00210 
00211 MetaTabPopup*
00212 meta_ui_tab_popup_new (const MetaTabEntry *entries,
00213                        int                 screen_number,
00214                        int                 entry_count,
00215                        int                 width,
00216                        gboolean            outline)
00217 {
00218   MetaTabPopup *popup;
00219   int i, left, right, top, bottom;
00220   int height;
00221   GtkWidget *table;
00222   GtkWidget *vbox;
00223   GtkWidget *align;
00224   GList *tmp;
00225   GtkWidget *frame;
00226   int max_label_width; /* the actual max width of the labels we create */
00227   AtkObject *obj;
00228   GdkScreen *screen;
00229   int screen_width;
00230   
00231   popup = g_new (MetaTabPopup, 1);
00232 
00233   popup->outline_window = gtk_window_new (GTK_WINDOW_POPUP);
00234 
00235   screen = gdk_display_get_screen (gdk_display_get_default (),
00236                                    screen_number);
00237   gtk_window_set_screen (GTK_WINDOW (popup->outline_window),
00238                          screen);
00239 
00240   gtk_widget_set_app_paintable (popup->outline_window, TRUE);
00241   gtk_widget_realize (popup->outline_window);
00242 
00243   g_signal_connect (G_OBJECT (popup->outline_window), "expose_event",
00244                     G_CALLBACK (outline_window_expose), popup);
00245   
00246   popup->window = gtk_window_new (GTK_WINDOW_POPUP);
00247 
00248   gtk_window_set_screen (GTK_WINDOW (popup->window),
00249                          screen);
00250 
00251   gtk_window_set_position (GTK_WINDOW (popup->window),
00252                            GTK_WIN_POS_CENTER_ALWAYS);
00253   /* enable resizing, to get never-shrink behavior */
00254   gtk_window_set_resizable (GTK_WINDOW (popup->window),
00255                             TRUE);
00256   popup->current = NULL;
00257   popup->entries = NULL;
00258   popup->current_selected_entry = NULL;
00259   popup->outline = outline;
00260 
00261   screen_width = gdk_screen_get_width (screen);
00262   for (i = 0; i < entry_count; ++i)
00263     {
00264       TabEntry* new_entry = tab_entry_new (&entries[i], screen_width, outline);
00265       popup->entries = g_list_prepend (popup->entries, new_entry);
00266     }
00267 
00268   popup->entries = g_list_reverse (popup->entries);
00269     
00270   g_assert (width > 0);
00271   height = i / width;
00272   if (i % width)
00273     height += 1;
00274 
00275   table = gtk_table_new (height, width, FALSE);
00276   vbox = gtk_vbox_new (FALSE, 0);
00277   
00278   frame = gtk_frame_new (NULL);
00279   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
00280   gtk_container_set_border_width (GTK_CONTAINER (table), 1);
00281   gtk_container_add (GTK_CONTAINER (popup->window),
00282                      frame);
00283   gtk_container_add (GTK_CONTAINER (frame),
00284                      vbox);
00285 
00286   align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
00287 
00288   gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);
00289 
00290   gtk_container_add (GTK_CONTAINER (align),
00291                      table);
00292 
00293   popup->label = gtk_label_new ("");
00294 
00295   /* Set the accessible role of the label to a status bar so it
00296    * will emit name changed events that can be used by screen
00297    * readers.
00298    */
00299   obj = gtk_widget_get_accessible (popup->label);
00300   atk_object_set_role (obj, ATK_ROLE_STATUSBAR);
00301 
00302   gtk_misc_set_padding (GTK_MISC (popup->label), 3, 3);
00303 
00304   gtk_box_pack_end (GTK_BOX (vbox), popup->label, FALSE, FALSE, 0);
00305 
00306   max_label_width = 0;
00307   top = 0;
00308   bottom = 1;
00309   tmp = popup->entries;
00310 
00311   while (tmp && top < height)
00312     {      
00313       left = 0;
00314       right = 1;
00315 
00316       while (tmp && left < width)
00317         {
00318           GtkWidget *image;
00319           GtkRequisition req;
00320 
00321           TabEntry *te;
00322 
00323           te = tmp->data;
00324 
00325           if (te->blank)
00326             {
00327               /* just stick a widget here to avoid special cases */
00328               image = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
00329             }
00330           else if (outline)
00331             {
00332               if (te->dimmed_icon)
00333                 {
00334                   image = selectable_image_new (te->dimmed_icon);
00335                 }
00336               else 
00337                 {
00338                   image = selectable_image_new (te->icon);
00339                 }
00340 
00341               gtk_misc_set_padding (GTK_MISC (image),
00342                                     INSIDE_SELECT_RECT + OUTSIDE_SELECT_RECT + 1,
00343                                     INSIDE_SELECT_RECT + OUTSIDE_SELECT_RECT + 1);
00344               gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.5);
00345             }   
00346           else
00347             {
00348               image = selectable_workspace_new ((MetaWorkspace *) te->key);
00349             }
00350 
00351           te->widget = image;
00352 
00353           gtk_table_attach (GTK_TABLE (table),
00354                             te->widget,
00355                             left, right,           top, bottom,
00356                             0,                     0,
00357                             0,                     0);
00358 
00359           /* Efficiency rules! */
00360           gtk_label_set_markup (GTK_LABEL (popup->label),
00361                               te->title);
00362           gtk_widget_size_request (popup->label, &req);
00363           max_label_width = MAX (max_label_width, req.width);
00364           
00365           tmp = tmp->next;
00366           
00367           ++left;
00368           ++right;
00369         }
00370       
00371       ++top;
00372       ++bottom;
00373     }
00374 
00375   /* remove all the temporary text */
00376   gtk_label_set_text (GTK_LABEL (popup->label), "");
00377   /* Make it so that we ellipsize if the text is too long */
00378   gtk_label_set_ellipsize (GTK_LABEL (popup->label), PANGO_ELLIPSIZE_END);
00379 
00380   /* Limit the window size to no bigger than screen_width/4 */
00381   if (max_label_width>(screen_width/4)) 
00382     {
00383       max_label_width = screen_width/4;
00384     }
00385 
00386   max_label_width += 20; /* add random padding */
00387   
00388   gtk_window_set_default_size (GTK_WINDOW (popup->window),
00389                                max_label_width,
00390                                -1);
00391   
00392   return popup;
00393 }
00394 
00395 static void
00396 free_tab_entry (gpointer data, gpointer user_data)
00397 {
00398   TabEntry *te;
00399 
00400   te = data;
00401   
00402   g_free (te->title);
00403   if (te->icon)
00404     g_object_unref (G_OBJECT (te->icon));
00405   if (te->dimmed_icon)
00406     g_object_unref (G_OBJECT (te->dimmed_icon));
00407 
00408   g_free (te);
00409 }
00410 
00411 void
00412 meta_ui_tab_popup_free (MetaTabPopup *popup)
00413 {
00414   meta_verbose ("Destroying tab popup window\n");
00415   
00416   gtk_widget_destroy (popup->outline_window);
00417   gtk_widget_destroy (popup->window);
00418   
00419   g_list_foreach (popup->entries, free_tab_entry, NULL);
00420 
00421   g_list_free (popup->entries);
00422   
00423   g_free (popup);
00424 }
00425 
00426 void
00427 meta_ui_tab_popup_set_showing (MetaTabPopup *popup,
00428                                gboolean      showing)
00429 {
00430   if (showing)
00431     {
00432       gtk_widget_show_all (popup->window);
00433     }
00434   else
00435     {
00436       if (GTK_WIDGET_VISIBLE (popup->window))
00437         {
00438           meta_verbose ("Hiding tab popup window\n");
00439           gtk_widget_hide (popup->window);
00440           meta_core_increment_event_serial (gdk_display);
00441         }
00442     }
00443 }
00444 
00445 static void
00446 display_entry (MetaTabPopup *popup,
00447                TabEntry     *te)
00448 {
00449   GdkRectangle rect;
00450   GdkRegion *region;
00451   GdkRegion *inner_region;
00452 
00453   
00454   if (popup->current_selected_entry)
00455   {
00456     if (popup->outline)
00457       unselect_image (popup->current_selected_entry->widget);
00458     else
00459       unselect_workspace (popup->current_selected_entry->widget);
00460   }
00461   
00462   gtk_label_set_markup (GTK_LABEL (popup->label), te->title);
00463 
00464   if (popup->outline)
00465     select_image (te->widget);
00466   else
00467     select_workspace (te->widget);
00468 
00469   if (popup->outline)
00470     {
00471       /* Do stuff behind gtk's back */
00472       gdk_window_hide (popup->outline_window->window);
00473       meta_core_increment_event_serial (gdk_display);
00474   
00475       rect = te->rect;
00476       rect.x = 0;
00477       rect.y = 0;
00478 
00479       gdk_window_move_resize (popup->outline_window->window,
00480                               te->rect.x, te->rect.y,
00481                               te->rect.width, te->rect.height);
00482   
00483       gdk_window_set_background (popup->outline_window->window,
00484                                  &popup->outline_window->style->black);
00485   
00486       region = gdk_region_rectangle (&rect);
00487       inner_region = gdk_region_rectangle (&te->inner_rect);
00488       gdk_region_subtract (region, inner_region);
00489       gdk_region_destroy (inner_region);
00490   
00491       gdk_window_shape_combine_region (popup->outline_window->window,
00492                                        region,
00493                                        0, 0);
00494 
00495       gdk_region_destroy (region);
00496   
00497       /* This should piss off gtk a bit, but we don't want to raise
00498        * above the tab popup.  So, instead of calling gtk_widget_show,
00499        * we manually set the window as mapped and then manually map it
00500        * with gdk functions.
00501        */
00502       GTK_WIDGET_SET_FLAGS (popup->outline_window, GTK_MAPPED);
00503       gdk_window_show_unraised (popup->outline_window->window);
00504     }
00505 
00506   /* Must be before we handle an expose for the outline window */
00507   popup->current_selected_entry = te;
00508 }
00509 
00510 void
00511 meta_ui_tab_popup_forward (MetaTabPopup *popup)
00512 {
00513   if (popup->current != NULL)
00514     popup->current = popup->current->next;
00515 
00516   if (popup->current == NULL)
00517     popup->current = popup->entries;
00518   
00519   if (popup->current != NULL)
00520     {
00521       TabEntry *te;
00522 
00523       te = popup->current->data;
00524 
00525       display_entry (popup, te);
00526     }
00527 }
00528 
00529 void
00530 meta_ui_tab_popup_backward (MetaTabPopup *popup)
00531 {
00532   if (popup->current != NULL)
00533     popup->current = popup->current->prev;
00534 
00535   if (popup->current == NULL)
00536     popup->current = g_list_last (popup->entries);
00537   
00538   if (popup->current != NULL)
00539     {
00540       TabEntry *te;
00541 
00542       te = popup->current->data;
00543 
00544       display_entry (popup, te);
00545     }
00546 }
00547 
00548 MetaTabEntryKey
00549 meta_ui_tab_popup_get_selected (MetaTabPopup *popup)
00550 {
00551   if (popup->current)
00552     {
00553       TabEntry *te;
00554 
00555       te = popup->current->data;
00556 
00557       return te->key;
00558     }
00559   else
00560     return (MetaTabEntryKey)None;
00561 }
00562 
00563 void
00564 meta_ui_tab_popup_select (MetaTabPopup *popup,
00565                           MetaTabEntryKey key)
00566 {
00567   GList *tmp;
00568 
00569   /* Note, "key" may not be in the list of entries; other code assumes
00570    * it's OK to pass in a key that isn't.
00571    */
00572   
00573   tmp = popup->entries;
00574   while (tmp != NULL)
00575     {
00576       TabEntry *te;
00577 
00578       te = tmp->data;
00579 
00580       if (te->key == key)
00581         {
00582           popup->current = tmp;
00583           
00584           display_entry (popup, te);
00585 
00586           return;
00587         }
00588       
00589       tmp = tmp->next;
00590     }
00591 }
00592 
00593 #define META_TYPE_SELECT_IMAGE            (meta_select_image_get_type ())
00594 #define META_SELECT_IMAGE(obj)            (GTK_CHECK_CAST ((obj), META_TYPE_SELECT_IMAGE, MetaSelectImage))
00595 
00596 typedef struct _MetaSelectImage       MetaSelectImage;
00597 typedef struct _MetaSelectImageClass  MetaSelectImageClass;
00598 
00599 struct _MetaSelectImage
00600 {
00601   GtkImage parent_instance;
00602   guint selected : 1;
00603 };
00604 
00605 struct _MetaSelectImageClass
00606 {
00607   GtkImageClass parent_class;
00608 };
00609 
00610 
00611 static GType meta_select_image_get_type (void) G_GNUC_CONST;
00612 
00613 static GtkWidget*
00614 selectable_image_new (GdkPixbuf *pixbuf)
00615 {
00616   GtkWidget *w;
00617 
00618   w = g_object_new (meta_select_image_get_type (), NULL);
00619   gtk_image_set_from_pixbuf (GTK_IMAGE (w), pixbuf); 
00620 
00621   return w;
00622 }
00623 
00624 static void
00625 select_image (GtkWidget *widget)
00626 {
00627   META_SELECT_IMAGE (widget)->selected = TRUE;
00628   gtk_widget_queue_draw (widget);
00629 }
00630 
00631 static void
00632 unselect_image (GtkWidget *widget)
00633 {
00634   META_SELECT_IMAGE (widget)->selected = FALSE;
00635   gtk_widget_queue_draw (widget);
00636 }
00637 
00638 static void     meta_select_image_class_init   (MetaSelectImageClass *klass);
00639 static gboolean meta_select_image_expose_event (GtkWidget            *widget,
00640                                                 GdkEventExpose       *event);
00641 
00642 static GtkImageClass *parent_class;
00643 
00644 GType
00645 meta_select_image_get_type (void)
00646 {
00647   static GtkType image_type = 0;
00648 
00649   if (!image_type)
00650     {
00651       static const GTypeInfo image_info =
00652       {
00653         sizeof (MetaSelectImageClass),
00654         NULL,           /* base_init */
00655         NULL,           /* base_finalize */
00656         (GClassInitFunc) meta_select_image_class_init,
00657         NULL,           /* class_finalize */
00658         NULL,           /* class_data */
00659         sizeof (MetaSelectImage),
00660         16,             /* n_preallocs */
00661         (GInstanceInitFunc) NULL,
00662       };
00663 
00664       image_type = g_type_register_static (GTK_TYPE_IMAGE, "MetaSelectImage", &image_info, 0);
00665     }
00666 
00667   return image_type;
00668 }
00669 
00670 static void
00671 meta_select_image_class_init (MetaSelectImageClass *klass)
00672 {
00673   GtkWidgetClass *widget_class;
00674   
00675   parent_class = gtk_type_class (gtk_image_get_type ());
00676 
00677   widget_class = GTK_WIDGET_CLASS (klass);
00678   
00679   widget_class->expose_event = meta_select_image_expose_event;
00680 }
00681 
00682 static gboolean
00683 meta_select_image_expose_event (GtkWidget      *widget,
00684                                 GdkEventExpose *event)
00685 {
00686   if (META_SELECT_IMAGE (widget)->selected)
00687     {
00688       int x, y, w, h;
00689       GtkMisc *misc;
00690 
00691       misc = GTK_MISC (widget);
00692       
00693       x = (widget->allocation.x * (1.0 - misc->xalign) +
00694            (widget->allocation.x + widget->allocation.width
00695             - (widget->requisition.width - misc->xpad * 2)) *
00696            misc->xalign) + 0.5;
00697       y = (widget->allocation.y * (1.0 - misc->yalign) +
00698            (widget->allocation.y + widget->allocation.height
00699             - (widget->requisition.height - misc->ypad * 2)) *
00700            misc->yalign) + 0.5;
00701 
00702       x -= INSIDE_SELECT_RECT + 1;
00703       y -= INSIDE_SELECT_RECT + 1;      
00704       
00705       w = widget->requisition.width - OUTSIDE_SELECT_RECT * 2 - 1;
00706       h = widget->requisition.height - OUTSIDE_SELECT_RECT * 2 - 1;
00707 
00708       gdk_draw_rectangle (widget->window,
00709                           widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
00710                           FALSE,
00711                           x, y, w, h);
00712       gdk_draw_rectangle (widget->window,
00713                           widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
00714                           FALSE,
00715                           x - 1, y - 1, w + 2, h + 2);
00716       
00717 #if 0
00718       gdk_draw_rectangle (widget->window,
00719                           widget->style->bg_gc[GTK_STATE_SELECTED],
00720                           TRUE,
00721                           x, y, w, h);
00722 #endif
00723 #if 0      
00724       gtk_paint_focus (widget->style, widget->window,
00725                        &event->area, widget, "meta-tab-image",
00726                        x, y, w, h);
00727 #endif
00728     }
00729 
00730   return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
00731 }
00732 
00733 #define META_TYPE_SELECT_WORKSPACE   (meta_select_workspace_get_type ())
00734 #define META_SELECT_WORKSPACE(obj)   (GTK_CHECK_CAST ((obj), META_TYPE_SELECT_WORKSPACE, MetaSelectWorkspace))
00735 
00736 typedef struct _MetaSelectWorkspace       MetaSelectWorkspace;
00737 typedef struct _MetaSelectWorkspaceClass  MetaSelectWorkspaceClass;
00738 
00739 struct _MetaSelectWorkspace
00740 {
00741   GtkDrawingArea parent_instance;
00742   MetaWorkspace *workspace;
00743   guint selected : 1;
00744 };
00745 
00746 struct _MetaSelectWorkspaceClass
00747 {
00748   GtkDrawingAreaClass parent_class;
00749 };
00750 
00751 
00752 static GType meta_select_workspace_get_type (void) G_GNUC_CONST;
00753 
00754 #define SELECT_OUTLINE_WIDTH 2
00755 #define MINI_WORKSPACE_WIDTH 48
00756 
00757 static GtkWidget*
00758 selectable_workspace_new (MetaWorkspace *workspace)
00759 {
00760   GtkWidget *widget;
00761   double screen_aspect;
00762   
00763   widget = g_object_new (meta_select_workspace_get_type (), NULL);
00764 
00765   screen_aspect = (double) workspace->screen->rect.height /
00766                   (double) workspace->screen->rect.width;
00767   
00768   /* account for select rect */ 
00769   gtk_widget_set_size_request (widget,
00770                                MINI_WORKSPACE_WIDTH + SELECT_OUTLINE_WIDTH * 2,
00771                                MINI_WORKSPACE_WIDTH * screen_aspect + SELECT_OUTLINE_WIDTH * 2);
00772 
00773   META_SELECT_WORKSPACE (widget)->workspace = workspace;
00774 
00775   return widget;
00776 }
00777 
00778 static void
00779 select_workspace (GtkWidget *widget)
00780 {
00781   META_SELECT_WORKSPACE(widget)->selected = TRUE;
00782   gtk_widget_queue_draw (widget);
00783 }
00784 
00785 static void
00786 unselect_workspace (GtkWidget *widget)
00787 {
00788   META_SELECT_WORKSPACE (widget)->selected = FALSE;
00789   gtk_widget_queue_draw (widget);
00790 }
00791 
00792 static void meta_select_workspace_class_init (MetaSelectWorkspaceClass *klass);
00793 
00794 static gboolean meta_select_workspace_expose_event (GtkWidget      *widget,
00795                                                     GdkEventExpose *event);
00796 
00797 GType
00798 meta_select_workspace_get_type (void)
00799 {
00800   static GtkType workspace_type = 0;
00801 
00802   if (!workspace_type)
00803     {
00804       static const GTypeInfo workspace_info =
00805       {
00806         sizeof (MetaSelectWorkspaceClass),
00807         NULL,           /* base_init */
00808         NULL,           /* base_finalize */
00809         (GClassInitFunc) meta_select_workspace_class_init,
00810         NULL,           /* class_finalize */
00811         NULL,           /* class_data */
00812         sizeof (MetaSelectWorkspace),
00813         16,             /* n_preallocs */
00814         (GInstanceInitFunc) NULL,
00815       };
00816 
00817       workspace_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, 
00818                                                "MetaSelectWorkspace", 
00819                                                &workspace_info, 
00820                                                0);
00821     }
00822 
00823   return workspace_type;
00824 }
00825 
00826 static void
00827 meta_select_workspace_class_init (MetaSelectWorkspaceClass *klass)
00828 {
00829   GtkWidgetClass *widget_class;
00830   
00831   widget_class = GTK_WIDGET_CLASS (klass);
00832   
00833   widget_class->expose_event = meta_select_workspace_expose_event;
00834 }
00835 
00841 static WnckWindowDisplayInfo
00842 meta_convert_meta_to_wnck (MetaWindow *window, MetaScreen *screen)
00843 {
00844   WnckWindowDisplayInfo wnck_window;
00845   wnck_window.icon = window->icon;
00846   wnck_window.mini_icon = window->mini_icon;
00847   
00848   wnck_window.is_active = FALSE;
00849   if (window == window->display->expected_focus_window)
00850     wnck_window.is_active = TRUE;
00851 
00852   if (window->frame)
00853     {
00854       wnck_window.x = window->frame->rect.x;
00855       wnck_window.y = window->frame->rect.y;
00856       wnck_window.width = window->frame->rect.width;
00857       wnck_window.height = window->frame->rect.height;
00858     }
00859   else
00860     {
00861       wnck_window.x = window->rect.x;
00862       wnck_window.y = window->rect.y;
00863       wnck_window.width = window->rect.width;
00864       wnck_window.height = window->rect.height;
00865     }
00866   return wnck_window;
00867 }
00868 
00869 
00870 static gboolean
00871 meta_select_workspace_expose_event (GtkWidget      *widget,
00872                                     GdkEventExpose *event)
00873 {
00874   MetaWorkspace *workspace;
00875   WnckWindowDisplayInfo *windows;
00876   int i, n_windows;
00877   GList *tmp, *list;
00878 
00879   workspace = META_SELECT_WORKSPACE (widget)->workspace;
00880               
00881   list = meta_stack_list_windows (workspace->screen->stack, workspace);
00882   n_windows = g_list_length (list);
00883   windows = g_new (WnckWindowDisplayInfo, n_windows);
00884 
00885   tmp = list;
00886   i = 0;
00887   while (tmp != NULL)
00888     {
00889       MetaWindow *window;
00890       gboolean ignoreable_sticky;
00891 
00892       window = tmp->data;
00893 
00894       ignoreable_sticky = window->on_all_workspaces &&
00895                           workspace != workspace->screen->active_workspace;
00896 
00897       if (window->skip_pager || 
00898           !meta_window_showing_on_its_workspace (window) ||
00899           window->unmaps_pending ||
00900           ignoreable_sticky)
00901         {
00902           --n_windows;
00903         }
00904       else
00905         {
00906           windows[i] = meta_convert_meta_to_wnck (window, workspace->screen);
00907           i++;
00908         }
00909       tmp = tmp->next;
00910     }
00911 
00912   g_list_free (list);
00913 
00914   wnck_draw_workspace (widget,
00915                        widget->window,
00916                        SELECT_OUTLINE_WIDTH,
00917                        SELECT_OUTLINE_WIDTH,
00918                        widget->allocation.width - SELECT_OUTLINE_WIDTH * 2,
00919                        widget->allocation.height - SELECT_OUTLINE_WIDTH * 2,
00920                        workspace->screen->rect.width,
00921                        workspace->screen->rect.height,
00922                        NULL,
00923                        (workspace->screen->active_workspace == workspace),
00924                        windows,
00925                        n_windows);
00926 
00927   g_free (windows);
00928   
00929   if (META_SELECT_WORKSPACE (widget)->selected)
00930     {
00931       i = SELECT_OUTLINE_WIDTH - 1;
00932       while (i >= 0)
00933         {
00934           gdk_draw_rectangle (widget->window,
00935                               widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
00936                               FALSE,
00937                               i, 
00938                               i,
00939                               widget->allocation.width - i * 2 - 1,
00940                               widget->allocation.height - i * 2 - 1);
00941 
00942           --i;
00943         }
00944     }
00945 
00946   return TRUE;
00947 }
00948 

Generated on Sat Aug 23 22:04:18 2008 for metacity by  doxygen 1.5.5