Updated SkOSMenu to use the updated SkEvents
http://codereview.appspot.com/4809075/


git-svn-id: http://skia.googlecode.com/svn/trunk@2055 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/views/SkOSMenu.h b/include/views/SkOSMenu.h
index 54c7dbe..763499e 100644
--- a/include/views/SkOSMenu.h
+++ b/include/views/SkOSMenu.h
@@ -18,7 +18,6 @@
     explicit SkOSMenu(const char title[] = "");
     ~SkOSMenu();
     
-    void reset();
     /**
      * Each of these (except action) has an associated value, which is stored in 
      * the event payload for the item.
@@ -32,12 +31,6 @@
      *     TriState : TriState
      *     Custom : custom object/value
      */
-    enum TriState {
-        kMixedState = -1,
-        kOffState = 0,
-        kOnState = 1
-    };
-    
     enum Type {
         kAction_Type,
         kList_Type,
@@ -48,59 +41,90 @@
         kCustom_Type
     };
     
+    enum TriState {
+        kMixedState = -1,
+        kOffState = 0,
+        kOnState = 1
+    };
+    
     class Item {
     public:
-        //Auto increments a global to generate an unique ID for each new item
-        //Thread safe
+        /**
+         * Auto increments a global to generate an unique ID for each new item
+         * Note: Thread safe
+         */
         Item(const char label[], SkOSMenu::Type type, const char slotName[], 
-             SkEvent* evt, SkEventSinkID target);
+             SkEvent* evt);
         ~Item() { delete fEvent; }
         
-        SkEvent* getEvent() const { return fEvent; }
-        int getID() { return fID; }
+        SkEvent*    getEvent() const { return fEvent; }
+        int         getID() const { return fID; }
         const char* getLabel() const { return fLabel.c_str(); }
         const char* getSlotName() const { return fSlotName.c_str(); }
-        Type getType() const { return fType; }
+        Type        getType() const { return fType; }
+        void        setKeyEquivalent(SkUnichar key) { fKey = key; }
+        SkUnichar   getKeyEquivalent() const { return fKey; }
         
-        //Post event associated with the menu item to target, any changes to the
-        //associated event must be made prior to calling this method.
-        void postEvent() const {
-            (new SkEvent(*(fEvent)))->setTargetID(fTarget)->post();
-        }
+        /**
+         * Post event associated with the menu item to target, any changes to 
+         * the associated event must be made prior to calling this method
+         */
+        void postEvent() const { (new SkEvent(*(fEvent)))->post(); }
         
-        //Helper functions for predefined types
-        void postEventWithBool(bool value) const; //For Switch
-        void postEventWithScalar(SkScalar value) const; //For Slider
-        void postEventWithInt(int value) const; //For List, TriState
+        /**
+         * Helper functions for predefined types
+         */
+        void postEventWithBool(bool value) const;           //For Switch
+        void postEventWithScalar(SkScalar value) const;     //For Slider
+        void postEventWithInt(int value) const;             //For List, TriState
         void postEventWithString(const char value[]) const; //For TextField
 
-        
     private:
         int             fID;
         SkEvent*        fEvent;
         SkString        fLabel;
         SkString        fSlotName;
-        SkEventSinkID   fTarget;
         Type            fType;
+        SkUnichar       fKey;
     };
     
