[gnome-shell] Bug 577338 – Show item details on click in the expanded view



commit 5fbc0d4a56cef4bac5777027398c107c0abfa3cf
Author: Marina Zhurakhinskaya <marinaz redhat com>
Date:   Tue Mar 31 14:12:33 2009 -0400

    Bug 577338 â?? Show item details on click in the expanded view
    
    Change the overlay behavior to display more details about an item on single
    click and launch it on double click.
    
    When the item is clicked on in the expanded view, the details are shown in the
    area on the right that is allocated for showing details. The details pop-up is
    not shown for the item that was clicked on, but it is shown for other items on
    hover and for the item that was clicked if the mouse pointer is moved back to
    it.
    
    Both hovering and single clicking results in the details pop-up being shown in
    the regular view. (Single clicking actually doesn't do anything in the regular
    view, but the details pop-up is shown due to hovering within the time it takes
    to perform a single click.)
    
    The overlay now uses 3 columns on the wide screen for displaying items in the
    expanded view. This allows keeping the size of the details area the same for
    expanded and regular views.
    
    Add shell_get_button_event_click_count() to shell-global.[hc] to retrieve
    the click count for button press and release events.
    
    Add selectedItemDetails public variable actor to the generic display to
    contain the details of the selected item and be shown in the overlay when
    it is in the expanded view mode.
    
    Fix the bug when the sideshow section would loose selection in the expanded
    view if it did not have any items, and would not regain it if it was repopulated
    with some items (e.g. when the search string changes).
    
    The sideshow no longer takes overlay parent and width as constructor arguments.
    It is added to the overlay inside the overlay code and manages its own width
    instead (which is ok, since it is pretty much a private class within overlay).
    
    Clean up the way selection is moved when an item is launched in order to have
    selection on click and activation on double click be implemented in a similar
    fashion. An unneeded _activatedItem variable in generic display was removed,
    and the selected item is activated instead when necessary. The flow of processing
    signals changed so that generic display no longer waits for the selection from
    a different sideshow section to be removed before selecting an item that was
    clicked on. This removed the need for doActivate() function.
---
 js/ui/genericDisplay.js |  148 ++++++++++++++++++++++++++++++++++-------------
 js/ui/overlay.js        |  109 ++++++++++++++++++++++++-----------
 src/shell-global.c      |   19 ++++++
 src/shell-global.h      |    2 +
 4 files changed, 203 insertions(+), 75 deletions(-)

diff --git a/js/ui/genericDisplay.js b/js/ui/genericDisplay.js
index d96a934..e5c75ec 100644
--- a/js/ui/genericDisplay.js
+++ b/js/ui/genericDisplay.js
@@ -63,10 +63,14 @@ GenericDisplayItem.prototype = {
                                          width: availableWidth,
                                          height: ITEM_DISPLAY_HEIGHT });
         this.actor._delegate = this;
