Serenity Operating System
at portability 167 lines 5.9 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/HashMap.h> 28#include <AK/SharedBuffer.h> 29#include <LibGUI/Action.h> 30#include <LibGUI/ActionGroup.h> 31#include <LibGUI/Menu.h> 32#include <LibGUI/MenuItem.h> 33#include <LibGUI/WindowServerConnection.h> 34#include <LibGfx/Bitmap.h> 35 36//#define MENU_DEBUG 37 38namespace GUI { 39 40static HashMap<int, Menu*>& all_menus() 41{ 42 static HashMap<int, Menu*>* map; 43 if (!map) 44 map = new HashMap<int, Menu*>(); 45 return *map; 46} 47 48Menu* Menu::from_menu_id(int menu_id) 49{ 50 auto it = all_menus().find(menu_id); 51 if (it == all_menus().end()) 52 return nullptr; 53 return (*it).value; 54} 55 56Menu::Menu(const StringView& name) 57 : m_name(name) 58{ 59} 60 61Menu::~Menu() 62{ 63 unrealize_menu(); 64} 65 66void Menu::add_action(NonnullRefPtr<Action> action) 67{ 68 m_items.append(make<MenuItem>(m_menu_id, move(action))); 69#ifdef GMENU_DEBUG 70 dbgprintf("GUI::Menu::add_action(): MenuItem Menu ID: %d\n", m_menu_id); 71#endif 72} 73 74void Menu::add_submenu(NonnullRefPtr<Menu> submenu) 75{ 76 m_items.append(make<MenuItem>(m_menu_id, move(submenu))); 77} 78 79void Menu::add_separator() 80{ 81 m_items.append(make<MenuItem>(m_menu_id, MenuItem::Type::Separator)); 82} 83 84void Menu::realize_if_needed() 85{ 86 if (m_menu_id == -1) 87 realize_menu(); 88} 89 90void Menu::popup(const Gfx::Point& screen_position) 91{ 92 realize_if_needed(); 93 WindowServerConnection::the().post_message(Messages::WindowServer::PopupMenu(m_menu_id, screen_position)); 94} 95 96void Menu::dismiss() 97{ 98 if (m_menu_id == -1) 99 return; 100 WindowServerConnection::the().post_message(Messages::WindowServer::DismissMenu(m_menu_id)); 101} 102 103int Menu::realize_menu() 104{ 105 m_menu_id = WindowServerConnection::the().send_sync<Messages::WindowServer::CreateMenu>(m_name)->menu_id(); 106 107#ifdef MENU_DEBUG 108 dbgprintf("GUI::Menu::realize_menu(): New menu ID: %d\n", m_menu_id); 109#endif 110 ASSERT(m_menu_id > 0); 111 for (size_t i = 0; i < m_items.size(); ++i) { 112 auto& item = m_items[i]; 113 item.set_menu_id({}, m_menu_id); 114 item.set_identifier({}, i); 115 if (item.type() == MenuItem::Type::Separator) { 116 WindowServerConnection::the().send_sync<Messages::WindowServer::AddMenuSeparator>(m_menu_id); 117 continue; 118 } 119 if (item.type() == MenuItem::Type::Submenu) { 120 auto& submenu = *item.submenu(); 121 submenu.realize_if_needed(); 122 WindowServerConnection::the().send_sync<Messages::WindowServer::AddMenuItem>(m_menu_id, i, submenu.menu_id(), submenu.name(), true, false, false, "", -1, false); 123 continue; 124 } 125 if (item.type() == MenuItem::Type::Action) { 126 auto& action = *item.action(); 127 int icon_buffer_id = -1; 128 if (action.icon()) { 129 ASSERT(action.icon()->format() == Gfx::BitmapFormat::RGBA32); 130 ASSERT(action.icon()->size() == Gfx::Size(16, 16)); 131 if (action.icon()->shared_buffer_id() == -1) { 132 auto shared_buffer = SharedBuffer::create_with_size(action.icon()->size_in_bytes()); 133 ASSERT(shared_buffer); 134 auto shared_icon = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *shared_buffer, action.icon()->size()); 135 memcpy(shared_buffer->data(), action.icon()->bits(0), action.icon()->size_in_bytes()); 136 shared_buffer->seal(); 137 shared_buffer->share_with(WindowServerConnection::the().server_pid()); 138 action.set_icon(shared_icon); 139 } 140 icon_buffer_id = action.icon()->shared_buffer_id(); 141 } 142 auto shortcut_text = action.shortcut().is_valid() ? action.shortcut().to_string() : String(); 143 bool exclusive = action.group() && action.group()->is_exclusive() && action.is_checkable(); 144 WindowServerConnection::the().send_sync<Messages::WindowServer::AddMenuItem>(m_menu_id, i, -1, action.text(), action.is_enabled(), action.is_checkable(), action.is_checkable() ? action.is_checked() : false, shortcut_text, icon_buffer_id, exclusive); 145 } 146 } 147 all_menus().set(m_menu_id, this); 148 return m_menu_id; 149} 150 151void Menu::unrealize_menu() 152{ 153 if (m_menu_id == -1) 154 return; 155 all_menus().remove(m_menu_id); 156 WindowServerConnection::the().send_sync<Messages::WindowServer::DestroyMenu>(m_menu_id); 157 m_menu_id = 0; 158} 159 160Action* Menu::action_at(size_t index) 161{ 162 if (index >= m_items.size()) 163 return nullptr; 164 return m_items[index].action(); 165} 166 167}