-    //The following functions append new items to the menu and returns their 
-    //associated unique id, which can be used to by the client to refer to 
-    //the menu item created and change its state. slotName specifies the string
-    //identifier of any state/value to be returned in the item's SkEvent object
-    //NOTE: evt must be dynamically allocated
+    void reset();
+    const char* getTitle() const { return fTitle.c_str(); }
+    void        setTitle (const char title[]) { fTitle.set(title); }
+    int         countItems() const { return fItems.count(); }
+    const Item* getItem(int index) const { return fItems[index]; }
+    
+    /**
+     * Assign key to the menu item with itemID, will do nothing if there's no 
+     * item with the id given
+     */
+    void        assignKeyEquivalentToItem(int itemID, SkUnichar key);
+    /**
+     * Call this in a SkView's onHandleChar to trigger any menu items with the 
+     * given key equivalent. If such an item is found, the method will return 
+     * true and its corresponding event will be triggered (default behavior 
+     * defined for switches(toggling), tristates(cycle), and lists(cycle),
+     * for anything else, the event attached is posted without state changes) 
+     * If no menu item can be matched with the key, false will be returned
+     */
+    bool        handleKeyEquivalent(SkUnichar key);
+    
+    /**
+     * The following functions append new items to the menu and returns their 
+     * associated unique id, which can be used to by the client to refer to 
+     * the menu item created and change its state. slotName specifies the string
+     * identifier of any state/value to be returned in the item's SkEvent object
+     * NOTE: evt must be dynamically allocated
+     */
     int appendItem(const char label[], Type type, const char slotName[], 
-                   SkEvent* evt, SkEventSinkID target); 
+                   SkEvent* evt); 
     
-    //Predefined items and helper functions:
-    //Identifiers
-    static const char* EventType;
-    static const char* Delimiter;
-    static const char* List_Items_Str;
-    static const char* Slider_Min_Scalar;
-    static const char* Slider_Max_Scalar;
-    
-    //Create predefined items with the given parameters. To be used with the 
+    /**
+     * Create predefined items with the given parameters. To be used with the 
+     * other helper functions below to retrive/update state information.
+     * Note: the helper functions below assume that slotName is UNIQUE for all 
+     * menu items of the same type since it's used to identify the event
+     */
     int appendAction(const char label[], SkEventSinkID target);
     int appendList(const char label[], const char slotName[], 
                    SkEventSinkID target, int defaultIndex, const char[] ...);
@@ -110,26 +134,40 @@
     int appendSwitch(const char label[], const char slotName[], 
                      SkEventSinkID target, bool defaultState = false);
     int appendTriState(const char label[], const char slotName[],
-                       SkEventSinkID target, SkOSMenu::TriState defaultState = kOffState);
+                       SkEventSinkID target, TriState defaultState = kOffState);
     int appendTextField(const char label[], const char slotName[],
                         SkEventSinkID target, const char placeholder[] = "");
     
-    //Returns true if the event is of type SkOSMenu::EventType and retrieves 
-    //value stored in the evt that corresponds to the slotName. Otherwise, 
-    //returns false and leaves value unchanged
+    
+    /**
+     * Helper functions to retrieve information other than the stored value for
+     * some predefined types
+     */
+    static bool FindListItemCount(const SkEvent* evt, int* count);
+    /**
+     * Ensure that the items array can store n SkStrings where n is the count 
+     * extracted using FindListItemCount
+     */
+    static bool FindListItems(const SkEvent* evt, SkString items[]);
+    static bool FindSliderMin(const SkEvent* evt, SkScalar* min);
+    static bool FindSliderMax(const SkEvent* evt, SkScalar* max);
+    
+    /**
+     * Returns true if an action with the given label is found, false otherwise
+     */
     static bool FindAction(const SkEvent* evt, const char label[]);
-    static bool FindListIndex(const SkEvent* evt, const char slotName[], int* selected);
+    /**
+     * The following helper functions will return true if evt is generated from 
+     * a predefined item type and retrieve the corresponding state information. 
+     * They will return false and leave value unchanged if there's a type 
+     * mismatch or slotName is incorrect
+     */
+    static bool FindListIndex(const SkEvent* evt, const char slotName[], int* value);
     static bool FindSliderValue(const SkEvent* evt, const char slotName[], SkScalar* value);
     static bool FindSwitchState(const SkEvent* evt, const char slotName[], bool* value);
-    static bool FindTriState(const SkEvent* evt, const char slotName[], TriState* state);
+    static bool FindTriState(const SkEvent* evt, const char slotName[], TriState* value);
     static bool FindText(const SkEvent* evt, const char slotName[], SkString* value);
     