-        this.actor.connect('button-release-event',
+        this.actor.connect('button-press-event',
                            Lang.bind(this,
-                                     function(draggable, e) {
-                                         this.activate();
+                                     function(actor, e) {
+                                         let clickCount = Shell.get_button_event_click_count(e);
+                                         if (clickCount == 1)
+                                             this.select();
+                                         else if (clickCount == 2)
+                                             this.activate();
                                      }));
 
         let draggable = DND.makeDraggable(this.actor);
@@ -176,12 +180,69 @@ GenericDisplayItem.prototype = {
        this._bg.background_color = color;
     },
 
-    // Activates the item, as though it was clicked
+    // Activates the item, as though it was launched
     activate: function() {
         this.hidePreview();
         this.emit('activate');
     },
 
+    // Selects the item, as though it was clicked
+    select: function() {
+        this.emit('select');
+    },
+
+    /*
+     * Returns an actor containing item details. In the future details can have more information than what 
+     * the preview pop-up has and be item-type specific.
+     *
+     * availableWidth - width available for displaying details
+     */ 
+    createDetailsActor: function(availableWidth) {
+        let details = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
+                                    spacing: PREVIEW_BOX_SPACING,
+                                    width: availableWidth });
+
+        this._ensurePreviewIconCreated();
+
+        if (this._previewIcon != null) {
+            let previewIconClone = new Clutter.Clone({ source: this._previewIcon });
+            details.append(previewIconClone, Big.BoxPackFlags.NONE);
+        }
+
+	// Inner box with name and description
+        let textDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
+                                        spacing: PREVIEW_BOX_SPACING });
+        let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
+                                             font_name: "Sans bold 14px",
+                                             line_wrap: true,
+                                             text: this._name.text});
+        textDetails.append(detailsName, Big.BoxPackFlags.NONE);
+
+        let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
+                                                    font_name: "Sans 14px",
+                                                    line_wrap: true,
+                                                    text: this._description.text });
+        textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
+
+        details.append(textDetails, Big.BoxPackFlags.EXPAND);
+   
+        // We hide the preview pop-up if the details are shown elsewhere. 
+        details.connect("show", 
+                        Lang.bind(this, 
+                                  function() {
+                                          // Right now "show" signal is emitted when an actor is added to a parent that
+                                          // has not been added to anything and "visible" property is also set to true 
+                                          // at this point, so checking if the parent that the actor has been added to  
+                                          // has a parent of its own is a temporary workaround. That other actor is 
+                                          // presumed to be displayed, which is a limitation of this workaround, but is
+                                          // the case with our usage of the details actor now.         
+                                          // http://bugzilla.openedhand.com/show_bug.cgi?id=1138
+                                          if (details.get_parent() != null && details.get_parent().get_parent() != null)
+                                              this.hidePreview();
+                                  }));
+        return details;
+    },
+
     // Destoys the item, as well as a preview for the item if it exists.
     destroy: function() {
       this.actor.destroy();
@@ -274,33 +335,33 @@ GenericDisplayItem.prototype = {
                                       padding: PREVIEW_BOX_PADDING,
                                       spacing: PREVIEW_BOX_SPACING });
 
-        let previewDetailsWidth = this._availableWidth - PREVIEW_BOX_PADDING * 2;
+        let textDetailsWidth = this._availableWidth - PREVIEW_BOX_PADDING * 2;
 
         this._ensurePreviewIconCreated();
 
         if (this._previewIcon != null) {
             this._preview.append(this._previewIcon, Big.BoxPackFlags.EXPAND);
-            previewDetailsWidth = this._availableWidth - this._previewIcon.width - PREVIEW_BOX_PADDING * 2 - PREVIEW_BOX_SPACING;
+            textDetailsWidth = this._availableWidth - this._previewIcon.width - PREVIEW_BOX_PADDING * 2 - PREVIEW_BOX_SPACING;
         }
 
 	// Inner box with name and description
-        let previewDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
-                                           spacing: PREVIEW_BOX_SPACING });
-        let previewName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
+        let textDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
+                                        spacing: PREVIEW_BOX_SPACING });
+        let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
                                              font_name: "Sans bold 14px",
                                              text: this._name.text});
 
-        previewDetails.width = Math.max(PREVIEW_DETAILS_MIN_WIDTH, previewDetailsWidth, previewName.width);
+        textDetails.width = Math.max(PREVIEW_DETAILS_MIN_WIDTH, textDetailsWidth, detailsName.width);
 
-        previewDetails.append(previewName, Big.BoxPackFlags.NONE);
+        textDetails.append(detailsName, Big.BoxPackFlags.NONE);
 
-        let previewDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
+        let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
                                                     font_name: "Sans 14px",
                                                     line_wrap: true,
                                                     text: this._description.text });
-        previewDetails.append(previewDescription, Big.BoxPackFlags.NONE);
+        textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
 
-        this._preview.append(previewDetails, Big.BoxPackFlags.EXPAND);
+        this._preview.append(textDetails, Big.BoxPackFlags.EXPAND);
 
         // Add the preview to global stage to allow for top-level layering
         let global = Shell.Global.get();
@@ -377,8 +438,6 @@ GenericDisplay.prototype = {
         this._displayedItems = {};  
         this._displayedItemsCount = 0;
         this._pageDisplayed = 0;
-        // GenericDisplayItem
-        this._activatedItem = null;
         this._selectedIndex = -1;
         this._keepDisplayCurrent = false;
         this.actor = this._grid;
@@ -387,27 +446,32 @@ GenericDisplay.prototype = {
                                             height: 24,
                                             spacing: 12,
                                             orientation: Big.BoxOrientation.HORIZONTAL});
+
+        this._availableWidthForItemDetails = this._columnWidth;
+        this.selectedItemDetails = new Big.Box({});     
     },
 
     //// Public methods ////
 
