Serenity Operating System
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 "DisplayProperties.h"
28#include "ItemListModel.h"
29#include <AK/StringBuilder.h>
30#include <LibCore/DirIterator.h>
31#include <LibGUI/Action.h>
32#include <LibGUI/Application.h>
33#include <LibGUI/BoxLayout.h>
34#include <LibGUI/Button.h>
35#include <LibGUI/Desktop.h>
36#include <LibGUI/FileSystemModel.h>
37#include <LibGUI/GroupBox.h>
38#include <LibGUI/Label.h>
39#include <LibGUI/ListView.h>
40#include <LibGUI/ScrollBar.h>
41#include <LibGUI/Splitter.h>
42#include <LibGUI/TabWidget.h>
43#include <LibGUI/ToolBar.h>
44#include <LibGUI/Widget.h>
45#include <LibGUI/Window.h>
46#include <LibGUI/WindowServerConnection.h>
47#include <Servers/WindowServer/WindowManager.h>
48
49DisplayPropertiesWidget::DisplayPropertiesWidget()
50 : m_wm_config(Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini"))
51{
52 create_resolution_list();
53 create_wallpaper_list();
54 create_root_widget();
55 create_frame();
56}
57
58void DisplayPropertiesWidget::create_resolution_list()
59{
60 // TODO: Find a better way to get the default resolution
61 m_resolutions.append({ 640, 480 });
62 m_resolutions.append({ 800, 600 });
63 m_resolutions.append({ 1024, 768 });
64 m_resolutions.append({ 1280, 720 });
65 m_resolutions.append({ 1280, 1024 });
66 m_resolutions.append({ 1440, 900 });
67 m_resolutions.append({ 1600, 900 });
68 m_resolutions.append({ 1920, 1080 });
69 m_resolutions.append({ 2560, 1080 });
70
71 Gfx::Size find_size;
72
73 bool okay = false;
74 // Let's attempt to find the current resolution and select it!
75 find_size.set_width(m_wm_config->read_entry("Screen", "Width", "1024").to_int(okay));
76 if (!okay) {
77 fprintf(stderr, "DisplayProperties: failed to convert width to int!");
78 return;
79 }
80
81 find_size.set_height(m_wm_config->read_entry("Screen", "Height", "768").to_int(okay));
82 if (!okay) {
83 fprintf(stderr, "DisplayProperties: failed to convert height to int!");
84 return;
85 }
86
87 int index = 0;
88 for (auto& resolution : m_resolutions) {
89 if (resolution == find_size) {
90 m_selected_resolution = m_resolutions.at(index);
91 return; // We don't need to do anything else
92 }
93
94 index++;
95 }
96
97 m_selected_resolution = m_resolutions.at(0);
98}
99
100void DisplayPropertiesWidget::create_root_widget()
101{
102 m_root_widget = GUI::Widget::construct();
103 m_root_widget->set_layout(make<GUI::VerticalBoxLayout>());
104 m_root_widget->set_fill_with_background_color(true);
105 m_root_widget->layout()->set_margins({ 4, 4, 4, 16 });
106}
107
108void DisplayPropertiesWidget::create_wallpaper_list()
109{
110 m_selected_wallpaper = m_wm_config->read_entry("Background", "Wallpaper");
111 if (!m_selected_wallpaper.is_empty()) {
112 auto name_parts = m_selected_wallpaper.split('/');
113 m_selected_wallpaper = name_parts[2];
114 }
115
116 Core::DirIterator iterator("/res/wallpapers/", Core::DirIterator::Flags::SkipDots);
117
118 while (iterator.has_next()) {
119 m_wallpapers.append(iterator.next_path());
120 }
121}
122
123void DisplayPropertiesWidget::create_frame()
124{
125 auto tab_widget = m_root_widget->add<GUI::TabWidget>();
126
127 auto wallpaper_splitter = tab_widget->add_tab<GUI::VerticalSplitter>("Wallpaper");
128
129 auto wallpaper_content = wallpaper_splitter->add<GUI::Widget>();
130 wallpaper_content->set_layout(make<GUI::VerticalBoxLayout>());
131 wallpaper_content->layout()->set_margins({ 4, 4, 4, 4 });
132
133 m_wallpaper_preview = wallpaper_splitter->add<GUI::Label>();
134
135 auto wallpaper_list = wallpaper_content->add<GUI::ListView>();
136 wallpaper_list->set_background_color(Color::White);
137 wallpaper_list->set_model(*ItemListModel<AK::String>::create(m_wallpapers));
138
139 auto wallpaper_model = wallpaper_list->model();
140 auto find_first_wallpaper_index = m_wallpapers.find_first_index(m_selected_wallpaper);
141 ASSERT(find_first_wallpaper_index.has_value());
142 auto wallpaper_index_in_model = wallpaper_model->index(find_first_wallpaper_index.value(), wallpaper_list->model_column());
143 if (wallpaper_model->is_valid(wallpaper_index_in_model))
144 wallpaper_list->selection().set(wallpaper_index_in_model);
145
146 wallpaper_list->horizontal_scrollbar().set_visible(false);
147 wallpaper_list->on_selection = [this](auto& index) {
148 StringBuilder builder;
149 m_selected_wallpaper = m_wallpapers.at(index.row());
150 builder.append("/res/wallpapers/");
151 builder.append(m_selected_wallpaper);
152 m_wallpaper_preview->set_icon(Gfx::Bitmap::load_from_file(builder.to_string()));
153 m_wallpaper_preview->set_should_stretch_icon(true);
154 };
155
156 auto settings_splitter = tab_widget->add_tab<GUI::VerticalSplitter>("Settings");
157
158 auto settings_content = settings_splitter->add<GUI::Widget>();
159 settings_content->set_layout(make<GUI::VerticalBoxLayout>());
160 settings_content->layout()->set_margins({ 4, 4, 4, 4 });
161
162 auto resolution_list = settings_content->add<GUI::ListView>();
163 resolution_list->set_background_color(Color::White);
164 resolution_list->set_model(*ItemListModel<Gfx::Size>::create(m_resolutions));
165
166 auto resolution_model = resolution_list->model();
167 auto find_first_resolution_index = m_resolutions.find_first_index(m_selected_resolution);
168 ASSERT(find_first_resolution_index.has_value());
169 auto resolution_index_in_model = resolution_model->index(find_first_resolution_index.value(), resolution_list->model_column());
170 if (resolution_model->is_valid(resolution_index_in_model))
171 resolution_list->selection().set(resolution_index_in_model);
172
173 resolution_list->horizontal_scrollbar().set_visible(false);
174 resolution_list->on_selection = [this](auto& index) {
175 m_selected_resolution = m_resolutions.at(index.row());
176 };
177
178 settings_content->layout()->add_spacer();
179
180 // Add the apply and cancel buttons
181 auto bottom_widget = m_root_widget->add<GUI::Widget>();
182 bottom_widget->set_layout(make<GUI::HorizontalBoxLayout>());
183 bottom_widget->layout()->add_spacer();
184 bottom_widget->set_size_policy(Orientation::Vertical, GUI::SizePolicy::Fixed);
185 bottom_widget->set_preferred_size(1, 22);
186
187 auto apply_button = bottom_widget->add<GUI::Button>();
188 apply_button->set_text("Apply");
189 apply_button->set_size_policy(Orientation::Vertical, GUI::SizePolicy::Fixed);
190 apply_button->set_size_policy(Orientation::Horizontal, GUI::SizePolicy::Fixed);
191 apply_button->set_preferred_size(60, 22);
192 apply_button->on_click = [this, tab_widget](GUI::Button&) {
193 send_settings_to_window_server(tab_widget->active_tab_index());
194 };
195
196 auto ok_button = bottom_widget->add<GUI::Button>();
197 ok_button->set_text("OK");
198 ok_button->set_size_policy(Orientation::Vertical, GUI::SizePolicy::Fixed);
199 ok_button->set_size_policy(Orientation::Horizontal, GUI::SizePolicy::Fixed);
200 ok_button->set_preferred_size(60, 22);
201 ok_button->on_click = [this, tab_widget](GUI::Button&) {
202 send_settings_to_window_server(tab_widget->active_tab_index());
203 GUI::Application::the().quit();
204 };
205
206 auto cancel_button = bottom_widget->add<GUI::Button>();
207 cancel_button->set_text("Cancel");
208 cancel_button->set_size_policy(Orientation::Vertical, GUI::SizePolicy::Fixed);
209 cancel_button->set_size_policy(Orientation::Horizontal, GUI::SizePolicy::Fixed);
210 cancel_button->set_preferred_size(60, 22);
211 cancel_button->on_click = [](auto&) {
212 GUI::Application::the().quit();
213 };
214}
215
216void DisplayPropertiesWidget::send_settings_to_window_server(int tab_index)
217{
218 if (tab_index == TabIndices::Wallpaper) {
219 StringBuilder builder;
220 builder.append("/res/wallpapers/");
221 builder.append(m_selected_wallpaper);
222 GUI::Desktop::the().set_wallpaper(builder.to_string());
223 } else if (tab_index == TabIndices::Settings) {
224 dbg() << "Attempting to set resolution " << m_selected_resolution;
225 GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetResolution>(m_selected_resolution);
226 } else {
227 dbg() << "Invalid tab index " << tab_index;
228 }
229}