1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 // Interface header.
31 #include "entitybrowserwindow.h"
32 
33 // UI definition header.
34 #include "ui_entitybrowserwindow.h"
35 
36 // appleseed.studio headers.
37 #include "utility/interop.h"
38 #include "utility/miscellaneous.h"
39 
40 // appleseed.foundation headers.
41 #include "foundation/utility/containers/dictionary.h"
42 #include "foundation/utility/foreach.h"
43 
44 // Qt headers.
45 #include <QDialogButtonBox>
46 #include <QList>
47 #include <QListWidget>
48 #include <QListWidgetItem>
49 #include <QPushButton>
50 #include <QRegExp>
51 #include <QShortcut>
52 #include <Qt>
53 #include <QWidget>
54 
55 using namespace foundation;
56 using namespace std;
57 
58 namespace appleseed {
59 namespace studio {
60 
EntityBrowserWindow(QWidget * parent,const string & window_title)61 EntityBrowserWindow::EntityBrowserWindow(
62     QWidget*                parent,
63     const string&           window_title)
64   : WindowBase(parent, "entity_browser_window")
65   , m_ui(new Ui::EntityBrowserWindow())
66 {
67     m_ui->setupUi(this);
68 
69     setAttribute(Qt::WA_DeleteOnClose);
70     setWindowFlags(Qt::Tool);
71     setWindowTitle(QString::fromStdString(window_title));
72 
73     resize(400, 300);
74 
75     m_ui->buttonbox->button(QDialogButtonBox::Ok)->setEnabled(false);
76     m_ui->pushbutton_clear_filter->setEnabled(false);
77 
78     create_connections();
79 
80     WindowBase::load_settings();
81 }
82 
~EntityBrowserWindow()83 EntityBrowserWindow::~EntityBrowserWindow()
84 {
85     delete m_ui;
86 }
87 
88 namespace
89 {
add_items_to_list_widget(QListWidget * list_widget,const StringDictionary & items)90     void add_items_to_list_widget(QListWidget* list_widget, const StringDictionary& items)
91     {
92         for (const_each<StringDictionary> i = items; i; ++i)
93         {
94             const QString item_label = i->key();
95             const QString item_value = i->value<QString>();
96 
97             QListWidgetItem* item = new QListWidgetItem(item_label, list_widget);
98             item->setData(Qt::UserRole, item_value);
99         }
100     }
101 
filter_item(QListWidgetItem * item,const QRegExp & regexp)102     void filter_item(QListWidgetItem* item, const QRegExp& regexp)
103     {
104         const bool visible = regexp.indexIn(item->text()) >= 0;
105         item->setHidden(!visible);
106     }
107 }
108 
add_items_page(const string & page_name,const string & page_label,const StringDictionary & items)109 void EntityBrowserWindow::add_items_page(
110     const string&           page_name,
111     const string&           page_label,
112     const StringDictionary& items)
113 {
114     QWidget* tab = new QWidget(m_ui->tab_widget);
115     QGridLayout* layout = new QGridLayout(tab);
116 
117     QListWidget* list_widget = new QListWidget(tab);
118     layout->addWidget(list_widget, 0, 0, 1, 1);
119 
120     disable_osx_focus_rect(list_widget);
121 
122     add_items_to_list_widget(list_widget, items);
123 
124     connect(
125         list_widget, SIGNAL(itemSelectionChanged()),
126         this, SLOT(slot_item_selection_changed()));
127 
128     connect(
129         list_widget, SIGNAL(itemActivated(QListWidgetItem*)),
130         this, SLOT(slot_item_activated(QListWidgetItem*)));
131 
132     const bool were_signals_blocked = m_ui->tab_widget->blockSignals(true);
133     const int tab_index = m_ui->tab_widget->addTab(tab, QString::fromStdString(page_label));
134     m_ui->tab_widget->blockSignals(were_signals_blocked);
135 
136     Page page;
137     page.m_page_name = page_name;
138     page.m_list_widget = list_widget;
139     m_pages[tab_index] = page;
140 }
141 
create_connections()142 void EntityBrowserWindow::create_connections()
143 {
144     connect(
145         m_ui->tab_widget, SIGNAL(currentChanged(int)),
146         this, SLOT(slot_current_tab_changed(int)));
147 
148     connect(m_ui->buttonbox, SIGNAL(accepted()), this, SLOT(slot_accept()));
149     connect(m_ui->buttonbox, SIGNAL(rejected()), this, SLOT(close()));
150     connect(m_ui->pushbutton_clear_filter, SIGNAL(clicked()), this, SLOT(slot_clear_filter()));
151 
152     connect(
153         m_ui->lineedit_filter, SIGNAL(textChanged(const QString&)),
154         SLOT(slot_filter_text_changed(const QString&)));
155 
156     connect(
157         create_window_local_shortcut(this, Qt::Key_Return), SIGNAL(activated()),
158         this, SLOT(slot_accept()));
159 
160     connect(
161         create_window_local_shortcut(this, Qt::Key_Enter), SIGNAL(activated()),
162         this, SLOT(slot_accept()));
163 
164     connect(
165         create_window_local_shortcut(this, Qt::Key_Escape), SIGNAL(activated()),
166         this, SLOT(close()));
167 }
168 
slot_current_tab_changed(int tab_index)169 void EntityBrowserWindow::slot_current_tab_changed(int tab_index)
170 {
171     slot_item_selection_changed();
172 }
173 
slot_item_selection_changed()174 void EntityBrowserWindow::slot_item_selection_changed()
175 {
176     const Page& page = m_pages[m_ui->tab_widget->currentIndex()];
177     const bool item_selected = page.m_list_widget->selectedItems().size() == 1;
178 
179     m_ui->buttonbox->button(QDialogButtonBox::Ok)->setEnabled(item_selected);
180 }
181 
slot_item_activated(QListWidgetItem * item)182 void EntityBrowserWindow::slot_item_activated(QListWidgetItem* item)
183 {
184     const Page& page = m_pages[m_ui->tab_widget->currentIndex()];
185 
186     emit signal_accepted(QString::fromStdString(page.m_page_name), item->data(Qt::UserRole).toString());
187 }
188 
slot_accept()189 void EntityBrowserWindow::slot_accept()
190 {
191     const Page& page = m_pages[m_ui->tab_widget->currentIndex()];
192     const QList<QListWidgetItem*> selected_items = page.m_list_widget->selectedItems();
193 
194     if (selected_items.size() == 1)
195         slot_item_activated(selected_items.first());
196 }
197 
slot_filter_text_changed(const QString & pattern)198 void EntityBrowserWindow::slot_filter_text_changed(const QString& pattern)
199 {
200     m_ui->pushbutton_clear_filter->setEnabled(!pattern.isEmpty());
201 
202     const QRegExp regexp(pattern, Qt::CaseInsensitive);
203     const Page& page = m_pages[m_ui->tab_widget->currentIndex()];
204 
205     for (int i = 0; i < page.m_list_widget->count(); ++i)
206         filter_item(page.m_list_widget->item(i), regexp);
207 }
208 
slot_clear_filter()209 void EntityBrowserWindow::slot_clear_filter()
210 {
211     m_ui->lineedit_filter->clear();
212     m_ui->pushbutton_clear_filter->setEnabled(false);
213 }
214 
215 }   // namespace studio
216 }   // namespace appleseed
217