+    setAvailableWidthForItemDetails: function(availableWidth) {
+        this._availableWidthForItemDetails = availableWidth;
+    },
+
+    getAvailableWidthForItemDetails: function() {
+        return this._availableWidthForItemDetails;
+    },
+
     // Sets the search string and displays the matching items.
     setSearch: function(text) {
         this._search = text.toLowerCase();
         this._redisplay(true);
     },
 
-    // Sets this._activatedItem to the item that is selected and emits 'activated' signal.
-    // The reason we don't call launch() on the activated item right away is because we want
-    // the class that contains the display to do all other necessary actions and then call
-    // doActivate(). Currently, when a selected item is activated we only clear the search 
-    // entry, but when an item that was not selected is clicked, we want to move the selection
-    // to the clicked item first. This needs to happen in the class that contains the display
-    // because the selection might be moved from some other display that class contains.
+    // Launches the item that is currently selected and emits 'activated' signal.
     activateSelected: function() {
         if (this._selectedIndex != -1) {
             let selected = this._findDisplayedByIndex(this._selectedIndex);
-            this._activatedItem = selected;
+            selected.launch()
             this.emit('activated');
         }
     },
@@ -475,16 +539,6 @@ GenericDisplay.prototype = {
         return this._displayedItemsCount > 0;
     },
 
-    // Highlights the activated item and launches it.
-    doActivate: function() {
-        if (this._activatedItem != null) {
-            // We update the selection, so that in case an item was selected by clicking on it and 
-            // it is different from an item that was previously selected, we can highlight the new selection.
-            this._selectIndex(this._getIndexOfDisplayedActor(this._activatedItem.actor));
-            this._activatedItem.launch();    
-        } 
-    },
-
     // Readjusts display layout and the items displayed based on the new dimensions.
     updateDimensions: function(width, height, numberOfColumns) {
         this._numberOfColumns = numberOfColumns;
@@ -567,16 +621,24 @@ GenericDisplay.prototype = {
             return;
         }
 
-        let me = this;
-
         let itemInfo = this._allItems[itemId];
         let displayItem = this._createDisplayItem(itemInfo);
         displayItem.setShowPreview(true);
 
-        displayItem.connect('activate', function() {
-            me._activatedItem = displayItem;
-            me.emit('activated');
-        });
+        displayItem.connect('activate', 
+                            Lang.bind(this,
+                                      function() {
+                                          // update the selection
+                                          this._selectIndex(this._getIndexOfDisplayedActor(displayItem.actor));
+                                          this.activateSelected();
+                                      }));
+
+        displayItem.connect('select', 
+                            Lang.bind(this,
+                                      function() {
+                                          // update the selection
+                                          this._selectIndex(this._getIndexOfDisplayedActor(displayItem.actor));
+                                      }));
         this._grid.add_actor(displayItem.actor);
         this._displayedItems[itemId] = displayItem;
         this._displayedItemsCount++;
@@ -819,16 +881,20 @@ GenericDisplay.prototype = {
         return -1;
     },
 
-    // Selects (e.g. highlights) a display item at the provided index.
+    // Selects (e.g. highlights) a display item at the provided index,
+    // updates this.selectedItemDetails actor, and emits 'selected' signal.
     _selectIndex: function(index) {
         if (this._selectedIndex != -1) {
             let prev = this._findDisplayedByIndex(this._selectedIndex);
             prev.markSelected(false);
+            this.selectedItemDetails.remove_all();
         }
         this._selectedIndex = index;
         if (index != -1 && index < this._displayedItemsCount) {
             let item = this._findDisplayedByIndex(index);
             item.markSelected(true);
+            this.selectedItemDetails.append(item.createDetailsActor(this._availableWidthForItemDetails), Big.BoxPackFlags.NONE);  
+            this.emit('selected'); 
         }
     }
 };
diff --git a/js/ui/overlay.js b/js/ui/overlay.js
index 97a1ec9..819249c 100644
--- a/js/ui/overlay.js
+++ b/js/ui/overlay.js
@@ -28,13 +28,17 @@ const SIDESHOW_MIN_WIDTH = 250;
 const SIDESHOW_SECTION_PADDING_TOP = 6;
 const SIDESHOW_SECTION_SPACING = 6;
 const SIDESHOW_COLUMNS = 1;
