1 | /* This file is part of the KDE project |
2 | Copyright (C) 2004 Esben Mose Hansen <kde@mosehansen.dk> |
3 | |
4 | This program is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2 of the License, or (at your option) any later version. |
8 | |
9 | This program is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with this program; see the file COPYING. If not, write to |
16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | Boston, MA 02110-1301, USA. |
18 | */ |
19 | #include "popupproxy.h" |
20 | |
21 | #include <QtCore/QRegExp> |
22 | #include <QtGui/QStyle> |
23 | #include <QtGui/QPixmap> |
24 | #include <QtGui/QImage> |
25 | #include <QtGui/QStyleOption> |
26 | |
27 | #include <KMenu> |
28 | #include <KLocale> |
29 | #include <KDebug> |
30 | |
31 | #include "historyitem.h" |
32 | #include "history.h" |
33 | #include "klipperpopup.h" |
34 | |
35 | |
36 | PopupProxy::( KlipperPopup* parent, int , int ) |
37 | : QObject( parent ), |
38 | m_proxy_for_menu( parent ), |
39 | m_spill_uuid(), |
40 | m_menu_height( menu_height ), |
41 | m_menu_width( menu_width ) |
42 | { |
43 | if (!parent->history()->empty()) { |
44 | m_spill_uuid = parent->history()->first()->uuid(); |
45 | } |
46 | connect( parent->history(), SIGNAL(changed()), SLOT(slotHistoryChanged()) ); |
47 | connect(m_proxy_for_menu, SIGNAL(triggered(QAction*)), parent->history(), SLOT(slotMoveToTop(QAction*))); |
48 | } |
49 | |
50 | void PopupProxy::() { |
51 | deleteMoreMenus(); |
52 | |
53 | } |
54 | |
55 | void PopupProxy::() { |
56 | const KMenu* myParent = parent(); |
57 | if ( myParent != m_proxy_for_menu ) { |
58 | KMenu* delme = m_proxy_for_menu; |
59 | m_proxy_for_menu = static_cast<KMenu*>( m_proxy_for_menu->parent() ); |
60 | while ( m_proxy_for_menu != myParent ) { |
61 | delme = m_proxy_for_menu; |
62 | m_proxy_for_menu = static_cast<KMenu*>( m_proxy_for_menu->parent() ); |
63 | } |
64 | // We are called probably from within the menus event-handler (triggered=>slotMoveToTop=>changed=>slotHistoryChanged=>deleteMoreMenus) |
65 | // what can result in a crash if we just delete the menu here (#155196 and #165154) So, delay the delete. |
66 | delme->deleteLater(); |
67 | } |
68 | } |
69 | |
70 | int PopupProxy::( int index, const QRegExp& filter ) { |
71 | deleteMoreMenus(); |
72 | // Start from top of history (again) |
73 | m_spill_uuid = parent()->history()->empty() ? QByteArray() : parent()->history()->first()->uuid(); |
74 | if ( filter.isValid() ) { |
75 | m_filter = filter; |
76 | } |
77 | |
78 | return insertFromSpill( index ); |
79 | |
80 | } |
81 | |
82 | KlipperPopup* PopupProxy::() { |
83 | return static_cast<KlipperPopup*>( QObject::parent() ); |
84 | } |
85 | |
86 | void PopupProxy::() { |
87 | insertFromSpill(); |
88 | } |
89 | |
90 | void PopupProxy::( HistoryItem const * const item, |
91 | int& remainingHeight, |
92 | const int index ) |
93 | { |
94 | QAction *action = new QAction(m_proxy_for_menu); |
95 | QPixmap image( item->image() ); |
96 | if ( image.isNull() ) { |
97 | // Squeeze text strings so that do not take up the entire screen (or more) |
98 | QString text = m_proxy_for_menu->fontMetrics().elidedText( item->text().simplified(), Qt::ElideMiddle, m_menu_width ); |
99 | text.replace( '&', "&&" ); |
100 | action->setText(text); |
101 | } else { |
102 | #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 |
103 | const QSize max_size( m_menu_width,m_menu_height/4 ); |
104 | if ( image.height() > max_size.height() || image.width() > max_size.width() ) { |
105 | image = image.scaled( max_size, Qt::KeepAspectRatio, Qt::SmoothTransformation ); |
106 | } |
107 | #endif |
108 | action->setIcon(QIcon(image)); |
109 | } |
110 | |
111 | action->setData(item->uuid()); |
112 | |
113 | // 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. |
114 | QAction *before = index < m_proxy_for_menu->actions().count() ? m_proxy_for_menu->actions().at(index) : 0; |
115 | // insert the new action to the m_proxy_for_menu |
116 | m_proxy_for_menu->insertAction(before, action); |
117 | |
118 | // Determine height of a menu item. |
119 | QStyleOptionMenuItem style_options; |
120 | // It would be much easier to use QMenu::initStyleOptions. But that is protected, so until we have a better |
121 | // excuse to subclass that, I'd rather implement this manually. |
122 | // Note 2 properties, tabwidth and maxIconWidth, are not available from the public interface, so those are left out (probably not |
123 | // important for height. Also, Exlsive checkType is disregarded as I don't think we will ever use it) |
124 | style_options.initFrom(m_proxy_for_menu); |
125 | style_options.checkType = action->isCheckable() ? QStyleOptionMenuItem::NonExclusive : QStyleOptionMenuItem::NotCheckable; |
126 | style_options.checked = action->isChecked(); |
127 | style_options.font = action->font(); |
128 | style_options.icon = action->icon(); |
129 | style_options.menuHasCheckableItems = true; |
130 | style_options.menuRect = m_proxy_for_menu->rect(); |
131 | style_options.text = action->text(); |
132 | |
133 | int font_height = QFontMetrics(m_proxy_for_menu->fontMetrics()).height(); |
134 | |
135 | int itemheight = m_proxy_for_menu->style()->sizeFromContents(QStyle::CT_MenuItem, |
136 | &style_options, |
137 | QSize( 0, font_height ), |
138 | m_proxy_for_menu).height(); |
139 | // Subtract the used height |
140 | remainingHeight -= itemheight; |
141 | } |
142 | |
143 | int PopupProxy::( int index ) { |
144 | |
145 | const History* history = parent()->history(); |
146 | // This menu is going to be filled, so we don't need the aboutToShow() |
147 | // signal anymore |
148 | disconnect( m_proxy_for_menu, 0, this, 0 ); |
149 | |
150 | // Insert history items into the current m_proxy_for_menu, |
151 | // discarding any that doesn't match the current filter. |
152 | // stop when the total number of items equal m_itemsPerMenu; |
153 | int count = 0; |
154 | int remainingHeight = m_menu_height - m_proxy_for_menu->sizeHint().height(); |
155 | const HistoryItem* item = history->find(m_spill_uuid); |
156 | if (!item) { |
157 | return count; |
158 | } |
159 | do { |
160 | if ( m_filter.indexIn( item->text() ) != -1) { |
161 | tryInsertItem( item, remainingHeight, index++ ); |
162 | count++; |
163 | } |
164 | item = history->find(item->next_uuid()); |
165 | } while ( item && history->first() != item && remainingHeight >= 0); |
166 | m_spill_uuid = item->uuid(); |
167 | |
168 | // If there is more items in the history, insert a new "More..." menu and |
169 | // make *this a proxy for that menu ('s content). |
170 | if (history->first() && m_spill_uuid != history->first()->uuid()) { |
171 | KMenu* = new KMenu(i18n("&More" ), m_proxy_for_menu); |
172 | connect(moreMenu, SIGNAL(aboutToShow()), SLOT(slotAboutToShow())); |
173 | QAction *before = index < m_proxy_for_menu->actions().count() ? m_proxy_for_menu->actions().at(index) : 0; |
174 | m_proxy_for_menu->insertMenu(before, moreMenu); |
175 | m_proxy_for_menu = moreMenu; |
176 | } |
177 | |
178 | // Return the number of items inserted. |
179 | return count; |
180 | |
181 | } |
182 | #include "popupproxy.moc" |
183 | |