summaryrefslogtreecommitdiff
path: root/src/gui/piePreview.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/piePreview.vala')
-rw-r--r--src/gui/piePreview.vala369
1 files changed, 369 insertions, 0 deletions
diff --git a/src/gui/piePreview.vala b/src/gui/piePreview.vala
new file mode 100644
index 0000000..5745fcb
--- /dev/null
+++ b/src/gui/piePreview.vala
@@ -0,0 +1,369 @@
+/*
+Copyright (c) 2011 by Simon Schneegans
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace GnomePie {
+
+/////////////////////////////////////////////////////////////////////////
+/// A custom widget displaying the preview of a Pie. It can be used to
+/// configure the displayed Pie in various aspects.
+/////////////////////////////////////////////////////////////////////////
+
+class PiePreview : Gtk.DrawingArea {
+
+ /////////////////////////////////////////////////////////////////////
+ /// These get called when the last Slice is removed and when the
+ /// first Slice is added respectively.
+ /////////////////////////////////////////////////////////////////////
+
+ public signal void on_last_slice_removed();
+ public signal void on_first_slice_added();
+
+ /////////////////////////////////////////////////////////////////////
+ /// The internally used renderer to draw the Pie.
+ /////////////////////////////////////////////////////////////////////
+
+ private PiePreviewRenderer renderer = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The window which pops up, when a Slice is added or edited.
+ /////////////////////////////////////////////////////////////////////
+
+ private NewSliceWindow? new_slice_window = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /// A timer used for calculating the frame time.
+ /////////////////////////////////////////////////////////////////////
+
+ private GLib.Timer timer;
+
+ /////////////////////////////////////////////////////////////////////
+ /// True, when it is possible to drag a slice from this widget.
+ /// False, when the user currently hovers over the add sign.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool drag_enabled = false;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The ID of the currently displayed Pie.
+ /////////////////////////////////////////////////////////////////////
+
+ private string current_id = "";
+
+ /////////////////////////////////////////////////////////////////////
+ /// The position from where a Slice-drag started.
+ /////////////////////////////////////////////////////////////////////
+
+ private int drag_start_index = -1;
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, creates the widget.
+ /////////////////////////////////////////////////////////////////////
+
+ public PiePreview() {
+ this.renderer = new PiePreviewRenderer();
+
+ #if HAVE_GTK_3
+ this.draw.connect(this.on_draw);
+ #else
+ this.expose_event.connect(this.on_draw);
+ #endif
+
+ this.timer = new GLib.Timer();
+ this.set_events(Gdk.EventMask.POINTER_MOTION_MASK
+ | Gdk.EventMask.LEAVE_NOTIFY_MASK
+ | Gdk.EventMask.ENTER_NOTIFY_MASK);
+
+ // setup drag and drop
+ this.enable_drag_source();
+
+ Gtk.TargetEntry uri_dest = {"text/uri-list", 0, 0};
+ Gtk.TargetEntry slice_dest = {"text/plain", Gtk.TargetFlags.SAME_WIDGET, 0};
+ Gtk.TargetEntry[] destinations = { uri_dest, slice_dest };
+ Gtk.drag_dest_set(this, Gtk.DestDefaults.ALL, destinations, Gdk.DragAction.COPY | Gdk.DragAction.MOVE | Gdk.DragAction.LINK);
+
+ this.drag_begin.connect(this.on_start_drag);
+ this.drag_end.connect(this.on_end_drag);
+ this.drag_data_received.connect(this.on_dnd_received);
+
+ // connect mouse events
+ this.drag_motion.connect(this.on_drag_move);
+ this.leave_notify_event.connect(this.on_mouse_leave);
+ this.enter_notify_event.connect(this.on_mouse_enter);
+ this.motion_notify_event.connect_after(this.on_mouse_move);
+ this.button_release_event.connect_after(this.on_button_release);
+ this.button_press_event.connect_after(this.on_button_press);
+
+ this.new_slice_window = new NewSliceWindow();
+ this.new_slice_window.on_select.connect((new_action, as_new_slice, at_position) => {
+ var pie = PieManager.all_pies[this.current_id];
+
+ if (new_action.has_quickaction())
+ renderer.disable_quickactions();
+
+ if (as_new_slice) {
+ pie.add_group(new_action, at_position+1);
+ this.renderer.add_group(new_action, at_position+1);
+
+ if (this.renderer.slice_count() == 1)
+ this.on_first_slice_added();
+ } else {
+ pie.update_group(new_action, at_position);
+ this.renderer.update_group(new_action, at_position);
+ }
+ });
+
+ this.renderer.on_edit_slice.connect((pos) => {
+ this.new_slice_window.reload();
+
+ this.new_slice_window.set_parent(this.get_toplevel() as Gtk.Window);
+ this.new_slice_window.show();
+
+ var pie = PieManager.all_pies[this.current_id];
+ this.new_slice_window.set_action(pie.action_groups[pos], pos);
+ });
+
+ this.renderer.on_add_slice.connect((pos) => {
+ this.new_slice_window.reload();
+
+ this.new_slice_window.set_parent(this.get_toplevel() as Gtk.Window);
+ this.new_slice_window.show();
+
+ this.new_slice_window.set_default(this.current_id, pos);
+ });
+
+ this.renderer.on_remove_slice.connect((pos) => {
+
+ var dialog = new Gtk.MessageDialog(this.get_toplevel() as Gtk.Window, Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO,
+ _("Do you really want to delete this Slice?"));
+
+ dialog.response.connect((response) => {
+ if (response == Gtk.ResponseType.YES) {
+ var pie = PieManager.all_pies[this.current_id];
+
+ pie.remove_group(pos);
+ this.renderer.remove_group(pos);
+
+ if (this.renderer.slice_count() == 0)
+ this.on_last_slice_removed();
+ }
+ });
+
+ dialog.run();
+ dialog.destroy();
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Sets the currently displayed Pie to the Pie with the given ID.
+ /////////////////////////////////////////////////////////////////////
+
+ public void set_pie(string id) {
+ this.current_id = id;
+ this.modify_bg(Gtk.StateType.NORMAL, Gtk.rc_get_style(this).light[0]);
+ this.renderer.load_pie(PieManager.all_pies[id]);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Begins the draw loop. It automatically ends, when the containing
+ /// window becomes invisible.
+ /////////////////////////////////////////////////////////////////////
+
+ public void draw_loop() {
+ this.timer.start();
+ this.queue_draw();
+
+ GLib.Timeout.add((uint)(1000.0/Config.global.refresh_rate), () => {
+ this.queue_draw();
+ return this.get_toplevel().visible;
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called every frame.
+ /////////////////////////////////////////////////////////////////////
+
+ #if HAVE_GTK_3
+ private bool on_draw(Cairo.Context ctx) {
+ #else
+ private bool on_draw(Gtk.Widget da, Gdk.EventExpose event) {
+ var ctx = Gdk.cairo_create(this.get_window());
+ #endif
+ // store the frame time
+ double frame_time = this.timer.elapsed();
+ this.timer.reset();
+
+ Gtk.Allocation allocation;
+ this.get_allocation(out allocation);
+ ctx.translate((int)(allocation.width*0.5), (int)(allocation.height*0.5));
+
+ this.renderer.draw(frame_time, ctx);
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the mouse leaves the area of this widget.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool on_mouse_leave(Gdk.EventCrossing event) {
+ this.renderer.on_mouse_leave();
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the mouse enters the area of this widget.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool on_mouse_enter(Gdk.EventCrossing event) {
+ this.renderer.on_mouse_enter();
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the mouse moves in the area of this widget.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool on_mouse_move(Gdk.EventMotion event) {
+ this.renderer.set_dnd_mode(false);
+ Gtk.Allocation allocation;
+ this.get_allocation(out allocation);
+ this.renderer.on_mouse_move(event.x-allocation.width*0.5, event.y-allocation.height*0.5);
+
+ if (this.renderer.get_active_slice() < 0) this.disable_drag_source();
+ else this.enable_drag_source();
+
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when a mouse button is pressed.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool on_button_press() {
+ this.renderer.on_button_press();
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when a mouse button is released.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool on_button_release() {
+ if (!this.renderer.drag_n_drop_mode)
+ this.renderer.on_button_release();
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the mouse is moved over this widget.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool on_drag_move(Gdk.DragContext ctx, int x, int y, uint time) {
+ this.renderer.set_dnd_mode(true);
+ Gtk.Allocation allocation;
+ this.get_allocation(out allocation);
+ this.renderer.on_mouse_move(x-allocation.width*0.5, y-allocation.height*0.5);
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the user tries to drag something from this widget.
+ /////////////////////////////////////////////////////////////////////
+
+ private void on_start_drag(Gdk.DragContext ctx) {
+ this.drag_start_index = this.renderer.get_active_slice();
+ var icon = this.renderer.get_active_icon();
+ var pixbuf = icon.to_pixbuf();
+
+ this.renderer.hide_group(this.drag_start_index);
+ Gtk.drag_set_icon_pixbuf(ctx, pixbuf, icon.size()/2, icon.size()/2);
+
+ this.renderer.set_dnd_mode(true);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the user finishes a drag operation on this widget.
+ /// Only used for Slice-movement.
+ /////////////////////////////////////////////////////////////////////
+
+ private void on_end_drag(Gdk.DragContext context) {
+
+ if (context.list_targets() != null) {
+
+ int target_index = this.renderer.get_active_slice();
+ this.renderer.set_dnd_mode(false);
+
+ context.list_targets().foreach((target) => {
+ Gdk.Atom target_type = (Gdk.Atom)target;
+ if (target_type.name() == "text/plain") {
+ var pie = PieManager.all_pies[this.current_id];
+ pie.move_group(this.drag_start_index, target_index);
+ this.renderer.show_hidden_group_at(target_index);
+ }
+ });
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the user finishes a drag operation on this widget.
+ /// Only used for external drags.
+ /////////////////////////////////////////////////////////////////////
+
+ private void on_dnd_received(Gdk.DragContext context, int x, int y,
+ Gtk.SelectionData selection_data, uint info, uint time_) {
+
+ var pie = PieManager.all_pies[this.current_id];
+ int position = this.renderer.get_active_slice();
+ this.renderer.set_dnd_mode(false);
+
+ foreach (var uri in selection_data.get_uris()) {
+ pie.add_action(ActionRegistry.new_for_uri(uri), position);
+ this.renderer.add_group(pie.action_groups[position], position);
+
+ if (this.renderer.slices.size == 1)
+ this.on_first_slice_added();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Enables this widget to be a source for drag operations.
+ /////////////////////////////////////////////////////////////////////
+
+ private void enable_drag_source() {
+ if (!this.drag_enabled) {
+ this.drag_enabled = true;
+ Gtk.TargetEntry slice_source = {"text/plain", Gtk.TargetFlags.SAME_WIDGET | Gtk.TargetFlags.SAME_APP, 0};
+ Gtk.TargetEntry[] sources = { slice_source };
+ Gtk.drag_source_set(this, Gdk.ModifierType.BUTTON1_MASK, sources, Gdk.DragAction.MOVE);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Disables this widget to be a source for drag operations.
+ /////////////////////////////////////////////////////////////////////
+
+ private void disable_drag_source() {
+ if (this.drag_enabled) {
+ this.drag_enabled = false;
+ Gtk.drag_source_unset(this);
+ }
+ }
+
+}
+
+}