1 // -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*-
2 /* This file is part of the KDE project
3 Copyright (C) 2004 Esben Mose Hansen <kde@mosehansen.dk>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
24 #include <QStyleOption>
26 #include <kstringhandler.h>
30 #include "historyitem.h"
31 #include "popupproxy.h"
33 #include "klipperpopup.h"
36 PopupProxy::PopupProxy( KlipperPopup
* parent
, int menu_height
, int menu_width
)
38 m_proxy_for_menu( parent
),
39 m_spillPointer( parent
->history()->youngest() ),
40 m_menu_height( menu_height
),
41 m_menu_width( menu_width
),
44 connect( parent
->history(), SIGNAL( changed() ), SLOT( slotHistoryChanged() ) );
45 connect(m_proxy_for_menu
, SIGNAL(triggered(QAction
*)), parent
->history(), SLOT(slotMoveToTop(QAction
*)));
48 void PopupProxy::slotHistoryChanged() {
53 void PopupProxy::deleteMoreMenus() {
54 const KMenu
* myParent
= parent();
55 if ( myParent
!= m_proxy_for_menu
) {
56 KMenu
* delme
= m_proxy_for_menu
;
57 m_proxy_for_menu
= static_cast<KMenu
*>( m_proxy_for_menu
->parent() );
58 while ( m_proxy_for_menu
!= myParent
) {
59 delme
= m_proxy_for_menu
;
60 m_proxy_for_menu
= static_cast<KMenu
*>( m_proxy_for_menu
->parent() );
62 // We are called probably from within the menus event-handler (triggered=>slotMoveToTop=>changed=>slotHistoryChanged=>deleteMoreMenus)
63 // what can result in a crash if we just delete the menu here (#155196 and #165154) So, delay the delete.
68 int PopupProxy::buildParent( int index
, const QRegExp
& filter
) {
70 // Start from top of history (again)
71 m_spillPointer
= parent()->history()->youngest();
73 if ( filter
.isValid() ) {
77 return insertFromSpill( index
);
81 KlipperPopup
* PopupProxy::parent() {
82 return static_cast<KlipperPopup
*>( QObject::parent() );
85 void PopupProxy::slotAboutToShow() {
89 void PopupProxy::tryInsertItem( HistoryItem
const * const item
,
93 QAction
*action
= new QAction(m_proxy_for_menu
);
94 QPixmap
image( item
->image() );
95 if ( image
.isNull() ) {
96 // Squeeze text strings so that do not take up the entire screen (or more)
97 QString text
= m_proxy_for_menu
->fontMetrics().elidedText( item
->text().simplified(), Qt::ElideMiddle
, m_menu_width
);
98 text
.replace( '&', "&&" );
99 action
->setText(text
);
101 #if 0 // not used because QAction#setIcon does not respect this size; it does scale anyway. TODO: find a way to set a bigger image
102 const QSize
max_size( m_menu_width
,m_menu_height
/4 );
103 if ( image
.height() > max_size
.height() || image
.width() > max_size
.width() ) {
104 image
= image
.scaled( max_size
, Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
107 action
->setIcon(QIcon(image
));
110 action
->setData(m_nextItemNumber
);
112 // if the m_proxy_for_menu is a submenu (aka a "More" menu) then it may the case, that there is no other action in that menu yet.
113 QAction
*before
= index
< m_proxy_for_menu
->actions().count() ? m_proxy_for_menu
->actions().at(index
) : 0;
114 // insert the new action to the m_proxy_for_menu
115 m_proxy_for_menu
->insertAction(before
, action
);
117 // Determine height of a menu item.
118 int itemheight
= QFontMetrics(m_proxy_for_menu
->fontMetrics()).height();
120 //TODO Use old-style QStyle and QStyleOption API
122 Q_ASSERT( id
!= -1 ); // Be sure that the item was inserted.
123 QMenuItem
* mi
= m_proxy_for_menu
->findItem( id
);
126 int itemheight
= m_proxy_for_menu
->style().sizeFromContents(QStyle::CT_PopupMenuItem
,
128 QSize( 0, fontheight
),
129 QStyleOption(mi
,10,0) ).height();
131 // Test if there was enough space
132 remainingHeight
-= itemheight
;
135 int PopupProxy::insertFromSpill( int index
) {
137 // This menu is going to be filled, so we don't need the aboutToShow()
139 disconnect( m_proxy_for_menu
, 0, this, 0 );
141 // Insert history items into the current m_proxy_for_menu,
142 // discarding any that doesn't match the current filter.
143 // stop when the total number of items equal m_itemsPerMenu;
145 int remainingHeight
= m_menu_height
- m_proxy_for_menu
->sizeHint().height();
146 // Force at least one item to be inserted.
147 remainingHeight
= qMax( remainingHeight
, 0 );
149 while (m_spillPointer
.hasNext() && remainingHeight
>= 0) {
150 const HistoryItem
*item
= m_spillPointer
.next();
151 if ( m_filter
.indexIn( item
->text() ) == -1) {
152 m_nextItemNumber
++; // also count hidden items
155 tryInsertItem( item
, remainingHeight
, index
++ );
160 // If there is more items in the history, insert a new "More..." menu and
161 // make *this a proxy for that menu ('s content).
162 if (m_spillPointer
.hasNext()) {
163 KMenu
* moreMenu
= new KMenu(i18n("&More"), m_proxy_for_menu
);
164 connect(moreMenu
, SIGNAL(aboutToShow()), SLOT(slotAboutToShow()));
165 QAction
*before
= index
< m_proxy_for_menu
->actions().count() ? m_proxy_for_menu
->actions().at(index
) : 0;
166 m_proxy_for_menu
->insertMenu(before
, moreMenu
);
167 m_proxy_for_menu
= moreMenu
;
170 // Return the number of items inserted.
174 #include "popupproxy.moc"