1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/dbus/menu/menu_property_list.h"
6
7 #include <utility>
8
9 #include "base/strings/string16.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "build/build_config.h"
12 #include "ui/base/accelerators/accelerator.h"
13 #include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
14 #include "ui/base/models/image_model.h"
15 #include "ui/base/models/menu_model.h"
16 #include "ui/gfx/image/image.h"
17
18 #if defined(USE_X11)
19 #include "ui/events/keycodes/keyboard_code_conversion_x.h" // nogncheck
20 #include "ui/events/keycodes/keysym_to_unicode.h" // nogncheck
21 #endif
22
23 #if defined(USE_OZONE)
24 #include "ui/base/ui_base_features.h" // nogncheck
25 #include "ui/ozone/public/ozone_platform.h" // nogncheck
26 #include "ui/ozone/public/platform_menu_utils.h" // nogncheck
27 #endif
28
29 namespace {
30
ToDBusKeySym(ui::KeyboardCode code)31 std::string ToDBusKeySym(ui::KeyboardCode code) {
32 #if defined(USE_OZONE)
33 if (features::IsUsingOzonePlatform()) {
34 const auto* const platorm_menu_utils =
35 ui::OzonePlatform::GetInstance()->GetPlatformMenuUtils();
36 if (platorm_menu_utils)
37 return platorm_menu_utils->ToDBusKeySym(code);
38 return {};
39 }
40 #endif
41 #if defined(USE_X11)
42 return base::UTF16ToUTF8(
43 base::string16(1, ui::GetUnicodeCharacterFromXKeySym(
44 XKeysymForWindowsKeyCode(code, false))));
45 #endif
46 return {};
47 }
48
GetDbusMenuShortcut(ui::Accelerator accelerator)49 std::vector<DbusString> GetDbusMenuShortcut(ui::Accelerator accelerator) {
50 auto dbus_key_sym = ToDBusKeySym(accelerator.key_code());
51 if (dbus_key_sym.empty())
52 return {};
53
54 std::vector<DbusString> parts;
55 if (accelerator.IsCtrlDown())
56 parts.emplace_back("Control");
57 if (accelerator.IsAltDown())
58 parts.emplace_back("Alt");
59 if (accelerator.IsShiftDown())
60 parts.emplace_back("Shift");
61 if (accelerator.IsCmdDown())
62 parts.emplace_back("Super");
63 parts.emplace_back(dbus_key_sym);
64 return parts;
65 }
66
67 } // namespace
68
ComputeMenuPropertiesForMenuItem(ui::MenuModel * menu,int i)69 MenuItemProperties ComputeMenuPropertiesForMenuItem(ui::MenuModel* menu,
70 int i) {
71 // Properties should only be set if they differ from the default values.
72 MenuItemProperties properties;
73
74 // The dbusmenu interface has no concept of a "sublabel", "minor text", or
75 // "minor icon" like MenuModel has. Ignore these rather than trying to
76 // merge them with the regular label and icon.
77 base::string16 label = menu->GetLabelAt(i);
78 if (!label.empty()) {
79 properties["label"] = MakeDbusVariant(DbusString(
80 ui::ConvertAcceleratorsFromWindowsStyle(base::UTF16ToUTF8(label))));
81 }
82
83 if (!menu->IsEnabledAt(i))
84 properties["enabled"] = MakeDbusVariant(DbusBoolean(false));
85 if (!menu->IsVisibleAt(i))
86 properties["visible"] = MakeDbusVariant(DbusBoolean(false));
87
88 ui::ImageModel icon = menu->GetIconAt(i);
89 if (icon.IsImage()) {
90 properties["icon-data"] =
91 MakeDbusVariant(DbusByteArray(icon.GetImage().As1xPNGBytes()));
92 }
93
94 ui::Accelerator accelerator;
95 if (menu->GetAcceleratorAt(i, &accelerator)) {
96 auto parts = GetDbusMenuShortcut(accelerator);
97 if (!parts.empty()) {
98 properties["shortcut"] = MakeDbusVariant(
99 MakeDbusArray(DbusArray<DbusString>(std::move(parts))));
100 }
101 }
102
103 switch (menu->GetTypeAt(i)) {
104 case ui::MenuModel::TYPE_COMMAND:
105 case ui::MenuModel::TYPE_HIGHLIGHTED:
106 case ui::MenuModel::TYPE_TITLE:
107 // Nothing special to do.
108 break;
109 case ui::MenuModel::TYPE_CHECK:
110 case ui::MenuModel::TYPE_RADIO:
111 properties["toggle-type"] = MakeDbusVariant(DbusString(
112 menu->GetTypeAt(i) == ui::MenuModel::TYPE_CHECK ? "checkmark"
113 : "radio"));
114 properties["toggle-state"] =
115 MakeDbusVariant(DbusInt32(menu->IsItemCheckedAt(i) ? 1 : 0));
116 break;
117 case ui::MenuModel::TYPE_SEPARATOR:
118 // The dbusmenu interface doesn't have multiple types of separators like
119 // MenuModel. Just use a regular separator in all cases.
120 properties["type"] = MakeDbusVariant(DbusString("separator"));
121 break;
122 case ui::MenuModel::TYPE_BUTTON_ITEM:
123 // This type of menu represents a row of buttons, but the dbusmenu
124 // interface has no equivalent of this. Ignore these items for now
125 // since there's currently no uses of it that plumb into this codepath.
126 // If there are button menu items in the future, we'd have to fake them
127 // with multiple menu items.
128 NOTIMPLEMENTED();
129 break;
130 case ui::MenuModel::TYPE_SUBMENU:
131 case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
132 properties["children-display"] = MakeDbusVariant(DbusString("submenu"));
133 break;
134 }
135
136 return properties;
137 }
138
ComputeMenuPropertyChanges(const MenuItemProperties & old_properties,const MenuItemProperties & new_properties,MenuPropertyList * item_updated_props,MenuPropertyList * item_removed_props)139 void ComputeMenuPropertyChanges(const MenuItemProperties& old_properties,
140 const MenuItemProperties& new_properties,
141 MenuPropertyList* item_updated_props,
142 MenuPropertyList* item_removed_props) {
143 // Compute updated and removed properties.
144 for (const auto& pair : old_properties) {
145 const std::string& key = pair.first;
146 auto new_it = new_properties.find(key);
147 if (new_it != new_properties.end()) {
148 if (new_it->second != pair.second)
149 item_updated_props->push_back(key);
150 } else {
151 item_removed_props->push_back(key);
152 }
153 }
154 // Compute added properties.
155 for (const auto& pair : new_properties) {
156 const std::string& key = pair.first;
157 if (!base::Contains(old_properties, key))
158 item_updated_props->push_back(key);
159 }
160 }
161