1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2014 Thomas Schöps, Kai Pastor
4  *
5  *    This file is part of OpenOrienteering.
6  *
7  *    OpenOrienteering is free software: you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation, either version 3 of the License, or
10  *    (at your option) any later version.
11  *
12  *    OpenOrienteering is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #ifndef OPENORIENTEERING_PIE_MENU_H
23 #define OPENORIENTEERING_PIE_MENU_H
24 
25 #include <cmath>
26 
27 #include <QtGlobal>
28 #include <QHash>
29 #include <QObject>
30 #include <QPoint>
31 #include <QPolygon>
32 #include <QWidget>
33 
34 class QAction;
35 class QActionEvent;
36 class QHideEvent;
37 class QMouseEvent;
38 class QPaintEvent;
39 
40 namespace OpenOrienteering {
41 
42 
43 /**
44  * Displays a pie menu.
45  *
46  * This class has an API and behavior similar to QMenu,
47  * but is neither a subclass nor a full replacement.
48  */
49 class PieMenu : public QWidget
50 {
51 Q_OBJECT
52 public:
53 	/** Stores the geometry of a particular item in the PieMenu. */
54 	struct ItemGeometry
55 	{
56 		/** The area covered by the item. */
57 		QPolygon area;
58 
59 		/** The position of the center of the item. */
60 		QPoint   icon_pos;
61 	};
62 
63 	/** Constructs a pie menu with default settings. */
64 	PieMenu(QWidget* parent = nullptr);
65 
66 	/** Returns the minimum number of actions in the menu. */
67 	int minimumActionCount() const;
68 
69 	/** Sets the minimum number of actions in the menu (must be at least three). */
70 	void setMinimumActionCount(int count);
71 
72 	/** Returns the current number of visible actions. */
73 	int visibleActionCount() const;
74 
75 	/** Removes all actions from the menu.
76 	 *  Other than QMenu::clear(), this will not immediately delete any action,
77 	 *  even if owned by this object. (Actions owned by this object will be
78 	 *  deleted when this objected is deleted.)
79 	 *  @see QMenu::clear() */
80 	void clear();
81 
82 	/** Returns true if there are no visible actions in the menu,
83 	 *  false otherwise.
84 	 *  @see QMenu::isEmpty */
85 	bool isEmpty() const;
86 
87 	/** Returns the icon size (single dimension). */
88 	int iconSize() const;
89 
90 	/** Sets the icon size (single dimension).
91 	 *  @param icon_size the new size (at least 1) */
92 	void setIconSize(int icon_size);
93 
94 	/** Returns the action at pos.
95 	 *  Returns nullptr if there is no action at this place. */
96 	QAction* actionAt(const QPoint& pos) const;
97 
98 	/** Returns the geometry of the given action.
99 	 *  The action must be enabled and visible.
100 	 *  Otherwise the geometry will be empty.
101 	 *  @see QMenu::actionGeometry */
102 	ItemGeometry actionGeometry(QAction* action) const;
103 
104 	/** Returns the currently highlighted action.
105 	 *  Returns nullptr if no action is highlighted.
106 	 *  @see QMenu::activeAction */
107 	QAction* activeAction() const;
108 
109 	/** Sets the currently highlighted action.
110 	 *  The action must be enabled and visible.
111 	 *  Otherwise there will be no highlighted action.
112 	 *  @see QMenu::setActiveAction */
113 	void setActiveAction(QAction* action);
114 
115 	/** Shows the menu at the given absolute position.
116 	 *  @see QMenu::popup */
117 	void popup(const QPoint& pos);
118 
119 signals:
120 	/** This signal is emitted just before the menu is shown.
121 	 *  @see QMenu::aboutToShow */
122 	void aboutToShow();
123 
124 	/** This signal is emitted just before the menu is hidden.
125 	 *  @see QMenu::aboutToHide */
126 	void aboutToHide();
127 
128 	/** This signal is emitted when a menu item is highlighted.
129 	 *  @see QMenu::hovered */
130 	void hovered(QAction* action);
131 
132 	/** This signal is emitted when a menu item's action is triggered.
133 	 *  @see QMenu::triggered */
134 	void triggered(QAction* action);
135 
136 protected:
137 	void actionEvent(QActionEvent* event) override;
138 	void hideEvent(QHideEvent* event) override;
139 	void mouseMoveEvent(QMouseEvent* event) override;
140 	void mousePressEvent(QMouseEvent* event) override;
141 	void mouseReleaseEvent(QMouseEvent* event) override;
142 	void paintEvent(QPaintEvent* event) override;
143 
144 	void updateCachedState();
145 
146 	QPoint getPoint(double radius, double angle) const;
147 
148 	int minimum_action_count;
149 	int icon_size;
150 	QAction* active_action;
151 
152 	bool actions_changed;
153 	bool clicked;
154 	int total_radius;
155 	QPolygon outer_border;
156 	QPolygon inner_border;
157 	QHash< QAction*, ItemGeometry > geometries;
158 };
159 
160 
161 // ### PieMenu inline code ###
162 
163 inline
getPoint(double radius,double angle)164 QPoint PieMenu::getPoint(double radius, double angle) const
165 {
166 	return QPoint(total_radius + qRound(radius * -sin(angle)), total_radius + qRound(radius * -cos(angle)));
167 }
168 
169 
170 }  // namespace OpenOrienteering
171 
172 #endif
173