-    const char* getTitle() const { return fTitle.c_str(); }
-    void setTitle (const char title[]) { fTitle.set(title); }
-    // called by SkOSWindow when it receives an OS menu event
-    int         countItems() const;
-    const Item* getItem(int index) const;
-
 private:
     SkString fTitle;
     SkTDArray<Item*> fItems;
diff --git a/src/views/SkOSMenu.cpp b/src/views/SkOSMenu.cpp
index a8856cc..8137365 100644
--- a/src/views/SkOSMenu.cpp
+++ b/src/views/SkOSMenu.cpp
@@ -23,23 +23,63 @@
     fTitle.reset();
 }
 
-int SkOSMenu::countItems() const {
-	return fItems.count();
+void SkOSMenu::assignKeyEquivalentToItem(int itemID, SkUnichar key) {
+    for (int i = 0; i < fItems.count(); ++i) {
+        if (itemID == fItems[i]->getID())
+            fItems[i]->setKeyEquivalent(key);
+    }
 }
 
-const SkOSMenu::Item* SkOSMenu::getItem(int index) const{
-	return fItems[index];
+bool SkOSMenu::handleKeyEquivalent(SkUnichar key) {    
+    int value = 0, size = 0;
+    bool state;
+    SkOSMenu::TriState tristate;
+    for (int i = 0; i < fItems.count(); ++i) {
+        Item* item = fItems[i];
+        if (item->getKeyEquivalent()== key) {
+            SkString list;
+            switch (item->getType()) {
+                case kList_Type:
+                    SkOSMenu::FindListItemCount(item->getEvent(), &size);
+                    SkOSMenu::FindListIndex(item->getEvent(), item->getSlotName(), &value);
+                    value = (value + 1) % size;
+                    item->postEventWithInt(value);
+                    break;
+                case kSwitch_Type:
+                    SkOSMenu::FindSwitchState(item->getEvent(), item->getSlotName(), &state);
+                    item->postEventWithBool(!state);
+                    break;
+                case kTriState_Type:
+                    SkOSMenu::FindTriState(item->getEvent(), item->getSlotName(), &tristate);
+                    if (kOnState == tristate)
+                        tristate = kMixedState;
+                    else
+                        tristate = (SkOSMenu::TriState)((int)tristate + 1);
+                    item->postEventWithInt(tristate);
+                    break;
+                case kAction_Type:
+                case kCustom_Type:
+                case kSlider_Type:
+                case kTextField_Type:
+                default:
+                    item->postEvent();
+                    break;
+            }
+            return true;
+        }
+    }
+    return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 SkOSMenu::Item::Item(const char label[], SkOSMenu::Type type, 
-                     const char slotName[], SkEvent* evt, SkEventSinkID target) {
+                     const char slotName[], SkEvent* evt) {
     fLabel.set(label);
     fSlotName.set(slotName);
     fType = type;
-    fTarget = target;
     fEvent = evt;
+    fKey = 0;
     fID = sk_atomic_inc(&gOSMenuCmd);
 }
 
@@ -69,110 +109,130 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-const char* SkOSMenu::EventType = "SkOSMenuEventType";
-const char* SkOSMenu::Delimiter = "|";
-const char* SkOSMenu::Slider_Min_Scalar = "SkOSMenuSlider_Min";
-const char* SkOSMenu::Slider_Max_Scalar = "SkOSMenuSlider_Max";
-const char* SkOSMenu::List_Items_Str = "SkOSMenuList_Items";
+static const char* gMenuEventType = "SkOSMenuEventType";
+static const char* gSlider_Min_Scalar = "SkOSMenuSlider_Min";
+static const char* gSlider_Max_Scalar = "SkOSMenuSlider_Max";
+static const char* gDelimiter = "|";
+static const char* gList_Items_Str = "SkOSMenuList_Items";
+static const char* gList_ItemCount_S32 = "SkOSMenuList_ItemCount";
 
 int SkOSMenu::appendItem(const char label[], Type type, const char slotName[], 
-                         SkEvent* evt, SkEventSinkID target) {
-    SkOSMenu::Item* item = new Item(label, type, slotName, evt, target);
+                         SkEvent* evt) {
+    SkOSMenu::Item* item = new Item(label, type, slotName, evt);
     fItems.append(1, &item);
     return item->getID();
 }
 
 int SkOSMenu::appendAction(const char label[], SkEventSinkID target) {
-    SkEvent* evt = new SkEvent(SkOSMenu::EventType);
-    SkOSMenu::Item* item = new Item(label, SkOSMenu::kAction_Type, "", evt, target);
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
     //Store label in event so it can be used to identify the action later
-    evt->setString(label, "");
-    fItems.append(1, &item);
-    return item->getID();
+    evt->setString(label, label);
+    return appendItem(label, SkOSMenu::kAction_Type, "", evt);
 }
 
 int SkOSMenu::appendList(const char label[], const char slotName[], 
                          SkEventSinkID target, int index, const char option[], ...) {
-    SkEvent* evt = new SkEvent(SkOSMenu::EventType);
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
     va_list args;
     if (option) {
         SkString str(option);
         va_start(args, option);
+        int count = 1;
         for (const char* arg = va_arg(args, const char*); arg != NULL; arg = va_arg(args, const char*)) {
-            str += SkOSMenu::Delimiter;
+            str += gDelimiter;
             str += arg;
+            ++count;
         }
         va_end(args);
-        evt->setString(SkOSMenu::List_Items_Str, str);
+        evt->setString(gList_Items_Str, str);
+        evt->setS32(gList_ItemCount_S32, count);
         evt->setS32(slotName, index);
     }
-    SkOSMenu::Item* item = new Item(label, SkOSMenu::kList_Type, slotName, evt, target);
-    fItems.append(1, &item);
-    return item->getID();
+    return appendItem(label, SkOSMenu::kList_Type, slotName, evt);
 }
 
 int SkOSMenu::appendSlider(const char label[], const char slotName[], 
                            SkEventSinkID target, SkScalar min, SkScalar max, 
                            SkScalar defaultValue) {
-    SkEvent* evt = new SkEvent(SkOSMenu::EventType);
-    evt->setScalar(SkOSMenu::Slider_Min_Scalar, min);
-    evt->setScalar(SkOSMenu::Slider_Max_Scalar, max);
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
+    evt->setScalar(gSlider_Min_Scalar, min);
+    evt->setScalar(gSlider_Max_Scalar, max);
     evt->setScalar(slotName, defaultValue);
-    SkOSMenu::Item* item = new Item(label, SkOSMenu::kSlider_Type, slotName, evt, target);
-    fItems.append(1, &item);
-    return item->getID();
+    return appendItem(label, SkOSMenu::kSlider_Type, slotName, evt);
 }
 
 int SkOSMenu::appendSwitch(const char label[], const char slotName[], 
                            SkEventSinkID target, bool defaultState) {
-    SkEvent* evt = new SkEvent(SkOSMenu::EventType);
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
     evt->setBool(slotName, defaultState);
-    SkOSMenu::Item* item = new Item(label, SkOSMenu::kSwitch_Type, slotName, evt, target);
-    fItems.append(1, &item);
-    return item->getID();
+    return appendItem(label, SkOSMenu::kSwitch_Type, slotName, evt);
 }
 
 int SkOSMenu::appendTriState(const char label[], const char slotName[],
                              SkEventSinkID target, SkOSMenu::TriState defaultState) {
-    SkEvent* evt = new SkEvent(SkOSMenu::EventType);
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
     evt->setS32(slotName, defaultState);
-    SkOSMenu::Item* item = new Item(label, SkOSMenu::kTriState_Type, slotName, evt, target);
-    fItems.append(1, &item);
-    return item->getID();
+    return appendItem(label, SkOSMenu::kTriState_Type, slotName, evt);
 }
 
 int SkOSMenu::appendTextField(const char label[], const char slotName[], 
                               SkEventSinkID target, const char placeholder[]) {
-    SkEvent* evt = new SkEvent(SkOSMenu::EventType);
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
     evt->setString(slotName, placeholder);
-    SkOSMenu::Item* item = new Item(label, SkOSMenu::kTextField_Type, slotName, evt, target);
-    fItems.append(1, &item);
-    return item->getID();
+    return appendItem(label, SkOSMenu::kTextField_Type, slotName, evt);
 }
 
+bool SkOSMenu::FindListItemCount(const SkEvent* evt, int* count) {
+    return evt->isType(gMenuEventType) && evt->findS32(gList_ItemCount_S32, count);
+}
+
+bool SkOSMenu::FindListItems(const SkEvent* evt, SkString items[]) {
+    if (evt->isType(gMenuEventType) && NULL != items) {
+        const char* text = evt->findString(gList_Items_Str);
+        char temp[strlen(text)];
+        memcpy(temp, text, strlen(text) + 1); //make sure to copy the null terminator
+        char* token = strtok(temp, gDelimiter);
+        int index = 0;
+        while (token != NULL) {
+            items[index].set(token, strlen(token));
+            token = strtok (NULL, gDelimiter);
+            ++index;
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkOSMenu::FindSliderMin(const SkEvent* evt, SkScalar* min) {
+    return evt->isType(gMenuEventType) && evt->findScalar(gSlider_Min_Scalar, min);
+}
+
+bool SkOSMenu::FindSliderMax(const SkEvent* evt, SkScalar* max) {
+    return evt->isType(gMenuEventType) && evt->findScalar(gSlider_Max_Scalar, max);
+}
 
 bool SkOSMenu::FindAction(const SkEvent* evt, const char label[]) {
-    return evt->isType(SkOSMenu::EventType) && evt->findString(label);
+    return evt->isType(gMenuEventType) && evt->findString(label);
 }
 
-bool SkOSMenu::FindListIndex(const SkEvent* evt, const char slotName[], int* selected) {
-    return evt->isType(SkOSMenu::EventType) && evt->findS32(slotName, selected); 
+bool SkOSMenu::FindListIndex(const SkEvent* evt, const char slotName[], int* value) {
+    return evt->isType(gMenuEventType) && evt->findS32(slotName, value); 
 }
 
 bool SkOSMenu::FindSliderValue(const SkEvent* evt, const char slotName[], SkScalar* value) {
-    return evt->isType(SkOSMenu::EventType) && evt->findScalar(slotName, value);
+    return evt->isType(gMenuEventType) && evt->findScalar(slotName, value);
 }
 
 bool SkOSMenu::FindSwitchState(const SkEvent* evt, const char slotName[], bool* value) {
-    return evt->isType(SkOSMenu::EventType) && evt->findBool(slotName, value);
+    return evt->isType(gMenuEventType) && evt->findBool(slotName, value);
 }
 
-bool SkOSMenu::FindTriState(const SkEvent* evt, const char slotName[], SkOSMenu::TriState* state) {
-    return evt->isType(SkOSMenu::EventType) && evt->findS32(slotName, (int*)state);
+bool SkOSMenu::FindTriState(const SkEvent* evt, const char slotName[], SkOSMenu::TriState* value) {
+    return evt->isType(gMenuEventType) && evt->findS32(slotName, (int*)value);
 }
 
 bool SkOSMenu::FindText(const SkEvent* evt, const char slotName[], SkString* value) {
-    if (evt->isType(SkOSMenu::EventType)) {
+    if (evt->isType(gMenuEventType)) {
         const char* text = evt->findString(slotName);
         if (!text || !*text)
             return false;