1 /*********
2 *
3 * This file is part of BibleTime's source code, http://www.bibletime.info/.
4 *
5 * Copyright 1999-2016 by the BibleTime developers.
6 * The BibleTime source code is licensed under the GNU General Public License version 2.0.
7 *
8 **********/
9 
10 #include "frontend/displaywindow/bttextwindowheaderwidget.h"
11 
12 #include <QHBoxLayout>
13 #include <QLabel>
14 #include <QMenu>
15 #include <QToolButton>
16 #include <QToolTip>
17 #include <QSizePolicy>
18 #include <QString>
19 #include "backend/config/btconfig.h"
20 #include "backend/managers/cswordbackend.h"
21 #include "bibletimeapp.h"
22 #include "frontend/displaywindow/bttextwindowheader.h"
23 #include "util/btconnect.h"
24 #include "util/cresmgr.h"
25 
26 
27 namespace {
28 const QString BookshelfShowHiddenKey = "GUI/bookshelfShowHidden";
29 } // anonymous namespace
30 
31 const char* ActionType = "ActionType";
32 const char * ModuleName = "ModuleName";
33 
BtTextWindowHeaderWidget(BtTextWindowHeader * parent,CSwordModuleInfo::ModuleType mtype)34 BtTextWindowHeaderWidget::BtTextWindowHeaderWidget(BtTextWindowHeader *parent, CSwordModuleInfo::ModuleType mtype)
35         : QWidget(parent),
36         m_moduleType(mtype),
37         m_popup(nullptr) {
38     QHBoxLayout* layout = new QHBoxLayout(this);
39     layout->setContentsMargins(0, 0, 0, 0);
40 
41     m_label = new QLabel("", this);
42     QSizePolicy sizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
43     m_label->setSizePolicy(sizePolicy);
44     m_label->setStyleSheet("QLabel{font-weight:bold}");
45     layout->addWidget(m_label, 0, Qt::AlignRight);
46 
47     m_button = new QToolButton( this );
48     m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
49     m_button->setPopupMode( QToolButton::InstantPopup );
50     m_button->setArrowType(Qt::NoArrow);
51     m_button->setStyleSheet("QToolButton{margin:0px;}QToolButton::menu-indicator{subcontrol-position: center center;}");
52     m_button->setToolTip( tr("Add/remove/replace") );
53 
54     layout->addWidget(m_button, 0, Qt::AlignLeft);
55 
56     m_separator = new QFrame(this);
57     m_separator->setFrameShape(QFrame::VLine);
58     layout->addWidget(m_separator);
59 }
60 
recreateWidget(QStringList newModulesToUse,QString thisModule,int newIndex,int lefLikeModules)61 void BtTextWindowHeaderWidget::recreateWidget(QStringList newModulesToUse, QString thisModule, int newIndex, int lefLikeModules) {
62     populateMenu();
63     updateWidget(newModulesToUse, thisModule, newIndex, lefLikeModules);
64 }
65 
66 // don't remove yet, maybe we'll add icons to buttons...
67 // const QString BtTextWindowHeaderWidget::iconName() {
68 //     switch (m_moduleType) {
69 //         case CSwordModuleInfo::Bible:
70 //             return (m_hasModule) ? CResMgr::modules::bible::icon_unlocked : CResMgr::modules::bible::icon_add;
71 //         case CSwordModuleInfo::Commentary:
72 //             return (m_hasModule) ? CResMgr::modules::commentary::icon_unlocked : CResMgr::modules::commentary::icon_add;
73 //         case CSwordModuleInfo::Lexicon:
74 //             return m_hasModule ? CResMgr::modules::lexicon::icon_unlocked : CResMgr::modules::lexicon::icon_add;
75 //         case CSwordModuleInfo::GenericBook:
76 //             return m_hasModule ? CResMgr::modules::book::icon_unlocked : CResMgr::modules::book::icon_add;
77 //         default: //return as default the bible icon
78 //             return CResMgr::modules::bible::icon_unlocked;
79 //     }
80 // }
81 
updateWidget(QStringList newModulesToUse,QString thisModule,int newIndex,int leftLikeModules)82 void BtTextWindowHeaderWidget::updateWidget(QStringList newModulesToUse, QString thisModule, int newIndex, int leftLikeModules) {
83     m_label->setText(thisModule);
84     m_id = newIndex;
85     // create the menu if it doesn't exist
86     if (!m_popup)
87         populateMenu();
88 
89     m_module = thisModule;
90 
91     //All items are iterated and the state is changed properly
92     QListIterator<QMenu*> it(m_submenus);
93     while (it.hasNext()) {
94         QMenu* popup = it.next();
95         Q_FOREACH(QAction * const a, popup->actions()) {
96             a->setChecked(a->property(ModuleName).toString() == thisModule);
97             a->setDisabled(
98                         newModulesToUse.contains(
99                             a->property(ModuleName).toString()));
100         }
101     }
102 
103     bool disableRemove = false;
104     if (newModulesToUse.count() == 1 ||
105         (newIndex == 0 && leftLikeModules == 1))
106         disableRemove = true;
107     m_removeAction->setDisabled(disableRemove);
108 
109     // Disable non-Bible categories on left replace menu
110     if (m_moduleType == CSwordModuleInfo::Bible && m_id == 0) {
111         QList<QAction*> actionsType = m_popup->actions();
112         for (int t=0; t<actionsType.count(); t++) {
113             QAction* actionType = actionsType.at(t);
114             QString typeText = actionType->text();
115             if (typeText != QObject::tr("Replace"))
116                 continue;
117             QMenu* menuType = actionType->menu();
118             if (menuType == nullptr)
119                 continue;
120             QList<QAction*> actions = menuType->actions();
121             for (int i=0; i<actions.count(); i++) {
122                 QAction* action = actions.at(i);
123                 QString text = action->text();
124                 if (text != QObject::tr("Bibles")) {
125                     action->setDisabled(true);
126                 }
127             }
128         }
129     }
130 }
131 
132 /** Is called after a module was selected in the popup */
moduleChosen(QAction * action)133 void BtTextWindowHeaderWidget::moduleChosen( QAction* action ) {
134     if (action->property(ActionType).toInt() == RemoveAction) { // note: this is for m_popup, the toplevel!
135         emit sigModuleRemove(m_id);
136         return;
137     }
138     if (action->property(ActionType).toInt() == AddAction) {
139         emit sigModuleAdd(m_id + 1, action->property(ModuleName).toString());
140         return;
141     }
142     if (action->property(ActionType).toInt() == ReplaceAction) {
143         emit sigModuleReplace(m_id, action->property(ModuleName).toString());
144     }
145 }
146 
147 
populateMenu()148 void BtTextWindowHeaderWidget::populateMenu() {
149     delete m_popup;
150     m_popup = new QMenu(m_button);
151 
152     BT_CONNECT(m_popup, SIGNAL(triggered(QAction *)),
153                this,    SLOT(moduleChosen(QAction *)));
154     m_button->setMenu(m_popup);
155 
156     m_removeAction = new QAction(tr("Remove"), m_popup);
157     m_removeAction->setProperty(ActionType, RemoveAction);
158     m_removeAction->setIcon(CResMgr::displaywindows::general::icon_removeModule());
159     m_popup->addAction(m_removeAction);
160 
161     // Add Replace and Add menus, both have all modules in them
162     QMenu* replaceItem = new QMenu(tr("Replace"), m_popup);
163     replaceItem->setIcon(CResMgr::displaywindows::general::icon_replaceModule());
164     replaceItem->setProperty(ActionType, ReplaceAction);
165     m_popup->addMenu(replaceItem);
166 
167     QMenu* addItem = new QMenu(tr("Add"), m_popup);
168     addItem->setProperty(ActionType, AddAction);
169     addItem->setIcon(CResMgr::displaywindows::general::icon_addModule());
170     m_popup->addMenu(addItem);
171 
172     QList<QMenu*> toplevelMenus;
173     toplevelMenus.append(replaceItem);
174     toplevelMenus.append(addItem);
175 
176     Q_FOREACH(QMenu * const menu, toplevelMenus) {
177         // ******* Add categories, languages and modules ********
178         // Filters: add only non-hidden, non-locked and correct type
179         BTModuleTreeItem::HiddenOff hiddenFilter;
180         QList<BTModuleTreeItem::Filter*> filters;
181         if (!btConfig().value<bool>(BookshelfShowHiddenKey, false)) {
182             filters.append(&hiddenFilter);
183         }
184         TypeFilter typeFilter(m_moduleType);
185         filters.append(&typeFilter);
186 
187         TypeOfAction const typeOfAction =
188                 static_cast<TypeOfAction>(menu->property(ActionType).toInt());
189         if (m_moduleType == CSwordModuleInfo::Bible) {
190             BTModuleTreeItem root(filters, BTModuleTreeItem::CatLangMod);
191             QList<BTModuleTreeItem::Filter*> filters2;
192             if (!btConfig().value<bool>(BookshelfShowHiddenKey, false)) {
193                 filters2.append(&hiddenFilter);
194             }
195             if (menu == addItem || menu == replaceItem) {
196                 TypeFilter typeFilter2(CSwordModuleInfo::Commentary);
197                 filters2.append(&typeFilter2);
198                 root.add_items(filters2);
199             }
200             addItemToMenu(&root, menu, typeOfAction);
201         }
202         else {
203             BTModuleTreeItem root(filters, BTModuleTreeItem::LangMod);
204             addItemToMenu(&root, menu, typeOfAction);
205         }
206     }
207 }
208 
addItemToMenu(BTModuleTreeItem * item,QMenu * menu,TypeOfAction actionType)209 void BtTextWindowHeaderWidget::addItemToMenu(BTModuleTreeItem* item, QMenu* menu, TypeOfAction actionType) {
210     Q_FOREACH(BTModuleTreeItem * const i, item->children()) {
211         if (i->type() == BTModuleTreeItem::Language ||
212             i->type() == BTModuleTreeItem::Category) {
213             // argument menu was m_popup, create and add a new lang menu to it
214             QMenu* langMenu = new QMenu(i->text(), this);
215             menu->addMenu(langMenu);
216             m_submenus.append(langMenu);
217             // add the module items to the lang menu
218             addItemToMenu(i, langMenu, actionType);
219         }
220         else {
221             // item must be module, create and add it to the lang menu
222             QString name(i->text());
223             QAction* modItem = new QAction(name, menu);
224             modItem->setCheckable(true);
225             modItem->setProperty(ActionType, actionType);
226             modItem->setProperty(ModuleName, name);
227             menu->addAction(modItem);
228         }
229     }
230 }
231