-const EXPANDED_SIDESHOW_COLUMNS = 2;
+const DETAILS_CORNER_RADIUS = 5;
+const DETAILS_BORDER_WIDTH = 1;
+const DETAILS_PADDING = 6;
 // This is the height of section components other than the item display.
 const SIDESHOW_SECTION_MISC_HEIGHT = (LABEL_HEIGHT + SIDESHOW_SECTION_SPACING) * 2 + SIDESHOW_SECTION_PADDING_TOP;
 const SIDESHOW_SEARCH_BG_COLOR = new Clutter.Color();
 SIDESHOW_SEARCH_BG_COLOR.from_pixel(0xffffffff);
 const SIDESHOW_TEXT_COLOR = new Clutter.Color();
 SIDESHOW_TEXT_COLOR.from_pixel(0xffffffff);
+const DETAILS_BORDER_COLOR = new Clutter.Color();
+DETAILS_BORDER_COLOR.from_pixel(0xffffffff);
 
 // Time for initial animation going into overlay mode
 const ANIMATION_TIME = 0.5;
@@ -65,35 +69,43 @@ const WORKSPACE_GRID_PADDING = 12;
 const COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN = 3;
 const ROWS_FOR_WORKSPACES_REGULAR_SCREEN = 6;
 const WORKSPACES_X_FACTOR_ASIDE_MODE_REGULAR_SCREEN = 4 - 0.25;
+const EXPANDED_SIDESHOW_COLUMNS_REGULAR_SCREEN = 2;
 
 const COLUMNS_FOR_WORKSPACES_WIDE_SCREEN = 4;
 const ROWS_FOR_WORKSPACES_WIDE_SCREEN = 8;
 const WORKSPACES_X_FACTOR_ASIDE_MODE_WIDE_SCREEN = 5 - 0.25;
+const EXPANDED_SIDESHOW_COLUMNS_WIDE_SCREEN = 3;
 
 let wideScreen = false;
 let displayGridColumnWidth = null;
 let displayGridRowHeight = null;
 
