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 <stdarg.h>
8#include "SkOSMenu.h"
9#include "SkThread.h"
10
11static int gOSMenuCmd = 7000;
12
13SkOSMenu::SkOSMenu(const char title[]) {
14    fTitle.set(title);
15}
16
17SkOSMenu::~SkOSMenu() {
18    this->reset();
19}
20
21void SkOSMenu::reset() {
22    fItems.deleteAll();
23    fTitle.reset();
24}
25
26const 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 NULL;
32}
33
34void 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
42void 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
49bool 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
92SkOSMenu::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
102void SkOSMenu::Item::setBool(bool value) const {
103    SkASSERT(SkOSMenu::kSwitch_Type == fType);
104    fEvent->setBool(fSlotName.c_str(), value);
105}
106
107void SkOSMenu::Item::setScalar(SkScalar value) const {
108    SkASSERT(SkOSMenu::kSlider_Type == fType);
109    fEvent->setScalar(fSlotName.c_str(), value);
110}
111
112void SkOSMenu::Item::setInt(int value) const {
113    SkASSERT(SkOSMenu::kList_Type == fType);
114    fEvent->setS32(fSlotName.c_str(), value);
115}
116
117void SkOSMenu::Item::setTriState(TriState value) const {
118    SkASSERT(SkOSMenu::kTriState_Type == fType);
119    fEvent->setS32(fSlotName.c_str(), value);
120}
121
122void SkOSMenu::Item::setString(const char value[]) const {
123    SkASSERT(SkOSMenu::kTextField_Type == fType);
124    fEvent->setString(fSlotName.c_str(), value);
125}
126
127////////////////////////////////////////////////////////////////////////////////
128
129static const char* gMenuEventType = "SkOSMenuEventType";
130static const char* gSlider_Min_Scalar = "SkOSMenuSlider_Min";
131static const char* gSlider_Max_Scalar = "SkOSMenuSlider_Max";
132static const char* gDelimiter = "|";
133static const char* gList_Items_Str = "SkOSMenuList_Items";
134static const char* gList_ItemCount_S32 = "SkOSMenuList_ItemCount";
135
136int 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
143int 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
150int 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 != NULL; 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
171int 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
181int 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
188int 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
195int 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
202bool SkOSMenu::FindListItemCount(const SkEvent& evt, int* count) {
203    return evt.isType(gMenuEventType) && evt.findS32(gList_ItemCount_S32, count);
204}
205
206bool 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 != NULL) {
210            SkString temp(text);
211            char* token = strtok((char*)temp.c_str(), gDelimiter);
212            int index = 0;
213            while (token != NULL) {
214                items[index].set(token, strlen(token));
215                token = strtok (NULL, gDelimiter);
216                ++index;
217            }
218        }
219        return true;
220    }
221    return false;
222}
223
224bool SkOSMenu::FindSliderMin(const SkEvent& evt, SkScalar* min) {
225    return evt.isType(gMenuEventType) && evt.findScalar(gSlider_Min_Scalar, min);
226}
227
228bool SkOSMenu::FindSliderMax(const SkEvent& evt, SkScalar* max) {
229    return evt.isType(gMenuEventType) && evt.findScalar(gSlider_Max_Scalar, max);
230}
231
232bool SkOSMenu::FindAction(const SkEvent& evt, const char label[]) {
233    return evt.isType(gMenuEventType) && evt.findString(label);
234}
235
236bool SkOSMenu::FindListIndex(const SkEvent& evt, const char slotName[], int* value) {
237    return evt.isType(gMenuEventType) && evt.findS32(slotName, value);
238}
239
240bool SkOSMenu::FindSliderValue(const SkEvent& evt, const char slotName[], SkScalar* value) {
241    return evt.isType(gMenuEventType) && evt.findScalar(slotName, value);
242}
243
244bool SkOSMenu::FindSwitchState(const SkEvent& evt, const char slotName[], bool* value) {
245    return evt.isType(gMenuEventType) && evt.findBool(slotName, value);
246}
247
248bool SkOSMenu::FindTriState(const SkEvent& evt, const char slotName[], SkOSMenu::TriState* value) {
249    return evt.isType(gMenuEventType) && evt.findS32(slotName, (int*)value);
250}
251
252bool 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