1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "SkAtomics.h"
8 #include "SkOSMenu.h"
9 #include <stdarg.h>
10 
11 static int gOSMenuCmd = 7000;
12 
SkOSMenu(const char title[])13 SkOSMenu::SkOSMenu(const char title[]) {
14     fTitle.set(title);
15 }
16 
~SkOSMenu()17 SkOSMenu::~SkOSMenu() {
18     this->reset();
19 }
20 
reset()21 void SkOSMenu::reset() {
22     fItems.deleteAll();
23     fTitle.reset();
24 }
25 
getItemByID(int itemID) const26 const SkOSMenu::Item* SkOSMenu::getItemByID(int itemID) const {
27     for (int i = 0; i < fItems.count(); ++i) {
28         if (itemID == fItems[i]->getID())
29             return fItems[i];
30     }
31     return nullptr;
32 }
33 
getItems(const SkOSMenu::Item * items[]) const34 void SkOSMenu::getItems(const SkOSMenu::Item* items[]) const {
35     if (items) {
36         for (int i = 0; i < fItems.count(); ++i) {
37             items[i] = fItems[i];
38         }
39     }
40 }
41 
assignKeyEquivalentToItem(int itemID,SkUnichar key)42 void SkOSMenu::assignKeyEquivalentToItem(int itemID, SkUnichar key) {
43     for (int i = 0; i < fItems.count(); ++i) {
44         if (itemID == fItems[i]->getID())
45             fItems[i]->setKeyEquivalent(key);
46     }
47 }
48 
handleKeyEquivalent(SkUnichar key)49 bool SkOSMenu::handleKeyEquivalent(SkUnichar key) {
50     int value = 0, size = 0;
51     bool state;
52     SkOSMenu::TriState tristate;
53     for (int i = 0; i < fItems.count(); ++i) {
54         Item* item = fItems[i];
55         if (item->getKeyEquivalent()== key) {
56             SkString list;
57             switch (item->getType()) {
58                 case kList_Type:
59                     SkOSMenu::FindListItemCount(*item->getEvent(), &size);
60                     SkOSMenu::FindListIndex(*item->getEvent(), item->getSlotName(), &value);
61                     value = (value + 1) % size;
62                     item->setInt(value);
63                     break;
64                 case kSwitch_Type:
65                     SkOSMenu::FindSwitchState(*item->getEvent(), item->getSlotName(), &state);
66                     item->setBool(!state);
67                     break;
68                 case kTriState_Type:
69                     SkOSMenu::FindTriState(*item->getEvent(), item->getSlotName(), &tristate);
70                     if (kOnState == tristate)
71                         tristate = kMixedState;
72                     else
73                         tristate = (SkOSMenu::TriState)((int)tristate + 1);
74                     item->setTriState(tristate);
75                     break;
76                 case kAction_Type:
77                 case kCustom_Type:
78                 case kSlider_Type:
79                 case kTextField_Type:
80                 default:
81                     break;
82             }
83             item->postEvent();
84             return true;
85         }
86     }
87     return false;
88 }
89 
90 ////////////////////////////////////////////////////////////////////////////////
91 
Item(const char label[],SkOSMenu::Type type,const char slotName[],SkEvent * evt)92 SkOSMenu::Item::Item(const char label[], SkOSMenu::Type type,
93                      const char slotName[], SkEvent* evt) {
94     fLabel.set(label);
95     fSlotName.set(slotName);
96     fType = type;
97     fEvent = evt;
98     fKey = 0;
99     fID = sk_atomic_inc(&gOSMenuCmd);
100 }
101 
setBool(bool value) const102 void SkOSMenu::Item::setBool(bool value) const {
103     SkASSERT(SkOSMenu::kSwitch_Type == fType);
104     fEvent->setBool(fSlotName.c_str(), value);
105 }
106 
setScalar(SkScalar value) const107 void SkOSMenu::Item::setScalar(SkScalar value) const {
108     SkASSERT(SkOSMenu::kSlider_Type == fType);
109     fEvent->setScalar(fSlotName.c_str(), value);
110 }
111 
setInt(int value) const112 void SkOSMenu::Item::setInt(int value) const {
113     SkASSERT(SkOSMenu::kList_Type == fType);
114     fEvent->setS32(fSlotName.c_str(), value);
115 }
116 
setTriState(TriState value) const117 void SkOSMenu::Item::setTriState(TriState value) const {
118     SkASSERT(SkOSMenu::kTriState_Type == fType);
119     fEvent->setS32(fSlotName.c_str(), value);
120 }
121 
setString(const char value[]) const122 void SkOSMenu::Item::setString(const char value[]) const {
123     SkASSERT(SkOSMenu::kTextField_Type == fType);
124     fEvent->setString(fSlotName.c_str(), value);
125 }
126 
127 ////////////////////////////////////////////////////////////////////////////////
128 
129 static const char* gMenuEventType = "SkOSMenuEventType";
130 static const char* gSlider_Min_Scalar = "SkOSMenuSlider_Min";
131 static const char* gSlider_Max_Scalar = "SkOSMenuSlider_Max";
132 static const char* gDelimiter = "|";
133 static const char* gList_Items_Str = "SkOSMenuList_Items";
134 static const char* gList_ItemCount_S32 = "SkOSMenuList_ItemCount";
135 
appendItem(const char label[],Type type,const char slotName[],SkEvent * evt)136 int SkOSMenu::appendItem(const char label[], Type type, const char slotName[],
137                          SkEvent* evt) {
138     SkOSMenu::Item* item = new Item(label, type, slotName, evt);
139     fItems.append(1, &item);
140     return item->getID();
141 }
142 
appendAction(const char label[],SkEventSinkID target)143 int SkOSMenu::appendAction(const char label[], SkEventSinkID target) {
144     SkEvent* evt = new SkEvent(gMenuEventType, target);
145     //Store label in event so it can be used to identify the action later
146     evt->setString(label, label);
147     return appendItem(label, SkOSMenu::kAction_Type, "", evt);
148 }
149 
appendList(const char label[],const char slotName[],SkEventSinkID target,int index,const char option[],...)150 int SkOSMenu::appendList(const char label[], const char slotName[],
151                          SkEventSinkID target, int index, const char option[], ...) {
152     SkEvent* evt = new SkEvent(gMenuEventType, target);
153     va_list args;
154     if (option) {
155         SkString str(option);
156         va_start(args, option);
157         int count = 1;
158         for (const char* arg = va_arg(args, const char*); arg != nullptr; arg = va_arg(args, const char*)) {
159             str += gDelimiter;
160             str += arg;
161             ++count;
162         }
163         va_end(args);
164         evt->setString(gList_Items_Str, str);
165         evt->setS32(gList_ItemCount_S32, count);
166         evt->setS32(slotName, index);
167     }
168     return appendItem(label, SkOSMenu::kList_Type, slotName, evt);
169 }
170 
appendSlider(const char label[],const char slotName[],SkEventSinkID target,SkScalar min,SkScalar max,SkScalar defaultValue)171 int SkOSMenu::appendSlider(const char label[], const char slotName[],
172                            SkEventSinkID target, SkScalar min, SkScalar max,
173                            SkScalar defaultValue) {
174     SkEvent* evt = new SkEvent(gMenuEventType, target);
175     evt->setScalar(gSlider_Min_Scalar, min);
176     evt->setScalar(gSlider_Max_Scalar, max);
177     evt->setScalar(slotName, defaultValue);
178     return appendItem(label, SkOSMenu::kSlider_Type, slotName, evt);
179 }
180 
appendSwitch(const char label[],const char slotName[],SkEventSinkID target,bool defaultState)181 int SkOSMenu::appendSwitch(const char label[], const char slotName[],
182                            SkEventSinkID target, bool defaultState) {
183     SkEvent* evt = new SkEvent(gMenuEventType, target);
184     evt->setBool(slotName, defaultState);
185     return appendItem(label, SkOSMenu::kSwitch_Type, slotName, evt);
186 }
187 
appendTriState(const char label[],const char slotName[],SkEventSinkID target,SkOSMenu::TriState defaultState)188 int SkOSMenu::appendTriState(const char label[], const char slotName[],
189                              SkEventSinkID target, SkOSMenu::TriState defaultState) {
190     SkEvent* evt = new SkEvent(gMenuEventType, target);
191     evt->setS32(slotName, defaultState);
192     return appendItem(label, SkOSMenu::kTriState_Type, slotName, evt);
193 }
194 
appendTextField(const char label[],const char slotName[],SkEventSinkID target,const char placeholder[])195 int SkOSMenu::appendTextField(const char label[], const char slotName[],
196                               SkEventSinkID target, const char placeholder[]) {
197     SkEvent* evt = new SkEvent(gMenuEventType, target);
198     evt->setString(slotName, placeholder);
199     return appendItem(label, SkOSMenu::kTextField_Type, slotName, evt);
200 }
201 
FindListItemCount(const SkEvent & evt,int * count)202 bool SkOSMenu::FindListItemCount(const SkEvent& evt, int* count) {
203     return evt.isType(gMenuEventType) && evt.findS32(gList_ItemCount_S32, count);
204 }
205 
FindListItems(const SkEvent & evt,SkString items[])206 bool SkOSMenu::FindListItems(const SkEvent& evt, SkString items[]) {
207     if (evt.isType(gMenuEventType) && items) {
208         const char* text = evt.findString(gList_Items_Str);
209         if (text != nullptr) {
210             SkString temp(text);
211             char* token = strtok((char*)temp.c_str(), gDelimiter);
212             int index = 0;
213             while (token != nullptr) {
214                 items[index].set(token, strlen(token));
215                 token = strtok (nullptr, gDelimiter);
216                 ++index;
217             }
218         }
219         return true;
220     }
221     return false;
222 }
223 
FindSliderMin(const SkEvent & evt,SkScalar * min)224 bool SkOSMenu::FindSliderMin(const SkEvent& evt, SkScalar* min) {
225     return evt.isType(gMenuEventType) && evt.findScalar(gSlider_Min_Scalar, min);
226 }
227 
FindSliderMax(const SkEvent & evt,SkScalar * max)228 bool SkOSMenu::FindSliderMax(const SkEvent& evt, SkScalar* max) {
229     return evt.isType(gMenuEventType) && evt.findScalar(gSlider_Max_Scalar, max);
230 }
231 
FindAction(const SkEvent & evt,const char label[])232 bool SkOSMenu::FindAction(const SkEvent& evt, const char label[]) {
233     return evt.isType(gMenuEventType) && evt.findString(label);
234 }
235 
FindListIndex(const SkEvent & evt,const char slotName[],int * value)236 bool SkOSMenu::FindListIndex(const SkEvent& evt, const char slotName[], int* value) {
237     return evt.isType(gMenuEventType) && evt.findS32(slotName, value);
238 }
239 
FindSliderValue(const SkEvent & evt,const char slotName[],SkScalar * value)240 bool SkOSMenu::FindSliderValue(const SkEvent& evt, const char slotName[], SkScalar* value) {
241     return evt.isType(gMenuEventType) && evt.findScalar(slotName, value);
242 }
243 
FindSwitchState(const SkEvent & evt,const char slotName[],bool * value)244 bool SkOSMenu::FindSwitchState(const SkEvent& evt, const char slotName[], bool* value) {
245     return evt.isType(gMenuEventType) && evt.findBool(slotName, value);
246 }
247 
FindTriState(const SkEvent & evt,const char slotName[],SkOSMenu::TriState * value)248 bool SkOSMenu::FindTriState(const SkEvent& evt, const char slotName[], SkOSMenu::TriState* value) {
249     return evt.isType(gMenuEventType) && evt.findS32(slotName, (int*)value);
250 }
251 
FindText(const SkEvent & evt,const char slotName[],SkString * value)252 bool SkOSMenu::FindText(const SkEvent& evt, const char slotName[], SkString* value) {
253     if (evt.isType(gMenuEventType)) {
254         const char* text = evt.findString(slotName);
255         if (!text || !*text)
256             return false;
257         else {
258             value->set(text);
259             return true;
260         }
261     }
262     return false;
263 }
264