-function Sideshow(parent, width) {
-    this._init(parent, width);
+function Sideshow() {
+    this._init();
 }
 
 Sideshow.prototype = {
-    _init : function(parent, width) {
+    _init : function() {
         let me = this;
 
         this._moreAppsMode = false;
         this._moreDocsMode = false;
 
-        this._width = width - SIDESHOW_PAD;
+        let asideXFactor = wideScreen ? WORKSPACES_X_FACTOR_ASIDE_MODE_WIDE_SCREEN : WORKSPACES_X_FACTOR_ASIDE_MODE_REGULAR_SCREEN; 
+        this._expandedSideshowColumns = wideScreen ? EXPANDED_SIDESHOW_COLUMNS_WIDE_SCREEN : EXPANDED_SIDESHOW_COLUMNS_REGULAR_SCREEN;       
+
+        this._width = displayGridColumnWidth - SIDESHOW_PAD;
 
-        // this figures out the additional width we can give to the display in the 'More' mode
+        // this figures out the additional width we can give to the display in the 'More' mode,
+        // assuming that we want to keep the columns the same width in both modes
         this._additionalWidth = ((this._width + SIDESHOW_PAD) / SIDESHOW_COLUMNS) * 
-                                (EXPANDED_SIDESHOW_COLUMNS - SIDESHOW_COLUMNS);
+                                (this._expandedSideshowColumns - SIDESHOW_COLUMNS);
+
+        let previewWidth = displayGridColumnWidth * asideXFactor - this._width - 
+                           this._additionalWidth - SIDESHOW_SECTION_SPACING * 2; 
 
         let global = Shell.Global.get();
         this.actor = new Clutter.Group();
-        parent.add_actor(this.actor);
         let icontheme = Gtk.IconTheme.get_default();
         this._searchBox = new Big.Box({ background_color: SIDESHOW_SEARCH_BG_COLOR,
                                         corner_radius: 4,
@@ -255,37 +267,43 @@ Sideshow.prototype = {
         this._docsDisplayControlBox = new Big.Box({x_align: Big.BoxAlignment.CENTER});
         this._docsDisplayControlBox.append(this._docDisplay.displayControl, Big.BoxPackFlags.NONE);
 
+        this._details = new Big.Box({ x: SIDESHOW_PAD + this._width + this._additionalWidth + SIDESHOW_SECTION_SPACING,
+                                      y: Panel.PANEL_HEIGHT + SIDESHOW_PAD,
+                                      width: previewWidth,
+                                      height: global.screen_height - Panel.PANEL_HEIGHT - SIDESHOW_PAD - bottomHeight,
+                                      corner_radius: DETAILS_CORNER_RADIUS,
+                                      border: DETAILS_BORDER_WIDTH,
+                                      border_color: DETAILS_BORDER_COLOR,
+                                      padding: DETAILS_PADDING});
+        this._appDisplay.setAvailableWidthForItemDetails(previewWidth);
+        this._docDisplay.setAvailableWidthForItemDetails(previewWidth);
+
         /* Proxy the activated signals */
         this._appDisplay.connect('activated', function(appDisplay) {
-            // we allow clicking on an item to launch it, and this unsets the selection
-            // so that we can move it to the item that was clicked on
-            me._appDisplay.unsetSelected(); 
-            me._docDisplay.unsetSelected();
-            me._appDisplay.hidePreview(); 
-            me._docDisplay.hidePreview();
-            me._appDisplay.doActivate();
             me.emit('activated');
         });
         this._docDisplay.connect('activated', function(docDisplay) {
-            // we allow clicking on an item to launch it, and this unsets the selection
-            // so that we can move it to the item that was clicked on
-            me._appDisplay.unsetSelected(); 
+            me.emit('activated');
+        });
+        this._appDisplay.connect('selected', function(appDisplay) {
+            // We allow clicking on any item to select it, so if an 
+            // item in the app display is selected, we need to make sure that
+            // no item in the doc display has the selection.
             me._docDisplay.unsetSelected();
-            me._appDisplay.hidePreview(); 
             me._docDisplay.hidePreview();
-            me._docDisplay.doActivate();
-            me.emit('activated');
+        });
+        this._docDisplay.connect('selected', function(docDisplay) {
+            // We allow clicking on any item to select it, so if an 
+            // item in the doc display is selected, we need to make sure that
+            // no item in the app display has the selection.
+            me._appDisplay.unsetSelected(); 
+            me._appDisplay.hidePreview(); 
         });
         this._appDisplay.connect('redisplayed', function(appDisplay) {
-            // This can be applicable if app display previously had the selection,
-            // but it got updated and now has no items, so we can try to move
-            // the selection to the doc display. 
-            if (!me._appDisplay.hasSelected() && !me._docDisplay.hasSelected())
-                me._docDisplay.selectFirstItem();    
+            me._ensureItemSelected();
         });
         this._docDisplay.connect('redisplayed', function(docDisplay) {
-            if (!me._docDisplay.hasSelected() && !me._appDisplay.hasSelected())
-                me._appDisplay.selectFirstItem();
+            me._ensureItemSelected();
         });
 
         this._moreAppsLink.connect('clicked',
@@ -328,6 +346,22 @@ Sideshow.prototype = {
         this._unsetMoreDocsMode();
     },
 
+    // Ensures that one of the displays has the selection if neither owns it after the
+    // latest redisplay. This can be applicable if the display that earlier had the
+    // selection no longer has any items, or if their is a single section being shown 
+    // in the expanded view and it went from having no matching items to having some.
+    // We first try to place the selection in the applications section, because it is
+    // displayed above the documents section.
+    _ensureItemSelected: function() { 
+        if (!this._appDisplay.hasSelected() && !this._docDisplay.hasSelected()) {
+            if (this._appDisplay.hasItems()) { 
+                this._appDisplay.selectFirstItem();
+            } else if (this._docDisplay.hasItems()) {
+                this._docDisplay.selectFirstItem();
+            }
+        }
+    },
+ 
     // Sets the 'More' mode for browsing applications. Updates the applications section to have more items.
     // Slides down the documents section to reveal the additional applications.
     _setMoreAppsMode: function() {
@@ -428,13 +462,17 @@ Sideshow.prototype = {
         if (this._moreAppsMode) {
             this._appDisplay.updateDimensions(this._width + this._additionalWidth, 
                                               this._itemDisplayHeight + SIDESHOW_SECTION_MISC_HEIGHT,
-                                              EXPANDED_SIDESHOW_COLUMNS);
+                                              this._expandedSideshowColumns);
             this._moreAppsLink.setText("Less...");
             this._appsSection.insert_after(this._appsDisplayControlBox, this._appDisplay.actor, Big.BoxPackFlags.NONE); 
+            this.actor.add_actor(this._details);
+            this._details.append(this._appDisplay.selectedItemDetails, Big.BoxPackFlags.NONE);
         } else {
             this._appDisplay.updateDimensions(this._width, this._appsSectionDefaultHeight - SIDESHOW_SECTION_MISC_HEIGHT, SIDESHOW_COLUMNS);
             this._moreAppsLink.setText("More...");
             this._appsSection.remove_actor(this._appsDisplayControlBox);
+            this.actor.remove_actor(this._details);
+            this._details.remove_all();
         }
         this._moreAppsLink.actor.show();
     },
@@ -540,13 +578,17 @@ Sideshow.prototype = {
         if (this._moreDocsMode) {
             this._docDisplay.updateDimensions(this._width + this._additionalWidth, 
                                               this._itemDisplayHeight + SIDESHOW_SECTION_MISC_HEIGHT,
-                                              EXPANDED_SIDESHOW_COLUMNS);
+                                              this._expandedSideshowColumns);
             this._moreDocsLink.setText("Less...");
             this._docsSection.insert_after(this._docsDisplayControlBox, this._docDisplay.actor, Big.BoxPackFlags.NONE); 
+            this.actor.add_actor(this._details);
+            this._details.append(this._docDisplay.selectedItemDetails, Big.BoxPackFlags.NONE);
         } else {
             this._docDisplay.updateDimensions(this._width, this._docsSectionDefaultHeight - SIDESHOW_SECTION_MISC_HEIGHT, SIDESHOW_COLUMNS);
             this._moreDocsLink.setText("More...");
             this._docsSection.remove_actor(this._docsDisplayControlBox); 
+            this.actor.remove_actor(this._details); 
+            this._details.remove_all();
         }
         this._moreDocsLink.actor.show();
     }
@@ -594,9 +636,8 @@ Overlay.prototype = {
         global.overlay_group.add_actor(this._group);
 
         // TODO - recalculate everything when desktop size changes
-        let sideshowWidth = displayGridColumnWidth;  
-         
-        this._sideshow = new Sideshow(this._group, sideshowWidth);
+        this._sideshow = new Sideshow();
+        this._group.add_actor(this._sideshow.actor); 
         this._workspaces = null;
         this._workspacesBackground = null;
         this._sideshow.connect('activated', function(sideshow) {
@@ -698,7 +739,7 @@ Overlay.prototype = {
                                                              x: displayGridColumnWidth,
                                                              y: Panel.PANEL_HEIGHT,
                                                              width: displayGridColumnWidth * columnsUsed,
-                                                             height: global.screen_width - Panel.PANEL_HEIGHT });
+                                                             height: global.screen_height - Panel.PANEL_HEIGHT });
         this._group.add_actor(this._workspacesBackground);
 
         this._workspaces = new Workspaces.Workspaces(workspacesWidth, workspacesHeight, workspacesX, workspacesY, 
diff --git a/src/shell-global.c b/src/shell-global.c
index 438e498..6bbe40c 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -421,6 +421,12 @@ shell_get_categories_for_desktop_file(const char *desktop_file_name)
     return categories_list;
 }
 
+/**
+ * shell_get_event_key_symbol:
+ *
+ * Return value: Clutter key value for the key press and release events, 
+ *               as specified in clutter-keysyms.h  
+ */
 guint16
 shell_get_event_key_symbol(ClutterEvent *event)
 {
@@ -431,6 +437,19 @@ shell_get_event_key_symbol(ClutterEvent *event)
 }
 
 /**
+ * shell_get_button_event_click_count:
+ *
+ * Return value: click count for button press and release events
+ */
+guint16
+shell_get_button_event_click_count(ClutterEvent *event)
+{
+  g_return_val_if_fail(event->type == CLUTTER_BUTTON_PRESS ||
+                       event->type == CLUTTER_BUTTON_RELEASE, 0);
+  return event->button.click_count;
+}
+
+/**
  * shell_global_get:
  *
  * Gets the singleton global object that represents the desktop.
diff --git a/src/shell-global.h b/src/shell-global.h
index 580eba2..70cc962 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -40,6 +40,8 @@ GSList *shell_get_categories_for_desktop_file(const char *desktop_file_name);
 
 guint16 shell_get_event_key_symbol(ClutterEvent *event);
 
+guint16 shell_get_button_event_click_count(ClutterEvent *event);
+
 ShellGlobal *shell_global_get (void);
 
 void shell_global_grab_dbus_service (ShellGlobal *global);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]