File indexing completed on 2024-05-12 04:57:58
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "bookmarkstoolbar.h" 0019 #include "bookmarkstoolbarbutton.h" 0020 #include "bookmarkstools.h" 0021 #include "bookmarksmodel.h" 0022 #include "bookmarkitem.h" 0023 #include "bookmarks.h" 0024 #include "mainapplication.h" 0025 #include "iconprovider.h" 0026 #include "qztools.h" 0027 0028 #include <QDragEnterEvent> 0029 #include <QHBoxLayout> 0030 #include <QMimeData> 0031 #include <QTimer> 0032 #include <QFrame> 0033 #include <QInputDialog> 0034 0035 BookmarksToolbar::BookmarksToolbar(BrowserWindow* window, QWidget* parent) 0036 : QWidget(parent) 0037 , m_window(window) 0038 , m_bookmarks(mApp->bookmarks()) 0039 , m_clickedBookmark(nullptr) 0040 , m_dropRow(-1) 0041 { 0042 setObjectName("bookmarksbar"); 0043 setAcceptDrops(true); 0044 setContextMenuPolicy(Qt::CustomContextMenu); 0045 0046 m_layout = new QHBoxLayout(this); 0047 auto contentsMargin = style()->pixelMetric(QStyle::PM_ToolBarItemMargin, nullptr, this) 0048 + style()->pixelMetric(QStyle::PM_ToolBarFrameWidth, nullptr, this); 0049 m_layout->setContentsMargins(contentsMargin, contentsMargin, contentsMargin, contentsMargin); 0050 m_layout->setSpacing(style()->pixelMetric(QStyle::PM_ToolBarItemSpacing, nullptr, this)); 0051 setLayout(m_layout); 0052 0053 m_updateTimer = new QTimer(this); 0054 m_updateTimer->setInterval(300); 0055 m_updateTimer->setSingleShot(true); 0056 connect(m_updateTimer, &QTimer::timeout, this, &BookmarksToolbar::refresh); 0057 0058 connect(m_bookmarks, &Bookmarks::bookmarkAdded, this, &BookmarksToolbar::bookmarksChanged); 0059 connect(m_bookmarks, &Bookmarks::bookmarkRemoved, this, &BookmarksToolbar::bookmarksChanged); 0060 connect(m_bookmarks, &Bookmarks::bookmarkChanged, this, &BookmarksToolbar::bookmarksChanged); 0061 connect(m_bookmarks, &Bookmarks::showOnlyIconsInToolbarChanged, this, &BookmarksToolbar::showOnlyIconsChanged); 0062 connect(m_bookmarks, &Bookmarks::showOnlyTextInToolbarChanged, this, &BookmarksToolbar::showOnlyTextChanged); 0063 connect(this, &QWidget::customContextMenuRequested, this, &BookmarksToolbar::contextMenuRequested); 0064 0065 refresh(); 0066 } 0067 0068 void BookmarksToolbar::contextMenuRequested(const QPoint &pos) 0069 { 0070 BookmarksToolbarButton* button = buttonAt(pos); 0071 m_clickedBookmark = button ? button->bookmark() : nullptr; 0072 0073 QMenu menu; 0074 QAction* actNewTab = menu.addAction(IconProvider::newTabIcon(), tr("Open in new tab")); 0075 QAction* actNewWindow = menu.addAction(IconProvider::newWindowIcon(), tr("Open in new window")); 0076 QAction* actNewPrivateWindow = menu.addAction(IconProvider::privateBrowsingIcon(), tr("Open in new private window")); 0077 menu.addSeparator(); 0078 QAction* actNewFolder = menu.addAction(QIcon::fromTheme(QSL("folder-new")), tr("New Folder")); 0079 QAction* actEdit = menu.addAction(tr("Edit")); 0080 QAction* actDelete = menu.addAction(QIcon::fromTheme(QSL("edit-delete")), tr("Delete")); 0081 menu.addSeparator(); 0082 m_actShowOnlyIcons = menu.addAction(tr("Show Only Icons")); 0083 m_actShowOnlyIcons->setCheckable(true); 0084 m_actShowOnlyIcons->setChecked(m_bookmarks->showOnlyIconsInToolbar()); 0085 connect(m_actShowOnlyIcons, &QAction::toggled, m_bookmarks, &Bookmarks::setShowOnlyIconsInToolbar); 0086 m_actShowOnlyText = menu.addAction(tr("Show Only Text")); 0087 m_actShowOnlyText->setCheckable(true); 0088 m_actShowOnlyText->setChecked(m_bookmarks->showOnlyTextInToolbar()); 0089 connect(m_actShowOnlyText, &QAction::toggled, m_bookmarks, &Bookmarks::setShowOnlyTextInToolbar); 0090 0091 connect(actNewTab, &QAction::triggered, this, &BookmarksToolbar::openBookmarkInNewTab); 0092 connect(actNewWindow, &QAction::triggered, this, &BookmarksToolbar::openBookmarkInNewWindow); 0093 connect(actNewPrivateWindow, &QAction::triggered, this, &BookmarksToolbar::openBookmarkInNewPrivateWindow); 0094 connect(actNewFolder, &QAction::triggered, this, &BookmarksToolbar::createNewFolder); 0095 connect(actEdit, &QAction::triggered, this, &BookmarksToolbar::editBookmark); 0096 connect(actDelete, &QAction::triggered, this, &BookmarksToolbar::deleteBookmark); 0097 0098 actEdit->setEnabled(m_clickedBookmark && m_bookmarks->canBeModified(m_clickedBookmark)); 0099 actDelete->setEnabled(m_clickedBookmark && m_bookmarks->canBeModified(m_clickedBookmark)); 0100 actNewTab->setEnabled(m_clickedBookmark && m_clickedBookmark->isUrl()); 0101 actNewWindow->setEnabled(m_clickedBookmark && m_clickedBookmark->isUrl()); 0102 actNewPrivateWindow->setEnabled(m_clickedBookmark && m_clickedBookmark->isUrl()); 0103 0104 menu.exec(mapToGlobal(pos)); 0105 0106 if (button) { 0107 // Clear mouseover state after closing menu 0108 button->update(); 0109 } 0110 0111 m_clickedBookmark = nullptr; 0112 m_actShowOnlyIcons = nullptr; 0113 m_actShowOnlyText = nullptr; 0114 } 0115 0116 void BookmarksToolbar::refresh() 0117 { 0118 clear(); 0119 0120 BookmarkItem* folder = mApp->bookmarks()->toolbarFolder(); 0121 0122 const auto children = folder->children(); 0123 for (BookmarkItem* child : children) { 0124 addItem(child); 0125 } 0126 0127 m_layout->addStretch(); 0128 } 0129 0130 void BookmarksToolbar::bookmarksChanged() 0131 { 0132 m_updateTimer->start(); 0133 } 0134 0135 void BookmarksToolbar::showOnlyIconsChanged(bool state) 0136 { 0137 if (state && m_actShowOnlyText) { 0138 m_actShowOnlyText->setChecked(false); 0139 } 0140 0141 for (int i = 0; i < m_layout->count(); ++i) { 0142 auto* b = qobject_cast<BookmarksToolbarButton*>(m_layout->itemAt(i)->widget()); 0143 if (b) { 0144 b->setShowOnlyIcon(state); 0145 } 0146 } 0147 } 0148 0149 void BookmarksToolbar::showOnlyTextChanged(bool state) 0150 { 0151 if (state && m_actShowOnlyIcons) { 0152 m_actShowOnlyIcons->setChecked(false); 0153 } 0154 0155 for (int i = 0; i < m_layout->count(); ++i) { 0156 auto* b = qobject_cast<BookmarksToolbarButton*>(m_layout->itemAt(i)->widget()); 0157 if (b) { 0158 b->setShowOnlyText(state); 0159 } 0160 } 0161 } 0162 0163 void BookmarksToolbar::openBookmarkInNewTab() 0164 { 0165 if (m_clickedBookmark) { 0166 BookmarksTools::openBookmarkInNewTab(m_window, m_clickedBookmark); 0167 } 0168 } 0169 0170 void BookmarksToolbar::openBookmarkInNewWindow() 0171 { 0172 if (m_clickedBookmark) { 0173 BookmarksTools::openBookmarkInNewWindow(m_clickedBookmark); 0174 } 0175 } 0176 0177 void BookmarksToolbar::openBookmarkInNewPrivateWindow() 0178 { 0179 if (m_clickedBookmark) { 0180 BookmarksTools::openBookmarkInNewPrivateWindow(m_clickedBookmark); 0181 } 0182 } 0183 0184 void BookmarksToolbar::createNewFolder() 0185 { 0186 QString name = QInputDialog::getText(nullptr, tr("New Folder"), tr("Enter Folder Name:")); 0187 0188 if (!name.isEmpty()) { 0189 BookmarkItem* parent; 0190 0191 if (m_clickedBookmark && m_clickedBookmark->isFolder()) { 0192 parent = m_clickedBookmark; 0193 } else { 0194 parent = m_bookmarks->toolbarFolder(); 0195 } 0196 0197 auto folder = new BookmarkItem(BookmarkItem::Folder); 0198 folder->setTitle(name); 0199 m_bookmarks->addBookmark(parent, folder); 0200 } 0201 } 0202 0203 void BookmarksToolbar::editBookmark() 0204 { 0205 if (m_clickedBookmark) { 0206 BookmarksTools::editBookmarkDialog(this, m_clickedBookmark); 0207 m_bookmarks->changeBookmark(m_clickedBookmark); 0208 } 0209 } 0210 0211 void BookmarksToolbar::deleteBookmark() 0212 { 0213 if (m_clickedBookmark) { 0214 m_bookmarks->removeBookmark(m_clickedBookmark); 0215 } 0216 } 0217 0218 void BookmarksToolbar::clear() 0219 { 0220 int count = m_layout->count(); 0221 0222 for (int i = 0; i < count; ++i) { 0223 QLayoutItem* item = m_layout->takeAt(0); 0224 delete item->widget(); 0225 delete item; 0226 } 0227 0228 Q_ASSERT(m_layout->isEmpty()); 0229 } 0230 0231 void BookmarksToolbar::addItem(BookmarkItem* item) 0232 { 0233 Q_ASSERT(item); 0234 0235 auto* button = new BookmarksToolbarButton(item, this); 0236 button->setMainWindow(m_window); 0237 button->setShowOnlyIcon(m_bookmarks->showOnlyIconsInToolbar()); 0238 button->setShowOnlyText(m_bookmarks->showOnlyTextInToolbar()); 0239 m_layout->addWidget(button); 0240 } 0241 0242 BookmarksToolbarButton* BookmarksToolbar::buttonAt(const QPoint &pos) 0243 { 0244 return qobject_cast<BookmarksToolbarButton*>(QApplication::widgetAt(mapToGlobal(pos))); 0245 } 0246 0247 QSize BookmarksToolbar::minimumSizeHint() const 0248 { 0249 QSize size = QWidget::minimumSizeHint(); 0250 size.setHeight(qMax(20, size.height())); 0251 return size; 0252 } 0253 0254 void BookmarksToolbar::dropEvent(QDropEvent* e) 0255 { 0256 int row = m_dropRow; 0257 clearDropIndicator(); 0258 0259 const QMimeData* mime = e->mimeData(); 0260 0261 if (!mime->hasUrls() && !mime->hasFormat(BookmarksButtonMimeData::mimeType())) { 0262 QWidget::dropEvent(e); 0263 return; 0264 } 0265 0266 BookmarkItem* parent = m_bookmarks->toolbarFolder(); 0267 BookmarkItem* bookmark = nullptr; 0268 0269 if (mime->hasFormat(BookmarksButtonMimeData::mimeType())) { 0270 const auto* bookmarkMime = static_cast<const BookmarksButtonMimeData*>(mime); 0271 bookmark = bookmarkMime->item(); 0272 const int initialIndex = bookmark->parent()->children().indexOf(bookmark); 0273 BookmarksToolbarButton* current = buttonAt(m_dropPos); 0274 if (initialIndex < m_layout->indexOf(current)) { 0275 row -= 1; 0276 } 0277 } else { 0278 const QUrl url = mime->urls().at(0); 0279 const QString title = mime->hasText() ? mime->text() : QString::fromUtf8(url.toEncoded(QUrl::RemoveScheme)); 0280 0281 bookmark = new BookmarkItem(BookmarkItem::Url); 0282 bookmark->setTitle(title); 0283 bookmark->setUrl(url); 0284 } 0285 0286 if (row >= 0) { 0287 m_bookmarks->insertBookmark(parent, row, bookmark); 0288 } else { 0289 m_bookmarks->addBookmark(parent, bookmark); 0290 } 0291 } 0292 0293 void BookmarksToolbar::dragEnterEvent(QDragEnterEvent* e) 0294 { 0295 const QMimeData* mime = e->mimeData(); 0296 0297 if ((mime->hasUrls() && mime->hasText()) || mime->hasFormat(BookmarksButtonMimeData::mimeType())) { 0298 e->acceptProposedAction(); 0299 return; 0300 } 0301 0302 QWidget::dragEnterEvent(e); 0303 } 0304 0305 void BookmarksToolbar::dragMoveEvent(QDragMoveEvent *e) 0306 { 0307 int eventX = e->position().toPoint().x(); 0308 BookmarksToolbarButton* button = buttonAt(e->position().toPoint()); 0309 m_dropPos = e->position().toPoint(); 0310 m_dropRow = m_layout->indexOf(button); 0311 if (button) { 0312 bool res = eventX - button->x() < button->x() + button->width() -eventX; 0313 m_dropRow = res ? m_dropRow : m_dropRow + 1; 0314 } else { 0315 m_dropRow = -1; 0316 } 0317 0318 update(); 0319 } 0320 0321 void BookmarksToolbar::dragLeaveEvent(QDragLeaveEvent *e) 0322 { 0323 Q_UNUSED(e); 0324 clearDropIndicator(); 0325 } 0326 0327 void BookmarksToolbar::clearDropIndicator() 0328 { 0329 m_dropRow = -1; 0330 update(); 0331 } 0332 0333 void BookmarksToolbar::paintEvent(QPaintEvent *p) 0334 { 0335 QWidget::paintEvent(p); 0336 0337 // Draw drop indicator 0338 if (m_dropRow != -1) { 0339 BookmarksToolbarButton* button = buttonAt(m_dropPos); 0340 if (button) { 0341 if (button->bookmark()->isFolder()) { 0342 return; 0343 } 0344 const QRect tr = QRect(button->x(), 0, button->width(), height()); 0345 QRect r; 0346 0347 if (m_dropRow == m_layout->indexOf(button)) { 0348 r = QRect(qMax(0, tr.left() - 2), tr.top(), 3, tr.height()); 0349 } else { 0350 r = QRect(tr.right() + 0, tr.top(), 3, tr.height()); 0351 } 0352 0353 QzTools::paintDropIndicator(this, r); 0354 } 0355 } 0356 }