diff options
| author | Alessandro Ghedini <al3xbio@gmail.com> | 2012-01-21 19:07:09 +0100 | 
|---|---|---|
| committer | Alessandro Ghedini <al3xbio@gmail.com> | 2012-01-21 19:07:09 +0100 | 
| commit | 60560a030fda3c539ff9dc1563b9926414a193da (patch) | |
| tree | 77590b395685a8d48d3615e45629a1610d08c071 /src | |
| parent | d6b2677825cbb423e2099563c16321c3e23d7899 (diff) | |
Imported Upstream version 0.4.0upstream/0.4.0
Diffstat (limited to 'src')
59 files changed, 4369 insertions, 2468 deletions
| diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23b9474..b809c48 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,10 @@ if (${INDICATOR_FOUND})    SET(DEFINES --define HAVE_APPINDICATOR)  endif(${INDICATOR_FOUND}) +if (${GTK3_FOUND}) +  SET(DEFINES --define HAVE_GTK_3) +endif(${GTK3_FOUND}) +  if (${GMENU3_FOUND})      LIST(APPEND DEFINES --define HAVE_GMENU_3)  endif (${GMENU3_FOUND}) @@ -39,7 +43,7 @@ install(  # install credits  install(  	FILES -		${CMAKE_SOURCE_DIR}/README +		${CMAKE_SOURCE_DIR}/README.md  	DESTINATION  		${CMAKE_INSTALL_PREFIX}/share/doc/gnome-pie  ) @@ -63,11 +67,18 @@ install(  		${CMAKE_INSTALL_PREFIX}/share/gnome-pie  ) +# install UI files +install( +	DIRECTORY +		${CMAKE_SOURCE_DIR}/resources/ui +	DESTINATION +		${CMAKE_INSTALL_PREFIX}/share/gnome-pie +) +  # install icons  install(  	FILES  		${CMAKE_SOURCE_DIR}/resources/gnome-pie.svg -		${CMAKE_SOURCE_DIR}/resources/gnome-pie-indicator.svg  	DESTINATION  		${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps  ) diff --git a/src/actionGroups/actionGroup.vala b/src/actionGroups/actionGroup.vala index a6b52ff..c54be2f 100644 --- a/src/actionGroups/actionGroup.vala +++ b/src/actionGroups/actionGroup.vala @@ -70,6 +70,27 @@ public class ActionGroup : GLib.Object {      public void delete_all() {          actions.clear();      } +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes all contained Slices no Quick Actions. +    ///////////////////////////////////////////////////////////////////// +     +    public void disable_quickactions() { +        foreach (var action in actions) +            action.is_quickaction = false; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Returns true, if one o the contained Slices is a Quick Action +    ///////////////////////////////////////////////////////////////////// +     +    public bool has_quickaction() { +        foreach (var action in actions) +            if (action.is_quickaction) +                return true; +                 +        return false; +    }  }  } diff --git a/src/actionGroups/bookmarkGroup.vala b/src/actionGroups/bookmarkGroup.vala index 389b14a..0a560c5 100644 --- a/src/actionGroups/bookmarkGroup.vala +++ b/src/actionGroups/bookmarkGroup.vala @@ -31,10 +31,13 @@ public class BookmarkGroup : ActionGroup {      /// the pies.conf file for this kind of ActionGroups.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out string icon, out string settings_name) { -        name = _("Bookmarks"); -        icon = "user-bookmarks"; -        settings_name = "bookmarks"; +    public static GroupRegistry.TypeDescription register() { +        var description = new GroupRegistry.TypeDescription(); +        description.name = _("Group: Bookmarks"); +        description.icon = "user-bookmarks"; +        description.description = _("Shows a Slice for each of your directory Bookmarks."); +        description.id = "bookmarks"; +        return description;      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actionGroups/clipboardGroup.vala b/src/actionGroups/clipboardGroup.vala index cd1da36..836c927 100644 --- a/src/actionGroups/clipboardGroup.vala +++ b/src/actionGroups/clipboardGroup.vala @@ -59,10 +59,13 @@ public class ClipboardGroup : ActionGroup {      /// the pies.conf file for this kind of ActionGroups.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out string icon, out string settings_name) { -        name = _("Clipboard"); -        icon = "edit-paste"; -        settings_name = "clipboard"; +    public static GroupRegistry.TypeDescription register() { +        var description = new GroupRegistry.TypeDescription(); +        description.name = _("Group: Clipboard"); +        description.icon = "edit-paste"; +        description.description = _("Manages your Clipboard."); +        description.id = "clipboard"; +        return description;      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actionGroups/devicesGroup.vala b/src/actionGroups/devicesGroup.vala index 3d2ced0..dee6a6e 100644 --- a/src/actionGroups/devicesGroup.vala +++ b/src/actionGroups/devicesGroup.vala @@ -30,10 +30,13 @@ public class DevicesGroup : ActionGroup {      /// the pies.conf file for this kind of ActionGroups.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out string icon, out string settings_name) { -        name = _("Devices"); -        icon = "harddrive"; -        settings_name = "devices"; +    public static GroupRegistry.TypeDescription register() { +        var description = new GroupRegistry.TypeDescription(); +        description.name = _("Group: Devices"); +        description.icon = "harddrive"; +        description.description = _("Shows a Slice for each plugged in devices, like USB-Sticks."); +        description.id = "devices"; +        return description;      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actionGroups/groupRegistry.vala b/src/actionGroups/groupRegistry.vala index a9f8d06..7510a03 100644 --- a/src/actionGroups/groupRegistry.vala +++ b/src/actionGroups/groupRegistry.vala @@ -27,67 +27,67 @@ public class GroupRegistry : GLib.Object {      /// A list containing all available ActionGroup types.      ///////////////////////////////////////////////////////////////////// -    public static Gee.ArrayList<Type> types { get; private set; } +    public static Gee.ArrayList<string> types { get; private set; }      ///////////////////////////////////////////////////////////////////// -    /// Three maps associating a displayable name for each ActionGroup,  +    /// A map associating a displayable name for each ActionGroup,       /// an icon name and a name for the pies.conf file with it's type.      ///////////////////////////////////////////////////////////////////// -    public static Gee.HashMap<Type, string> names { get; private set; } -    public static Gee.HashMap<Type, string> icons { get; private set; } -    public static Gee.HashMap<Type, string> settings_names { get; private set; } +    public static Gee.HashMap<string, TypeDescription?> descriptions { get; private set; } +     +    public class TypeDescription { +        public string name { get; set; default=""; } +        public string icon { get; set; default=""; } +        public string description { get; set; default=""; } +        public string id { get; set; default=""; } +    }      /////////////////////////////////////////////////////////////////////      /// Registers all ActionGroup types.      /////////////////////////////////////////////////////////////////////      public static void init() { -        types = new Gee.ArrayList<Type>(); +        types = new Gee.ArrayList<string>(); +        descriptions = new Gee.HashMap<string, TypeDescription?>(); -        names = new Gee.HashMap<Type, string>(); -        icons = new Gee.HashMap<Type, string>(); -        settings_names = new Gee.HashMap<Type, string>(); -     -        string name = ""; -        string icon = ""; -        string settings_name = ""; +        TypeDescription type_description; -        BookmarkGroup.register(out name, out icon, out settings_name); -        types.add(typeof(BookmarkGroup)); -        names.set(typeof(BookmarkGroup), name); -        icons.set(typeof(BookmarkGroup), icon); -        settings_names.set(typeof(BookmarkGroup), settings_name); +        type_description = BookmarkGroup.register(); +        types.add(typeof(BookmarkGroup).name()); +        descriptions.set(typeof(BookmarkGroup).name(), type_description); -        DevicesGroup.register(out name, out icon, out settings_name); -        types.add(typeof(DevicesGroup)); -        names.set(typeof(DevicesGroup), name); -        icons.set(typeof(DevicesGroup), icon); -        settings_names.set(typeof(DevicesGroup), settings_name); +        type_description = DevicesGroup.register(); +        types.add(typeof(DevicesGroup).name()); +        descriptions.set(typeof(DevicesGroup).name(), type_description); -        MenuGroup.register(out name, out icon, out settings_name); -        types.add(typeof(MenuGroup)); -        names.set(typeof(MenuGroup), name); -        icons.set(typeof(MenuGroup), icon); -        settings_names.set(typeof(MenuGroup), settings_name); +        type_description = MenuGroup.register(); +        types.add(typeof(MenuGroup).name()); +        descriptions.set(typeof(MenuGroup).name(), type_description); -        SessionGroup.register(out name, out icon, out settings_name); -        types.add(typeof(SessionGroup)); -        names.set(typeof(SessionGroup), name); -        icons.set(typeof(SessionGroup), icon); -        settings_names.set(typeof(SessionGroup), settings_name); +        type_description = SessionGroup.register(); +        types.add(typeof(SessionGroup).name()); +        descriptions.set(typeof(SessionGroup).name(), type_description); -        WindowListGroup.register(out name, out icon, out settings_name); -        types.add(typeof(WindowListGroup)); -        names.set(typeof(WindowListGroup), name); -        icons.set(typeof(WindowListGroup), icon); -        settings_names.set(typeof(WindowListGroup), settings_name); +        type_description = WindowListGroup.register(); +        types.add(typeof(WindowListGroup).name()); +        descriptions.set(typeof(WindowListGroup).name(), type_description); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Creates a Group for a given type name. +    ///////////////////////////////////////////////////////////////////// +     +    public static ActionGroup? create_group(string type_id, string parent_id) { +        switch (type_id) { +            case "bookmarks": return new BookmarkGroup(parent_id); +            case "devices": return new DevicesGroup(parent_id); +            case "menu": return new MenuGroup(parent_id); +            case "session": return new SessionGroup(parent_id); +            case "window_list": return new WindowListGroup(parent_id); +        } -//        ClipboardGroup.register(out name, out icon, out settings_name); -//        types.add(typeof(ClipboardGroup)); -//        names.set(typeof(ClipboardGroup), name); -//        icons.set(typeof(ClipboardGroup), icon); -//        settings_names.set(typeof(ClipboardGroup), settings_name); +        return null;      }  } diff --git a/src/actionGroups/menuGroup.vala b/src/actionGroups/menuGroup.vala index 07a4bd1..247376d 100644 --- a/src/actionGroups/menuGroup.vala +++ b/src/actionGroups/menuGroup.vala @@ -30,10 +30,13 @@ public class MenuGroup : ActionGroup {      /// the pies.conf file for this kind of ActionGroups.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out string icon, out string settings_name) { -        name = _("Main menu"); -        icon = "gnome-main-menu"; -        settings_name = "menu"; +    public static GroupRegistry.TypeDescription register() { +        var description = new GroupRegistry.TypeDescription(); +        description.name = _("Group: Main menu"); +        description.icon = "gnome-main-menu"; +        description.description = _("Displays your main menu structure."); +        description.id = "menu"; +        return description;      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actionGroups/sessionGroup.vala b/src/actionGroups/sessionGroup.vala index 0b3f249..26f8ebc 100644 --- a/src/actionGroups/sessionGroup.vala +++ b/src/actionGroups/sessionGroup.vala @@ -30,10 +30,13 @@ public class SessionGroup : ActionGroup {      /// the pies.conf file for this kind of ActionGroups.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out string icon, out string settings_name) { -        name = _("Session Control"); -        icon = "gnome-logout"; -        settings_name = "session"; +    public static GroupRegistry.TypeDescription register() { +        var description = new GroupRegistry.TypeDescription(); +        description.name = _("Group: Session Control"); +        description.icon = "gnome-logout"; +        description.description = _("Shows a Slice for Shutdown, Reboot, and Hibernate."); +        description.id = "session"; +        return description;      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actionGroups/windowListGroup.vala b/src/actionGroups/windowListGroup.vala index b12f188..18bf55b 100644 --- a/src/actionGroups/windowListGroup.vala +++ b/src/actionGroups/windowListGroup.vala @@ -29,10 +29,13 @@ public class WindowListGroup : ActionGroup {      /// the pies.conf file for this kind of ActionGroups.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out string icon, out string settings_name) { -        name = _("Window List"); -        icon = "window-manager"; -        settings_name = "window_list"; +    public static GroupRegistry.TypeDescription register() { +        var description = new GroupRegistry.TypeDescription(); +        description.name = _("Group: Window List"); +        description.icon = "window-manager"; +        description.description = _("Shows a Slice for each of your opened Windows. Almost like Alt-Tab."); +        description.id = "window_list"; +        return description;      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actions/action.vala b/src/actions/action.vala index ceed357..ff0e9cd 100644 --- a/src/actions/action.vala +++ b/src/actions/action.vala @@ -42,14 +42,14 @@ public abstract class Action : GLib.Object {      /// The name of the Action.      /////////////////////////////////////////////////////////////////////   -    public virtual string name { get; protected set; } +    public virtual string name { get; set; }      /////////////////////////////////////////////////////////////////////      /// The name of the icon of this Action. It should be in the users      /// current icon theme.      ///////////////////////////////////////////////////////////////////// -    public virtual string icon { get; protected set; } +    public virtual string icon { get; set; }      /////////////////////////////////////////////////////////////////////      /// True, if this Action is the quickAction of the associated Pie. @@ -57,14 +57,14 @@ public abstract class Action : GLib.Object {      /// the center of a Pie.      ///////////////////////////////////////////////////////////////////// -    public virtual bool is_quick_action { get; protected set; } +    public virtual bool is_quickaction { get; set; }      /////////////////////////////////////////////////////////////////////      /// C'tor, initializes all members.      ///////////////////////////////////////////////////////////////////// -    public Action(string name, string icon, bool is_quick_action) { -        GLib.Object(name : name, icon : icon, is_quick_action : is_quick_action); +    public Action(string name, string icon, bool is_quickaction) { +        GLib.Object(name : name, icon : icon, is_quickaction : is_quickaction);      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actions/actionRegistry.vala b/src/actions/actionRegistry.vala index 091865f..135e90c 100644 --- a/src/actions/actionRegistry.vala +++ b/src/actions/actionRegistry.vala @@ -27,56 +27,68 @@ public class ActionRegistry : GLib.Object {      /// A list containing all available Action types.      ///////////////////////////////////////////////////////////////////// -    public static Gee.ArrayList<Type> types { get; private set; } +    public static Gee.ArrayList<string> types { get; private set; }      ///////////////////////////////////////////////////////////////////// -    /// Three maps associating a displayable name for each Action,  +    /// A map associating a displayable name for each Action,       /// whether it has a custom icon and a name for the pies.conf      /// file with it's type.      ///////////////////////////////////////////////////////////////////// -    public static Gee.HashMap<Type, string> names { get; private set; } -    public static Gee.HashMap<Type, bool> icon_name_editables { get; private set; } -    public static Gee.HashMap<Type, string> settings_names { get; private set; } +    public static Gee.HashMap<string, TypeDescription?> descriptions { get; private set; } +     +    ///////////////////////////////////////////////////////////////////// +    /// A helper class storing information on a Action type. +    ///////////////////////////////////////////////////////////////////// +     +    public class TypeDescription { +        public string name { get; set; default=""; } +        public string icon { get; set; default=""; } +        public string description { get; set; default=""; } +        public string id { get; set; default=""; } +        public bool icon_name_editable { get; set; default=false; } +    }      /////////////////////////////////////////////////////////////////////      /// Registers all Action types.      /////////////////////////////////////////////////////////////////////      public static void init() { -        types = new Gee.ArrayList<Type>(); -     -        names = new Gee.HashMap<Type, string>(); -        icon_name_editables = new Gee.HashMap<Type, bool>(); -        settings_names = new Gee.HashMap<Type, string>(); +        types = new Gee.ArrayList<string>(); +        descriptions = new Gee.HashMap<string, TypeDescription?>(); -        string name = ""; -        bool icon_name_editable = true; -        string settings_name = ""; +        TypeDescription type_description; -        AppAction.register(out name, out icon_name_editable, out settings_name); -        types.add(typeof(AppAction)); -        names.set(typeof(AppAction), name); -        icon_name_editables.set(typeof(AppAction), icon_name_editable); -        settings_names.set(typeof(AppAction), settings_name); +        types.add(typeof(AppAction).name()); +        type_description = AppAction.register(); +        descriptions.set(typeof(AppAction).name(), type_description); -        KeyAction.register(out name, out icon_name_editable, out settings_name); -        types.add(typeof(KeyAction)); -        names.set(typeof(KeyAction), name); -        icon_name_editables.set(typeof(KeyAction), icon_name_editable); -        settings_names.set(typeof(KeyAction), settings_name); +        types.add(typeof(KeyAction).name()); +        type_description = KeyAction.register(); +        descriptions.set(typeof(KeyAction).name(), type_description); -        PieAction.register(out name, out icon_name_editable, out settings_name); -        types.add(typeof(PieAction)); -        names.set(typeof(PieAction), name); -        icon_name_editables.set(typeof(PieAction), icon_name_editable); -        settings_names.set(typeof(PieAction), settings_name); +        types.add(typeof(PieAction).name()); +        type_description = PieAction.register(); +        descriptions.set(typeof(PieAction).name(), type_description); +         +        types.add(typeof(UriAction).name()); +        type_description = UriAction.register(); +        descriptions.set(typeof(UriAction).name(), type_description); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Creates a new Action from the given type name. +    ///////////////////////////////////////////////////////////////////// +     +    public static Action? create_action(string type_id, string name, string icon, string command, bool quickaction) { +        switch (type_id) { +            case "app": return new AppAction(name, icon, command, quickaction); +            case "key": return new KeyAction(name, icon, command, quickaction); +            case "uri": return new UriAction(name, icon, command, quickaction); +            case "pie": return new PieAction(command, quickaction); +        } -        UriAction.register(out name, out icon_name_editable, out settings_name); -        types.add(typeof(UriAction)); -        names.set(typeof(UriAction), name); -        icon_name_editables.set(typeof(UriAction), icon_name_editable); -        settings_names.set(typeof(UriAction), settings_name); +        return null;      }      ///////////////////////////////////////////////////////////////////// @@ -109,10 +121,12 @@ public class ActionRegistry : GLib.Object {              case "http": case "https":                  final_icon = "www"; +                final_name = get_domain_name(uri);                  break;              case "ftp": case "sftp":                  final_icon = "folder-remote"; +                final_name = get_domain_name(uri);                  break;              default: @@ -174,6 +188,12 @@ public class ActionRegistry : GLib.Object {      /////////////////////////////////////////////////////////////////////      public static Action? new_for_desktop_file(string file_name) { +        // check whether its a desktop file to open one of Gnome-Pie's pies +        if (file_name.has_prefix(Paths.launchers)) { +            string id = file_name.substring((long)file_name.length - 11, 3); +            return new PieAction(id); +        } +                  var info = new DesktopAppInfo.from_filename(file_name);          return new_for_app_info(info);      } @@ -192,9 +212,23 @@ public class ActionRegistry : GLib.Object {      /////////////////////////////////////////////////////////////////////      public static Action? default_for_uri(string uri) { -        var info = AppInfo. get_default_for_uri_scheme(uri); +        var info = AppInfo.get_default_for_uri_scheme(uri);          return new_for_app_info(info);      } +     +    ///////////////////////////////////////////////////////////////////// +    /// Returns for example www.google.com when http://www.google.de/?q=h +    /// is given. +    ///////////////////////////////////////////////////////////////////// +     +    private static string get_domain_name(string url) { +        int domain_end = url.index_of_char('/', 7); +        int domain_begin = url.index_of_char('/', 0) + 2; +         +        if (domain_begin < domain_end) return url.substring(domain_begin, domain_end-domain_begin); +         +        return url; +    }  }  } diff --git a/src/actions/appAction.vala b/src/actions/appAction.vala index d8363e4..2371f7c 100644 --- a/src/actions/appAction.vala +++ b/src/actions/appAction.vala @@ -29,10 +29,14 @@ public class AppAction : Action {      /// used in the pies.conf file for this kind of Actions.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out bool icon_name_editable, out string settings_name) { -        name = _("Launch application"); -        icon_name_editable = true; -        settings_name = "app"; +    public static ActionRegistry.TypeDescription register() { +        var description = new ActionRegistry.TypeDescription(); +        description.name = _("Launch application"); +        description.icon = "application-x-executable"; +        description.description = _("Executes the given command."); +        description.icon_name_editable = true; +        description.id = "app"; +        return description;      }      ///////////////////////////////////////////////////////////////////// @@ -51,8 +55,8 @@ public class AppAction : Action {      /// C'tor, initializes all members.      ///////////////////////////////////////////////////////////////////// -    public AppAction(string name, string icon, string command, bool is_quick_action = false) { -        GLib.Object(name : name, icon : icon, real_command : command, is_quick_action : is_quick_action); +    public AppAction(string name, string icon, string command, bool is_quickaction = false) { +        GLib.Object(name : name, icon : icon, real_command : command, is_quickaction : is_quickaction);      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actions/keyAction.vala b/src/actions/keyAction.vala index ddeebb5..3816686 100644 --- a/src/actions/keyAction.vala +++ b/src/actions/keyAction.vala @@ -29,10 +29,14 @@ public class KeyAction : Action {      /// used in the pies.conf file for this kind of Actions.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out bool icon_name_editable, out string settings_name) { -        name = _("Press hotkey"); -        icon_name_editable = true; -        settings_name = "key"; +    public static ActionRegistry.TypeDescription register() { +        var description = new ActionRegistry.TypeDescription(); +        description.name = _("Press hotkey"); +        description.icon = "preferences-desktop-keyboard-shortcuts"; +        description.description = _("Simulates the activation of a hotkey."); +        description.icon_name_editable = true; +        description.id = "key"; +        return description;      }         ///////////////////////////////////////////////////////////////////// @@ -57,12 +61,12 @@ public class KeyAction : Action {      /// C'tor, initializes all members.      ///////////////////////////////////////////////////////////////////// -    public KeyAction(string name, string icon, string command, bool is_quick_action = false) { -        GLib.Object(name : name, icon : icon, real_command : command, is_quick_action : is_quick_action); +    public KeyAction(string name, string icon, string command, bool is_quickaction = false) { +        GLib.Object(name : name, icon : icon, real_command : command, is_quickaction : is_quickaction);      }      construct { -        this.key = new Key(real_command); +        this.key = new Key.from_string(real_command);      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actions/pieAction.vala b/src/actions/pieAction.vala index 53ea919..5b2c81d 100644 --- a/src/actions/pieAction.vala +++ b/src/actions/pieAction.vala @@ -29,10 +29,14 @@ public class PieAction : Action {      /// used in the pies.conf file for this kind of Actions.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out bool icon_name_editable, out string settings_name) { -        name = _("Open Pie"); -        icon_name_editable = false; -        settings_name = "pie"; +    public static ActionRegistry.TypeDescription register() { +        var description = new ActionRegistry.TypeDescription(); +        description.name = _("Open Pie"); +        description.icon = "gnome-pie"; +        description.description = _("Opens another Pie of Gnome-Pie. You may create sub menus this way."); +        description.icon_name_editable = false; +        description.id = "pie"; +        return description;      }      ///////////////////////////////////////////////////////////////////// @@ -79,8 +83,8 @@ public class PieAction : Action {      /// C'tor, initializes all members.      ///////////////////////////////////////////////////////////////////// -    public PieAction(string id, bool is_quick_action = false) { -        GLib.Object(name : "", icon : "", real_command : id, is_quick_action : is_quick_action); +    public PieAction(string id, bool is_quickaction = false) { +        GLib.Object(name : "", icon : "", real_command : id, is_quickaction : is_quickaction);      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actions/sigAction.vala b/src/actions/sigAction.vala index cec9836..1edbc08 100644 --- a/src/actions/sigAction.vala +++ b/src/actions/sigAction.vala @@ -47,8 +47,8 @@ public class SigAction : Action {      /// C'tor, initializes all members.      ///////////////////////////////////////////////////////////////////// -    public SigAction(string name, string icon, string command, bool is_quick_action = false) { -        GLib.Object(name : name, icon : icon, real_command : command, is_quick_action : is_quick_action); +    public SigAction(string name, string icon, string command, bool is_quickaction = false) { +        GLib.Object(name : name, icon : icon, real_command : command, is_quickaction : is_quickaction);      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/actions/uriAction.vala b/src/actions/uriAction.vala index 25d5c75..f407f6c 100644 --- a/src/actions/uriAction.vala +++ b/src/actions/uriAction.vala @@ -29,10 +29,14 @@ public class UriAction : Action {      /// used in the pies.conf file for this kind of Actions.      ///////////////////////////////////////////////////////////////////// -    public static void register(out string name, out bool icon_name_editable, out string settings_name) { -        name = _("Open URI"); -        icon_name_editable = true; -        settings_name = "uri"; +    public static ActionRegistry.TypeDescription register() { +        var description = new ActionRegistry.TypeDescription(); +        description.name = _("Open URI"); +        description.icon = "web-browser"; +        description.description = _("Opens a given location. You may use URL's or files paths."); +        description.icon_name_editable = true; +        description.id = "uri"; +        return description;      }      ///////////////////////////////////////////////////////////////////// @@ -51,8 +55,10 @@ public class UriAction : Action {      /// C'tor, initializes all members.      ///////////////////////////////////////////////////////////////////// -    public UriAction(string name, string icon, string command, bool is_quick_action = false) { -        GLib.Object(name : name, icon : icon, real_command : command, is_quick_action : is_quick_action); +    public UriAction(string name, string icon, string command, bool is_quickaction = false) { +        GLib.Object(name : name, icon : icon,  +                    real_command : command.has_prefix("www") ? "http://" + command : command,  +                    is_quickaction : is_quickaction);      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/deamon.vala b/src/deamon.vala index 0cdb4c2..cec9539 100644 --- a/src/deamon.vala +++ b/src/deamon.vala @@ -15,11 +15,6 @@ You should have received a copy of the GNU General Public License along with  this program.  If not, see <http://www.gnu.org/licenses/>.   */ -///////////////////////////////////////////////////////////////////// -/// TODO-List (need comments): -/// PieList -///////////////////////////////////////////////////////////////////// -  namespace GnomePie {  /////////////////////////////////////////////////////////////////////////     @@ -36,6 +31,7 @@ public class Deamon : GLib.Object {      public static int main(string[] args) {          Logger.init(); +        Gdk.threads_init();          Gtk.init(ref args);          Paths.init(); @@ -100,7 +96,11 @@ public class Deamon : GLib.Object {          // create unique application          var app = new Unique.App("org.gnome.gnomepie", null); -        if (app.is_running) { +        #if HAVE_GTK_3 +            if (app.is_running()) { +        #else +            if (app.is_running) { +        #endif              // inform the running instance of the pie to be opened              if (open_pie != null) {              	message("Gnome-Pie is already running. Sending request to open pie " + open_pie + "."); @@ -120,27 +120,27 @@ public class Deamon : GLib.Object {              if (cmd == Unique.Command.ACTIVATE) {                  var pie = data.get_text(); -                if (pie != "") PieManager.open_pie(pie); -                else           this.indicator.show_preferences(); +                if (pie != null && pie != "") PieManager.open_pie(pie); +                else                          this.indicator.show_preferences();                  return Unique.Response.OK;              }              return Unique.Response.PASSTHROUGH;          }); -     +         +        // init locale support +        Intl.bindtextdomain ("gnomepie", Paths.locales); +        Intl.textdomain ("gnomepie"); +                  // init toolkits and static stuff -        Gdk.threads_init();          ActionRegistry.init();          GroupRegistry.init(); +                  PieManager.init();          Icon.init();          ThemedIcon.init();          RenderedText.init(); -     -        // init locale support -        Intl.bindtextdomain ("gnomepie", Paths.locales); -        Intl.textdomain ("gnomepie");          // launch the indicator          this.indicator = new Indicator(); diff --git a/src/gui/about.vala b/src/gui/aboutWindow.vala index ce4256e..ccd956a 100644 --- a/src/gui/about.vala +++ b/src/gui/aboutWindow.vala @@ -18,12 +18,17 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  namespace GnomePie {  /////////////////////////////////////////////////////////////////////////     -/// A simple about Dialog. +/// A simple about dialog.  ///////////////////////////////////////////////////////////////////////// -public class GnomePieAboutDialog: Gtk.AboutDialog { - -    public GnomePieAboutDialog () { +public class AboutWindow: Gtk.AboutDialog { +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, creates a new about dialog. The entries are sorted alpha- +    /// betically. +    ///////////////////////////////////////////////////////////////////// +     +    public AboutWindow () {      	string[] devs = {  			"Simon Schneegans <code@simonschneegans.de>",               "Francesco Piccinno <stack.box@gmail.com>" @@ -32,11 +37,13 @@ public class GnomePieAboutDialog: Gtk.AboutDialog {  			"Simon Schneegans <code@simonschneegans.de>"          };      	string[] translators = { -    		"DE\t\t Simon Schneegans <code@simonschneegans.de>", -    		"IT\t\t Riccardo Traverso <gr3yfox.fw@gmail.com>", -    		"PT-BR\t Magnun Leno <magnun@codecommunity.org>", -    		"EN\t\t Simon Schneegans <code@simonschneegans.de>", -    		"KO\t\t Kim Boram <Boramism@gmail.com>" +    		"Simon Schneegans <code@simonschneegans.de> (DE, EN)", +    		"Riccardo Traverso <gr3yfox.fw@gmail.com> (IT)", +    		"Magnun Leno <magnun@codecommunity.org> (PT-BR)", +    		"Kim Boram <Boramism@gmail.com> (KO)", +            "Eduardo Anabalon <lalo1412@gmail.com> (ES)", +            "Gregoire Bellon-Gervais <greggbg@gmail.com> (FR)", +            "Eugene Roskin <pams@imail.ru> (RU)"      	};      	// sort translators @@ -56,12 +63,12 @@ public class GnomePieAboutDialog: Gtk.AboutDialog {              artists : artists,              authors : devs,              translator_credits : translator_string, -            copyright : "Copyright (C) 2011 Simon Schneegans <code@simonschneegans.de>", +            copyright : "Copyright (C) 2011-2012 Simon Schneegans <code@simonschneegans.de>",              program_name: "Gnome-Pie",              logo_icon_name: "gnome-pie",              website: "http://www.simonschneegans.de/?page_id=12",              website_label: "www.gnome-pie.simonschneegans.de", -            version: "0.3.1" +            version: "0.4.0"          );      }  } diff --git a/src/gui/cellRendererIcon.vala b/src/gui/cellRendererIcon.vala deleted file mode 100644 index 959a0b7..0000000 --- a/src/gui/cellRendererIcon.vala +++ /dev/null @@ -1,132 +0,0 @@ -/*  -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 cellrenderer which displays an Icon. When clicked onto, a window -/// opens for selecting another icon. This needs to be a subclass of -/// Gtk.CellRendererText because Gtk.CellRendererPixbuf can't receive -/// click events. Internally it stores a Gtk.CellRendererPixbuf -/// which renders and stuff. -///////////////////////////////////////////////////////////////////////// - -public class CellRendererIcon : Gtk.CellRendererText { -     -    ///////////////////////////////////////////////////////////////////// -    /// This signal is emitted when the user selects another icon. -    ///////////////////////////////////////////////////////////////////// -     -    public signal void on_select(string path, string icon); -     -    ///////////////////////////////////////////////////////////////////// -    /// The IconSelectWindow which is shown on click. -    ///////////////////////////////////////////////////////////////////// - -    private IconSelectWindow select_window = null; -     -    ///////////////////////////////////////////////////////////////////// -    /// The internal Renderer used for drawing. -    ///////////////////////////////////////////////////////////////////// -     -    private Gtk.CellRendererPixbuf renderer = null; -     -    ///////////////////////////////////////////////////////////////////// -    /// A helper variable, needed to emit the current path. -    ///////////////////////////////////////////////////////////////////// -     -    private string current_path = ""; -     -    public string icon_name { get; set; } - -    ///////////////////////////////////////////////////////////////////// -    /// Forward some parts of the CellRendererPixbuf's interface. -    ///////////////////////////////////////////////////////////////////// -     -    public bool follow_state { -        get { return renderer.follow_state; } -        set { renderer.follow_state = value; } -    } -     -    public bool icon_sensitive { -        get { return renderer.sensitive; } -        set { renderer.sensitive = value; } -    } -     -    public Gdk.Pixbuf pixbuf { -        owned get { return renderer.pixbuf; } -        set { renderer.pixbuf = value; } -    } - -    ///////////////////////////////////////////////////////////////////// -    /// C'tor, creates a new CellRendererIcon. -    ///////////////////////////////////////////////////////////////////// -     -    public CellRendererIcon() { -        this.select_window = new IconSelectWindow();   -        this.renderer = new Gtk.CellRendererPixbuf(); -     -        this.select_window.on_select.connect((icon) => { -            this.on_select(current_path, icon); -        }); -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Forward some parts of the CellRendererPixbuf's interface. -    ///////////////////////////////////////////////////////////////////// -     -    public override void get_size (Gtk.Widget widget, Gdk.Rectangle? cell_area, -                               out int x_offset, out int y_offset, -                               out int width, out int height) { - -        this.renderer.get_size(widget, cell_area, out x_offset, out y_offset, out width, out height); -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Forward some parts of the CellRendererPixbuf's interface. -    ///////////////////////////////////////////////////////////////////// -     -    public override void render (Gdk.Window window, Gtk.Widget widget, -                             Gdk.Rectangle bg_area, -                             Gdk.Rectangle cell_area, -                             Gdk.Rectangle expose_area, -                             Gtk.CellRendererState flags) { -                              -        this.renderer.render(window, widget, bg_area, cell_area, expose_area, flags); -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Open the IconSelectWindow on click. -    ///////////////////////////////////////////////////////////////////// -     -    public override unowned Gtk.CellEditable start_editing( -        Gdk.Event event, Gtk.Widget widget, string path, Gdk.Rectangle bg_area,  -        Gdk.Rectangle cell_area, Gtk.CellRendererState flags) { -         -        this.select_window.set_transient_for((Gtk.Window)widget.get_toplevel()); -        this.select_window.set_modal(true); -         -        this.current_path = path; -        this.select_window.show(); -        this.select_window.active_icon = this.icon_name; -             -        return this.renderer.start_editing(event, widget, path, bg_area, cell_area, flags); -    } -} - -} - diff --git a/src/gui/cellRendererTrigger.vala b/src/gui/cellRendererTrigger.vala deleted file mode 100644 index a825c32..0000000 --- a/src/gui/cellRendererTrigger.vala +++ /dev/null @@ -1,84 +0,0 @@ -/*  -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 CellRenderer which opens a TriggerSelectWindow. -///////////////////////////////////////////////////////////////////////// - -public class CellRendererTrigger : Gtk.CellRendererText { -     -    ///////////////////////////////////////////////////////////////////// -    /// This signal is emitted when the user selects another trigger. -    ///////////////////////////////////////////////////////////////////// -     -    public signal void on_select(string path, Trigger trigger); -     -    ///////////////////////////////////////////////////////////////////// -    /// The trigger which can be set with this window. -    ///////////////////////////////////////////////////////////////////// -     -    public string trigger { get; set; } -     -    ///////////////////////////////////////////////////////////////////// -    /// The IconSelectWindow which is shown on click. -    ///////////////////////////////////////////////////////////////////// - -    private TriggerSelectWindow select_window = null; -     -    ///////////////////////////////////////////////////////////////////// -    /// A helper variable, needed to emit the current path. -    ///////////////////////////////////////////////////////////////////// -     -    private string current_path = ""; -     -    ///////////////////////////////////////////////////////////////////// -    /// C'tor, creates a new CellRendererIcon. -    ///////////////////////////////////////////////////////////////////// -     -    public CellRendererTrigger() { -        this.select_window = new TriggerSelectWindow();   -     -        this.select_window.on_select.connect((trigger) => { -            this.trigger = trigger.name; -            this.on_select(current_path, trigger); -        }); -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Open the TriggerSelectWindow on click. -    ///////////////////////////////////////////////////////////////////// -     -    public override unowned Gtk.CellEditable start_editing( -        Gdk.Event event, Gtk.Widget widget, string path, Gdk.Rectangle bg_area,  -        Gdk.Rectangle cell_area, Gtk.CellRendererState flags) { -         -        this.current_path = path; -         -        this.select_window.set_transient_for((Gtk.Window)widget.get_toplevel()); -        this.select_window.set_modal(true); -        this.select_window.set_trigger(new Trigger.from_string(this.trigger)); -                   -        this.select_window.show(); -             -        return base.start_editing(event, widget, path, bg_area, cell_area, flags); -    } -} - -} - diff --git a/src/gui/iconSelectWindow.vala b/src/gui/iconSelectWindow.vala index 01a4a40..d66c654 100644 --- a/src/gui/iconSelectWindow.vala +++ b/src/gui/iconSelectWindow.vala @@ -23,45 +23,19 @@ namespace GnomePie {  /// happens in an extra thread and a spinner is displayed while loading.  ///////////////////////////////////////////////////////////////////////// -public class IconSelectWindow : Gtk.Dialog { - +public class IconSelectWindow : GLib.Object { +         ///////////////////////////////////////////////////////////////////// -    /// The currently selected icon. If set, this icon gets focused. +    /// This signal gets emitted when the user selects a new icon.      ///////////////////////////////////////////////////////////////////// -    private string _active_icon = "application-default-icon"; -     -    public string active_icon { -        get { -            return _active_icon; -        } -        set { -            if (value.contains("/")) { -                this.file_chooser.set_filename(value); -                this.tabs.set_current_page(1); -            } else { -                this.icon_list_filtered.foreach((model, path, iter) => { -                    string name = ""; -                    model.get(iter, 0, out name); -                     -                    if (name == value) { -                        this.icon_view.select_path(path); -                        this.icon_view.scroll_to_path(path, true, 0.5f, 0.0f); -                        this.icon_view.set_cursor(path, null, false); -                    } -                    return (name == value); -                }); -                 -                this.tabs.set_current_page(0); -            } -        } -    } +    public signal void on_ok(string icon_name);      ///////////////////////////////////////////////////////////////////// -    /// This signal gets emitted when the user selects a new icon. +    /// Stores the currently selected icon.      ///////////////////////////////////////////////////////////////////// -    public signal void on_select(string icon_name); +    private string active_icon = "";      /////////////////////////////////////////////////////////////////////      /// The ListStore storing all theme-icons. @@ -121,6 +95,12 @@ public class IconSelectWindow : Gtk.Dialog {      private Gtk.Notebook tabs = null;      ///////////////////////////////////////////////////////////////////// +    /// The main window. +    ///////////////////////////////////////////////////////////////////// +     +    private Gtk.Window window = null; +     +    /////////////////////////////////////////////////////////////////////      /// A little structure containing data for one icon in the icon_view.      ///////////////////////////////////////////////////////////////////// @@ -156,217 +136,221 @@ public class IconSelectWindow : Gtk.Dialog {      /// C'tor, creates a new IconSelectWindow.      ///////////////////////////////////////////////////////////////////// -    public IconSelectWindow() { -        this.title = _("Choose an Icon"); -        this.set_size_request(520, 520); -        this.delete_event.connect(hide_on_delete); -        this.load_queue = new GLib.AsyncQueue<ListEntry?>(); +    public IconSelectWindow(Gtk.Window parent) { +        try { +            this.load_queue = new GLib.AsyncQueue<ListEntry?>(); -        if (this.icon_list == null) { -            this.icon_list = new Gtk.ListStore(3, typeof(string),      // icon name -                                                  typeof(IconContext), // icon type -                                                  typeof(Gdk.Pixbuf)); // the icon itself -                                                   -            // disable sorting until all icons are loaded -            // else loading becomes horribly slow                                     -            this.icon_list.set_default_sort_func(() => {return 0;}); +            if (this.icon_list == null) { +                this.icon_list = new Gtk.ListStore(3, typeof(string), // icon name +                                                      typeof(IconContext), // icon type +                                                      typeof(Gdk.Pixbuf)); // the icon itself +                                                       +                // disable sorting until all icons are loaded +                // else loading becomes horribly slow +                this.icon_list.set_default_sort_func(() => {return 0;}); -            // reload if icon theme changes -            Gtk.IconTheme.get_default().changed.connect(() => { -                if (this.visible) load_icons(); -                else              need_reload = true; -            }); -        }  -         -        // make the icon_view filterable -        this.icon_list_filtered = new Gtk.TreeModelFilter(this.icon_list, null); - -        var container = new Gtk.VBox(false, 12); -            container.set_border_width(12); +                // reload if icon theme changes +                Gtk.IconTheme.get_default().changed.connect(() => { +                    if (this.window.visible) load_icons(); +                    else need_reload = true; +                }); +            } +             +            // make the icon_view filterable +            this.icon_list_filtered = new Gtk.TreeModelFilter(this.icon_list, null); +                 +            Gtk.Builder builder = new Gtk.Builder(); -            // tab container -            this.tabs = new Gtk.Notebook(); +            builder.add_from_file (Paths.ui_files + "/icon_select.ui"); -                // icon theme tab -                var theme_tab = new Gtk.VBox(false, 12); -                    theme_tab.set_border_width(12); +            this.window = builder.get_object("window") as Gtk.Window; +            this.window.set_transient_for(parent); +            this.window.set_modal(true); +             +            this.tabs = builder.get_object("tabs") as Gtk.Notebook; +             +            this.spinner = builder.get_object("spinner") as Gtk.Spinner; +            this.spinner.start(); -                    // type chooser combo-box -                    var context_combo = new Gtk.ComboBox.text(); -                        context_combo.append_text(_("All icons")); -                        context_combo.append_text(_("Applications")); -                        context_combo.append_text(_("Actions")); -                        context_combo.append_text(_("Places")); -                        context_combo.append_text(_("File types")); -                        context_combo.append_text(_("Emotes")); -                        context_combo.append_text(_("Miscellaneous")); +            (builder.get_object("ok-button") as Gtk.Button).clicked.connect(on_ok_button_clicked); +            (builder.get_object("cancel-button") as Gtk.Button).clicked.connect(on_cancel_button_clicked); +             +            var combo_box = builder.get_object("combo-box") as Gtk.VBox; +             +            // context combo +            #if HAVE_GTK_3 +                var context_combo = new Gtk.ComboBoxText(); +            #else +                var context_combo = new Gtk.ComboBox.text(); +            #endif +                context_combo.append_text(_("All icons")); +                context_combo.append_text(_("Applications")); +                context_combo.append_text(_("Actions")); +                context_combo.append_text(_("Places")); +                context_combo.append_text(_("File types")); +                context_combo.append_text(_("Emotes")); +                context_combo.append_text(_("Miscellaneous")); -                        context_combo.set_active(0); -                         -                        context_combo.changed.connect(() => { -                            this.icon_list_filtered.refilter(); -                        }); -                         -                        theme_tab.pack_start(context_combo, false, false); +                context_combo.set_active(0); +                 +                context_combo.changed.connect(() => { +                    this.icon_list_filtered.refilter(); +                }); +                 +            combo_box.pack_start(context_combo, false, false); +                 +            // string filter entry +            var filter = builder.get_object("filter-entry") as Gtk.Entry; +                 +                // only display items which have the selected type +                // and whose name contains the text entered in the entry +                this.icon_list_filtered.set_visible_func((model, iter) => { +                    string name = ""; +                    IconContext context = IconContext.ALL; +                    model.get(iter, 0, out name); +                    model.get(iter, 1, out context); -                    // string filter entry -                    var filter = new Gtk.Entry(); -                        filter.primary_icon_stock = Gtk.Stock.FIND; -                        filter.primary_icon_activatable = false; -                        filter.secondary_icon_stock = Gtk.Stock.CLEAR; -                        theme_tab.pack_start(filter, false, false); -                         -                        // only display items which have the selected type -                        // and whose name contains the text entered in the entry -                        this.icon_list_filtered.set_visible_func((model, iter) => { -                            string name = ""; -                            IconContext context = IconContext.ALL; -                            model.get(iter, 0, out name); -                            model.get(iter, 1, out context); -                             -                            if (name == null) return false; -                             -                            return (context_combo.get_active() == context ||  -                                    context_combo.get_active() == IconContext.ALL) &&  -                                    name.down().contains(filter.text.down()); -                        }); -                         -                        // clear when the users clicks on the "clear" icon -                        filter.icon_release.connect((pos, event) => { -                            if (pos == Gtk.EntryIconPosition.SECONDARY) -                                filter.text = ""; -                        }); -                         -                        // refilter on input -                        filter.notify["text"].connect(() => { -                            this.icon_list_filtered.refilter(); -                        }); +                    if (name == null) return false; -                    // container for the icon_view -                    var scroll = new Gtk.ScrolledWindow (null, null); -                        scroll.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); -                        scroll.set_shadow_type (Gtk.ShadowType.IN); +                    return (context_combo.get_active() == context || +                            context_combo.get_active() == IconContext.ALL) && +                            name.down().contains(filter.text.down()); +                }); +                 +                // clear when the users clicks on the "clear" icon +                filter.icon_release.connect((pos, event) => { +                    if (pos == Gtk.EntryIconPosition.SECONDARY) +                        filter.text = ""; +                }); +                 +                // refilter on input +                filter.notify["text"].connect(() => { +                    this.icon_list_filtered.refilter(); +                }); +             +            // container for the icon_view +            var scroll = builder.get_object("icon-scrolledwindow") as Gtk.ScrolledWindow; -                        // displays the filtered icons -                        this.icon_view = new Gtk.IconView.with_model(this.icon_list_filtered); -                            this.icon_view.item_width = 32; -                            this.icon_view.item_padding = 3; -                            this.icon_view.pixbuf_column = 2; -                            this.icon_view.tooltip_column = 0; -                             -                            // set _active_icon if selection changes -                            this.icon_view.selection_changed.connect(() => { -                                foreach (var path in this.icon_view.get_selected_items()) { -                                    Gtk.TreeIter iter; -                                    this.icon_list_filtered.get_iter(out iter, path); -                                    this.icon_list_filtered.get(iter, 0, out this._active_icon); -                                } -                            }); -                             -                            // hide this window when the user activates an icon -                            this.icon_view.item_activated.connect((path) => { -                                Gtk.TreeIter iter; -                                this.icon_list_filtered.get_iter(out iter, path); -                                this.icon_list_filtered.get(iter, 0, out this._active_icon); -                                this.on_select(this._active_icon); -                                this.hide(); -                            }); +                // displays the filtered icons +                this.icon_view = new Gtk.IconView.with_model(this.icon_list_filtered); +                    this.icon_view.item_width = 32; +                    this.icon_view.item_padding = 3; +                    this.icon_view.pixbuf_column = 2; +                    this.icon_view.tooltip_column = 0; -                        scroll.add(this.icon_view); +                    // set active_icon if selection changes +                    this.icon_view.selection_changed.connect(() => { +                        foreach (var path in this.icon_view.get_selected_items()) { +                            Gtk.TreeIter iter; +                            this.icon_list_filtered.get_iter(out iter, path); +                            this.icon_list_filtered.get(iter, 0, out this.active_icon); +                        } +                    }); -                        theme_tab.pack_start(scroll, true, true); -                         -                    tabs.append_page(theme_tab, new Gtk.Label(_("Icon Theme"))); +                    // hide this window when the user activates an icon +                    this.icon_view.item_activated.connect((path) => { +                        Gtk.TreeIter iter; +                        this.icon_list_filtered.get_iter(out iter, path); +                        this.icon_list_filtered.get(iter, 0, out this.active_icon); +                        this.on_ok(this.active_icon); +                        this.window.hide(); +                    }); +             +                scroll.add(this.icon_view); -                // tab containing the possibility to choose a custom icon -                var custom_tab = new Gtk.VBox(false, 6); -                    custom_tab.border_width = 12; +            // file chooser widget +            this.file_chooser = builder.get_object("filechooser") as Gtk.FileChooserWidget; +                var file_filter = new Gtk.FileFilter(); +                    file_filter.add_pixbuf_formats(); -                    // file chooser widget -                    this.file_chooser = new Gtk.FileChooserWidget(Gtk.FileChooserAction.OPEN); -                        var file_filter = new Gtk.FileFilter(); -                        file_filter.add_pixbuf_formats(); +                    #if HAVE_GTK_3 +                        file_filter.set_filter_name(_("All supported image formats")); +                    #else                          file_filter.set_name(_("All supported image formats")); -                        file_chooser.add_filter(file_filter); -                         -                        // set _active_icon if the user selected a file -                        file_chooser.selection_changed.connect(() => { -                            if (file_chooser.get_filename() != null &&  -                                GLib.FileUtils.test(file_chooser.get_filename(),  -                                                    GLib.FileTest.IS_REGULAR)) -                                 -                                this._active_icon = file_chooser.get_filename(); -                        }); -                         -                        // hide this window when the user activates a file -                        file_chooser.file_activated.connect(() => { -                            this._active_icon = file_chooser.get_filename(); -                            this.on_select(this._active_icon); -                            this.hide(); -                        }); -                     -                     -                    custom_tab.pack_start(file_chooser, true, true); -                     -                tabs.append_page(custom_tab, new Gtk.Label(_("Custom Icon"))); -                     -            container.pack_start(tabs, true, true); - -            // button box --- this dialog has a custom button box at the bottom because it -            // should have a spinner there. Sadly that's impossible with the "normal" -            // action_area of Gtk.Dialog's  -            var bottom_box = new Gtk.HBox(false, 0); -             -                var bbox = new Gtk.HButtonBox(); -                    bbox.set_spacing(6); -                    bbox.set_layout(Gtk.ButtonBoxStyle.END); -                     -                    var cancel_button = new Gtk.Button.from_stock(Gtk.Stock.CANCEL); -                        cancel_button.clicked.connect(() => {  -                            this.hide(); -                        }); -                        bbox.pack_start(cancel_button); -                         -                    var ok_button = new Gtk.Button.from_stock(Gtk.Stock.OK); -                        ok_button.clicked.connect(() => {  -                            this.on_select(this._active_icon); -                            this.hide(); -                        }); -                        bbox.pack_start(ok_button); -                         -                    bottom_box.pack_end(bbox, false); +                    #endif -                    this.spinner = new Gtk.Spinner(); -                        this.spinner.set_size_request(16, 16); -                        this.spinner.start(); +                    file_chooser.add_filter(file_filter); +                 +                // set active_icon if the user selected a file +                file_chooser.selection_changed.connect(() => { +                    if (file_chooser.get_filename() != null && +                        GLib.FileUtils.test(file_chooser.get_filename(), +                                            GLib.FileTest.IS_REGULAR)) -                        bottom_box.pack_start(this.spinner, false, false); +                        this.active_icon = file_chooser.get_filename(); +                }); +                 +                // hide this window when the user activates a file +                file_chooser.file_activated.connect(() => { +                    this.active_icon = file_chooser.get_filename(); +                    this.on_ok(this.active_icon); +                    this.window.hide(); +                }); -            container.pack_start(bottom_box, false, false); -           -        this.vbox.pack_start(container, true, true); - -        this.vbox.show_all(); - -        this.set_focus(this.icon_view); +            this.window.set_focus(this.icon_view); +            this.window.delete_event.connect(this.window.hide_on_delete); +             +        } catch (GLib.Error e) { +            error("Could not load UI: %s\n", e.message); +        }      }      ///////////////////////////////////////////////////////////////////// -    /// Hide the "normal" action_area when this window is shown. Reload -    /// all icons if necessary. +    /// Displays the window. The icons are reloaded if neccessary.      ///////////////////////////////////////////////////////////////////// -     -    public override void show() { -        base.show(); -         -        // hide the "normal" action_area --- this Dialog has a custom set of -        // buttons containg the spinner -        this.action_area.hide(); + +    public void show() { +        this.window.show_all(); +        this.spinner.hide();          if (this.need_reload) {              this.need_reload = false;              this.load_icons();          } +    }  +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes the window select the icon of the given Pie. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_pie(string id) { +        string icon = PieManager.all_pies[id].icon; +     +        if (icon.contains("/")) { +            this.file_chooser.set_filename(icon); +            this.tabs.set_current_page(1); +        } else { +            this.icon_list_filtered.foreach((model, path, iter) => { +                string name = ""; +                model.get(iter, 0, out name); +                 +                if (name == icon) { +                    this.icon_view.select_path(path); +                    this.icon_view.scroll_to_path(path, true, 0.5f, 0.0f); +                    this.icon_view.set_cursor(path, null, false); +                } +                return (name == icon); +            }); +             +            this.tabs.set_current_page(0); +        } +    }  +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user clicks the ok button. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_ok_button_clicked() { +        this.on_ok(this.active_icon); +        this.window.hide(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user clicks the cancel button. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_cancel_button_clicked() { +        this.window.hide();      }      ///////////////////////////////////////////////////////////////////// @@ -400,13 +384,13 @@ public class IconSelectWindow : Gtk.Dialog {                      var new_entry = this.load_queue.pop();                      Gtk.TreeIter current;                      this.icon_list.append(out current); -                    this.icon_list.set(current, 0, new_entry.name,  +                    this.icon_list.set(current, 0, new_entry.name,                                                  1, new_entry.context,                                                  2, new_entry.pixbuf);                  }                  // enable sorting of the icon_view if loading finished -                if (!this.loading) this.icon_list.set_sort_column_id(0, Gtk.SortType.ASCENDING);   +                if (!this.loading) this.icon_list.set_sort_column_id(0, Gtk.SortType.ASCENDING);                  return loading;              }); @@ -439,12 +423,12 @@ public class IconSelectWindow : Gtk.Dialog {                          default: break;                      } -                    try {       +                    try {                          // create a new entry for the queue                          var new_entry = new ListEntry();                          new_entry.name = icon;                          new_entry.context = icon_context; -                        new_entry.pixbuf = icon_theme.load_icon(icon, 32, 0);  +                        new_entry.pixbuf = icon_theme.load_icon(icon, 32, 0);                          // some icons have only weird sizes... do not include them                          if (new_entry.pixbuf.width == 32) @@ -462,7 +446,7 @@ public class IconSelectWindow : Gtk.Dialog {          // hide the spinner          if (spinner != null) -            spinner.visible = this.loading; +            spinner.visible = false;          return null;      } diff --git a/src/gui/indicator.vala b/src/gui/indicator.vala index 8033cb7..dea4d3c 100644 --- a/src/gui/indicator.vala +++ b/src/gui/indicator.vala @@ -38,7 +38,7 @@ public class Indicator : GLib.Object {      /// The Preferences Menu of Gnome-Pie.      ///////////////////////////////////////////////////////////////////// -    private Preferences prefs { private get; private set; } +    private PreferencesWindow prefs { private get; private set; }      /////////////////////////////////////////////////////////////////////      /// Returns true, when the indicator is currently visible. @@ -73,7 +73,7 @@ public class Indicator : GLib.Object {              string icon = "indicator-applet";              try {                  path = GLib.Path.get_dirname(GLib.FileUtils.read_link("/proc/self/exe"))+"/resources"; -                icon = "gnome-pie-indicator"; +                icon = "gnome-pie";              } catch (GLib.FileError e) {                  warning("Failed to get path of executable!");              } @@ -86,23 +86,23 @@ public class Indicator : GLib.Object {              try {                  var file = GLib.File.new_for_path(GLib.Path.build_filename(                      GLib.Path.get_dirname(GLib.FileUtils.read_link("/proc/self/exe"))+"/resources", -                    "gnome-pie-indicator.svg" +                    "gnome-pie.svg"                  ));                  if (!file.query_exists()) -                  this.indicator.set_from_icon_name("gnome-pie-indicator"); +                  this.indicator.set_from_icon_name("gnome-pie");                  else                    this.indicator.set_from_file(file.get_path());              } catch (GLib.FileError e) {                  warning("Failed to get path of executable!"); -                this.indicator.set_from_icon_name("gnome-pie-indicator"); +                this.indicator.set_from_icon_name("gnome-pie");              }              this.menu = new Gtk.Menu();              var menu = this.menu;          #endif -        this.prefs = new Preferences(); +        this.prefs = new PreferencesWindow();          // preferences item          var item = new Gtk.ImageMenuItem.from_stock (Gtk.Stock.PREFERENCES, null); @@ -117,7 +117,7 @@ public class Indicator : GLib.Object {          item = new Gtk.ImageMenuItem.from_stock (Gtk.Stock.ABOUT, null);          item.show();          item.activate.connect(() => { -            var about = new GnomePieAboutDialog(); +            var about = new AboutWindow();              about.run();              about.destroy();          }); diff --git a/src/gui/newSliceWindow.vala b/src/gui/newSliceWindow.vala new file mode 100644 index 0000000..4e38376 --- /dev/null +++ b/src/gui/newSliceWindow.vala @@ -0,0 +1,394 @@ +/*  +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 window which allows selection of a new Slice which is about to be +/// added to a Pie. It can be also used to edit an existing Slice +///////////////////////////////////////////////////////////////////////// + +public class NewSliceWindow : GLib.Object { + +    ///////////////////////////////////////////////////////////////////// +    /// This signal gets emitted when the user confirms his selection. +    ///////////////////////////////////////////////////////////////////// + +    public signal void on_select(ActionGroup action, bool as_new_slice, int at_position);  + +    ///////////////////////////////////////////////////////////////////// +    /// The contained list of slice types. It contains both: Groups and +    /// single actions. +    ///////////////////////////////////////////////////////////////////// + +    private SliceTypeList slice_type_list = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// The IconSelectWindow used for icon selection for a Slice. +    ///////////////////////////////////////////////////////////////////// +     +    private IconSelectWindow? icon_window = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// Some widgets of this window. Loaded by a ui-builder and stored +    /// for later access. +    ///////////////////////////////////////////////////////////////////// +     +    private Gtk.Dialog window = null; +    private Gtk.HBox name_box = null; +    private Gtk.HBox command_box = null; +    private Gtk.Button icon_button = null; +    private Gtk.VBox no_options_box = null; +    private Gtk.HBox pie_box = null; +    private Gtk.HBox hotkey_box = null; +    private Gtk.HBox uri_box = null; +    private Gtk.HBox quickaction_box = null; +    private Gtk.Image icon = null; +    private Gtk.Entry name_entry = null; +    private Gtk.Entry command_entry = null; +    private Gtk.Entry uri_entry = null; +    private Gtk.CheckButton quickaction_checkbutton = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// Two custom widgets. For Pie and hotkey selection respectively. +    ///////////////////////////////////////////////////////////////////// +     +    private PieComboList pie_select = null; +    private TriggerSelectButton key_select = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// These members store information on the currently selected Slice. +    ///////////////////////////////////////////////////////////////////// +     +    private string current_type = ""; +    private string current_icon = ""; +    private string current_id = ""; +    private string current_custom_icon = ""; +    private string current_hotkey = ""; +    private string current_pie_to_open = ""; +     +    ///////////////////////////////////////////////////////////////////// +    /// The position of the edited Slice in its parent Pie. +    ///////////////////////////////////////////////////////////////////// +     +    private int slice_position = 0; +     +    ///////////////////////////////////////////////////////////////////// +    /// True, if the Slice i going to be added as a new Slice. Else it +    /// will edit the Slice at slice_position in its parent Pie. +    ///////////////////////////////////////////////////////////////////// +     +    private bool add_as_new_slice = true; +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor creates a new window. +    ///////////////////////////////////////////////////////////////////// +     +    public NewSliceWindow() { +        try { +         +            Gtk.Builder builder = new Gtk.Builder(); + +            builder.add_from_file (Paths.ui_files + "/slice_select.ui"); +             +            this.slice_type_list = new SliceTypeList(); +            this.slice_type_list.on_select.connect((type, icon) => { +                 +                this.name_box.hide(); +                this.command_box.hide(); +                this.icon_button.sensitive = false; +                this.no_options_box.hide(); +                this.pie_box.hide(); +                this.hotkey_box.hide(); +                this.uri_box.hide(); +                this.quickaction_box.hide(); +                 +                this.current_type = type; +                 +                switch (type) { +                    case "bookmarks": case "clipboard": case "devices": +                    case "menu": case "session": case "window_list": +                        this.no_options_box.show(); +                        this.set_icon(icon); +                        break; +                    case "app": +                        this.name_box.show(); +                        this.command_box.show(); +                        this.quickaction_box.show(); +                        this.icon_button.sensitive = true; +                        if (this.current_custom_icon == "") this.set_icon(icon); +                        else                                this.set_icon(this.current_custom_icon); +                        break; +                    case "key": +                        this.name_box.show(); +                        this.hotkey_box.show(); +                        this.quickaction_box.show(); +                        this.icon_button.sensitive = true; +                        if (this.current_custom_icon == "") this.set_icon(icon); +                        else                                this.set_icon(this.current_custom_icon); +                        break; +                    case "pie": +                        this.pie_box.show(); +                        this.quickaction_box.show(); +                        this.set_icon(PieManager.all_pies[this.pie_select.current_id].icon); +                        break; +                    case "uri": +                        this.name_box.show(); +                        this.uri_box.show(); +                        this.quickaction_box.show(); +                        this.icon_button.sensitive = true; +                        if (this.current_custom_icon == "") this.set_icon(icon); +                        else                                this.set_icon(this.current_custom_icon); +                        break; +                } +            }); +             +            this.name_box = builder.get_object("name-box") as Gtk.HBox; +            this.command_box = builder.get_object("command-box") as Gtk.HBox; +            this.icon_button = builder.get_object("icon-button") as Gtk.Button; +            this.no_options_box = builder.get_object("no-options-box") as Gtk.VBox; +            this.pie_box = builder.get_object("pie-box") as Gtk.HBox; +            this.pie_select = new PieComboList(); +            this.pie_select.on_select.connect((id) => { +                this.current_pie_to_open = id; +                this.set_icon(PieManager.all_pies[id].icon); +            }); +             +            this.pie_box.pack_start(this.pie_select, true, true); +                 +            this.hotkey_box = builder.get_object("hotkey-box") as Gtk.HBox; +            this.key_select = new TriggerSelectButton(false); +            this.hotkey_box.pack_start(this.key_select, false, true); +            this.key_select.on_select.connect((trigger) => { +                this.current_hotkey = trigger.name; +            }); +             +            this.uri_box = builder.get_object("uri-box") as Gtk.HBox; +             +            this.name_entry = builder.get_object("name-entry") as Gtk.Entry; +            this.uri_entry = builder.get_object("uri-entry") as Gtk.Entry; +            this.command_entry = builder.get_object("command-entry") as Gtk.Entry; +            this.quickaction_checkbutton = builder.get_object("quick-action-checkbutton") as Gtk.CheckButton; +             +            this.quickaction_box = builder.get_object("quickaction-box") as Gtk.HBox; +            this.icon = builder.get_object("icon") as Gtk.Image;             +             +            this.icon_button.clicked.connect(on_icon_button_clicked); +             +            var scroll_area = builder.get_object("slice-scrolledwindow") as Gtk.ScrolledWindow; +                scroll_area.add(this.slice_type_list); + +            this.window = builder.get_object("window") as Gtk.Dialog; +             +            (builder.get_object("ok-button") as Gtk.Button).clicked.connect(on_ok_button_clicked); +            (builder.get_object("cancel-button") as Gtk.Button).clicked.connect(on_cancel_button_clicked); +             +            this.window.delete_event.connect(this.window.hide_on_delete); +                 +        } catch (GLib.Error e) { +            error("Could not load UI: %s\n", e.message); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Sets the parent window, in order to make this window stay in +    /// front. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_parent(Gtk.Window parent) { +        this.window.set_transient_for(parent); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Sows the window on the screen. +    ///////////////////////////////////////////////////////////////////// +     +    public void show() { +        this.slice_type_list.select_first(); +        this.pie_select.select_first(); +        this.key_select.set_trigger(new Trigger()); +        this.window.show_all(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Reloads the window. +    ///////////////////////////////////////////////////////////////////// +     +    public void reload() { +        this.pie_select.reload(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes all widgets display stuff according to the given action. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_action(ActionGroup group, int position) { +        this.set_default(group.parent_id, position); +         +        this.add_as_new_slice = false; +        string type = ""; +         +        if (group.get_type().depth() == 2) { +            var action = group.actions[0]; +            type = ActionRegistry.descriptions[action.get_type().name()].id; +            this.select_type(type); +             +            this.set_icon(action.icon); +            this.quickaction_checkbutton.active = action.is_quickaction; +            this.name_entry.text = action.name; +             +            switch (type) { +                case "app": +                    this.current_custom_icon = action.icon; +                    this.command_entry.text = action.real_command; +                    break; +                case "key": +                    this.current_custom_icon = action.icon; +                    this.key_select.set_trigger(new Trigger.from_string(action.real_command)); +                    break; +                case "pie": +                    this.pie_select.select(action.real_command); +                    break; +                case "uri": +                    this.current_custom_icon = action.icon; +                    this.uri_entry.text = action.real_command; +                    break; +            } +             +        } else { +            type = GroupRegistry.descriptions[group.get_type().name()].id; +            this.select_type(type); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Selects a default action. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_default(string pie_id, int position) { +        this.slice_position = position; +        this.add_as_new_slice = true; +        this.current_custom_icon = ""; +        this.select_type("app"); +        this.current_id = pie_id; +        this.key_select.set_trigger(new Trigger()); +        this.pie_select.select_first(); +        this.name_entry.text = _("Rename me!"); +        this.command_entry.text = ""; +        this.uri_entry.text = ""; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Selects a specific action type. +    ///////////////////////////////////////////////////////////////////// +     +    private void select_type(string type) { +        this.current_type = type; +        this.slice_type_list.select(type); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called, when the user presses the ok button. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_ok_button_clicked() { +        this.window.hide(); +         +        ActionGroup group = null; +         +        switch (this.current_type) { +            case "bookmarks":   group = new BookmarkGroup(this.current_id);      break; +            case "clipboard":   group = new ClipboardGroup(this.current_id);     break; +            case "devices":     group = new DevicesGroup(this.current_id);       break; +            case "menu":        group = new MenuGroup(this.current_id);          break; +            case "session":     group = new SessionGroup(this.current_id);       break; +            case "window_list": group = new WindowListGroup(this.current_id);    break; + +            case "app": +                group = new ActionGroup(this.current_id); +                group.add_action(new AppAction(this.name_entry.text, this.current_icon,  +                                               this.command_entry.text,  +                                               this.quickaction_checkbutton.active)); +                break; +            case "key": +                group = new ActionGroup(this.current_id); +                group.add_action(new KeyAction(this.name_entry.text, this.current_icon,  +                                               this.current_hotkey,  +                                               this.quickaction_checkbutton.active)); +                break; +            case "pie": +                group = new ActionGroup(this.current_id); +                group.add_action(new PieAction(this.current_pie_to_open,  +                                               this.quickaction_checkbutton.active)); +                break; +            case "uri": +                group = new ActionGroup(this.current_id); +                group.add_action(new UriAction(this.name_entry.text, this.current_icon,  +                                               this.uri_entry.text,  +                                               this.quickaction_checkbutton.active)); +                break; +        } +         +        this.on_select(group, this.add_as_new_slice, this.slice_position); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user presses the cancel button. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_cancel_button_clicked() { +        this.window.hide(); +    }    +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user presses the icon select button. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_icon_button_clicked(Gtk.Button button) { +        if (icon_window == null) { +            icon_window = new IconSelectWindow(this.window); +            icon_window.on_ok.connect((icon) => { +                this.current_custom_icon = icon; +                this.set_icon(icon); +            }); +        } +         +        icon_window.show(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Helper method which sets the icon of the icon select button. +    /// It assures that both can be displayed: A customly chosen image +    /// from or an icon from the current theme. +    ///////////////////////////////////////////////////////////////////// +     +    private void set_icon(string icon) { +        if (icon.contains("/")) +            try { +                this.icon.pixbuf = new Gdk.Pixbuf.from_file_at_scale(icon, this.icon.get_pixel_size(),  +                                                                     this.icon.get_pixel_size(), true); +            } catch (GLib.Error error) { +                warning(error.message); +            } +        else +            this.icon.icon_name = icon; +             +        this.current_icon = icon; +    }  +} + +} diff --git a/src/gui/pieComboList.vala b/src/gui/pieComboList.vala new file mode 100644 index 0000000..3b54944 --- /dev/null +++ b/src/gui/pieComboList.vala @@ -0,0 +1,155 @@ +/*  +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 drop-down list, containing one entry for each existing Pie. +///////////////////////////////////////////////////////////////////////// + +class PieComboList : Gtk.ComboBox { + +    ///////////////////////////////////////////////////////////////////// +    /// This signal gets emitted when the user selects a new Pie. +    ///////////////////////////////////////////////////////////////////// +     +    public signal void on_select(string id); +     +    ///////////////////////////////////////////////////////////////////// +    /// The currently selected row. +    ///////////////////////////////////////////////////////////////////// +     +    public string current_id { get; private set; default=""; } +     +    ///////////////////////////////////////////////////////////////////// +    /// Stores the data internally. +    ///////////////////////////////////////////////////////////////////// +     +    private Gtk.ListStore data; +    private enum DataPos {ICON, NAME, ID} + +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, constructs the Widget. +    ///////////////////////////////////////////////////////////////////// + +    public PieComboList() { +        GLib.Object(); +         +        this.data = new Gtk.ListStore(3, typeof(Gdk.Pixbuf),    +                                         typeof(string), +                                         typeof(string)); +                                          +        this.data.set_sort_column_id(1, Gtk.SortType.ASCENDING); +         +        base.set_model(this.data); +         +        var icon_render = new Gtk.CellRendererPixbuf(); +            icon_render.xpad = 4; +            this.pack_start(icon_render, false); +     +        var name_render = new Gtk.CellRendererText(); +            this.pack_start(name_render, true); +         +        this.add_attribute(icon_render, "pixbuf", DataPos.ICON); +        this.add_attribute(name_render, "markup", DataPos.NAME); +         +        this.changed.connect(() => { +            Gtk.TreeIter active; +            if (this.get_active_iter(out active)) { +                string id = ""; +                this.data.get(active, DataPos.ID, out id); +                this.on_select(id); +                this.current_id = id; +            } +        }); +         +        reload(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Loads all existing Pies to the list. +    ///////////////////////////////////////////////////////////////////// +     +    public void reload() { +        Gtk.TreeIter active; +        string id = ""; +        if (this.get_active_iter(out active)) +            this.data.get(active, DataPos.ID, out id); +     +        data.clear(); +        foreach (var pie in PieManager.all_pies.entries) { +            this.load_pie(pie.value); +        } +         +        select_first(); +        select(id); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Selects the first Pie. +    ///////////////////////////////////////////////////////////////////// +     +    public void select_first() { +        Gtk.TreeIter active; +         +        if(this.data.get_iter_first(out active) ) { +            this.set_active_iter(active); +            string id = ""; +            this.data.get(active, DataPos.ID, out id); +            this.on_select(id); +            this.current_id = id; +        } else { +            this.on_select(""); +            this.current_id = ""; +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Selects the Pie with the given ID. +    ///////////////////////////////////////////////////////////////////// +     +    public void select(string id) { +        this.data.foreach((model, path, iter) => { +            string pie_id; +            this.data.get(iter, DataPos.ID, out pie_id); +             +            if (id == pie_id) { +                this.set_active_iter(iter); +                return true; +            } +             +            return false; +        }); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Loads one given pie to the list. +    ///////////////////////////////////////////////////////////////////// +     +    private void load_pie(Pie pie) { +        if (pie.id.length == 3) { +            Gtk.TreeIter last; +            this.data.append(out last); +            var icon = new Icon(pie.icon, 24); +            this.data.set(last, DataPos.ICON, icon.to_pixbuf(),  +                                DataPos.NAME, pie.name, +                                DataPos.ID, pie.id);  +        } +    } +} + +} diff --git a/src/gui/pieList.vala b/src/gui/pieList.vala index 46970d5..bfcb832 100644 --- a/src/gui/pieList.vala +++ b/src/gui/pieList.vala @@ -17,1029 +17,226 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  namespace GnomePie { -// A very complex Widget. This is by far the most ugly file of this project -// but well, this list *is* complex... sorry ;) +/////////////////////////////////////////////////////////////////////////     +/// A list, containing one entry for each existing Pie. +/////////////////////////////////////////////////////////////////////////  class PieList : Gtk.TreeView { -    private Gtk.ListStore groups; -    private Gtk.ListStore pies; -    private Gtk.ListStore actions; -    private Gtk.TreeStore data; +    ///////////////////////////////////////////////////////////////////// +    /// This signal gets emitted when the user selects a new Pie. +    ///////////////////////////////////////////////////////////////////// -    private const int small_icon = 24; -    private const int large_icon = 36; +    public signal void on_select(string id); -    // data positions in the data ListStore -    private enum DataPos {IS_QUICKACTION, ICON, NAME, TYPE_ID, ACTION_TYPE, -                          ICON_PIXBUF, FONT_WEIGHT, ICON_NAME_EDITABLE, QUICKACTION_VISIBLE, QUICKACTION_ACTIVATABLE, -                          TYPE_VISIBLE, GROUP_VISIBLE, APP_VISIBLE, KEY_VISIBLE, PIE_VISIBLE, -                          URI_VISIBLE, TRIGGER_VISIBLE, DISPLAY_COMMAND_GROUP, DISPLAY_COMMAND_APP,  -                          DISPLAY_COMMAND_KEY, DISPLAY_COMMAND_PIE, DISPLAY_COMMAND_URI, -                          REAL_COMMAND_GROUP, REAL_COMMAND_PIE, REAL_COMMAND_KEY} +    ///////////////////////////////////////////////////////////////////// +    /// Stores the data internally. +    ///////////////////////////////////////////////////////////////////// -    // data positions in the actions ListStore -    private enum ActionPos {NAME, TYPE, CAN_QUICKACTION, ICON_NAME_EDITABLE} +    private Gtk.ListStore data; +    private enum DataPos {ICON, ICON_NAME, NAME, ID} -    // data positions in the pies ListStore -    private enum PiePos {NAME, ID} +    ///////////////////////////////////////////////////////////////////// +    /// Stores where a drag startet. +    ///////////////////////////////////////////////////////////////////// -    // data positions in the groups ListStore -    private enum GroupPos {NAME, TYPE, ICON} +    private Gtk.TreeIter? drag_start = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// Rembers the time when a last drag move event was reported. Used +    /// to avoid frequent changes of selected Pie when a Pie is dragged +    /// over this widget. +    ///////////////////////////////////////////////////////////////////// + +    private uint last_hover = 0; +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, constructs the Widget. +    /////////////////////////////////////////////////////////////////////      public PieList() {          GLib.Object(); -        Gtk.TreeIter last; -         -        // group choices -        this.groups = new Gtk.ListStore(3, typeof(string),     // group name -                                           typeof(string),     // group type -                                           typeof(string));    // group icon -         -        // add all registered group types -        foreach (var type in GroupRegistry.types) { -            this.groups.append(out last);  -            this.groups.set(last, GroupPos.NAME, GroupRegistry.names[type],  -                                  GroupPos.TYPE, type.name(),  -                                  GroupPos.ICON, GroupRegistry.icons[type]);  -        } -          -        // pie choices -        this.pies = new Gtk.ListStore(2,  typeof(string),      // pie name  -                                          typeof(string));     // pie id -         -        // action type choices                                                               -        this.actions = new Gtk.ListStore(4, typeof(string),    // type name -                                            typeof(string),    // action type  -                                            typeof(bool),      // can be quickaction -                                            typeof(bool));     // icon/name editable    -         -        // add all registered action types -        foreach (var type in ActionRegistry.types) { -            this.actions.append(out last);  -            this.actions.set(last, ActionPos.NAME, ActionRegistry.names[type],  -                                   ActionPos.TYPE, type.name(),  -                        ActionPos.CAN_QUICKACTION, true,  -                     ActionPos.ICON_NAME_EDITABLE, ActionRegistry.icon_name_editables[type]);  -        } -        // and one type for groups -        this.actions.append(out last);  -        this.actions.set(last, ActionPos.NAME, _("Slice group"),  -                               ActionPos.TYPE, typeof(ActionGroup).name(),  -                    ActionPos.CAN_QUICKACTION, false,  -                 ActionPos.ICON_NAME_EDITABLE, false);  +        this.data = new Gtk.ListStore(4, typeof(Gdk.Pixbuf),    +                                         typeof(string), +                                         typeof(string), +                                         typeof(string)); +                                          +        this.data.set_sort_column_id(DataPos.NAME, Gtk.SortType.ASCENDING); -        // main data model -        this.data = new Gtk.TreeStore(25, typeof(bool),       // is quickaction -                                          typeof(string),     // icon -                                          typeof(string),     // name    -                                          typeof(string),     // slice: type label, pie: "ID: %id" -                                          typeof(string),     // typeof(action), typeof(ActionGroup).name() if group action, pie_id if Pie  -                                           -                                          typeof(Gdk.Pixbuf), // icon pixbuf -                                          typeof(int),        // font weight -                                           -                                          typeof(bool),       // icon/name editable -                                           -                                          typeof(bool),       // quickaction visible -                                          typeof(bool),       // quickaction activatable -                                          typeof(bool),       // type visible -                                          typeof(bool),       // group renderer visible -                                          typeof(bool),       // app renderer visible -                                          typeof(bool),       // key renderer visible -                                          typeof(bool),       // pie renderer visible -                                          typeof(bool),       // uri renderer visible -                                          typeof(bool),       // trigger renderer visible -                                           -                                          typeof(string),     // display command group -                                          typeof(string),     // display command app -                                          typeof(string),     // display command key -                                          typeof(string),     // display command pie -                                          typeof(string),     // display command uri -                                           -                                          typeof(string),     // real command group -                                          typeof(string),     // real command pie -                                          typeof(string));    // real command key -                                           -                      this.set_model(this.data); +        this.set_headers_visible(false);          this.set_grid_lines(Gtk.TreeViewGridLines.NONE); -        this.set_enable_tree_lines(false); -        this.set_reorderable(false); -        this.set_level_indentation(-10); +        this.width_request = 170; +        this.set_enable_search(false); -        // create the gui -        // icon column -        var icon_column = new Gtk.TreeViewColumn(); -            icon_column.title = _("Icon"); -            icon_column.expand = false; -             -            // quickaction checkbox -            var check_render = new Gtk.CellRendererToggle(); -                check_render.activatable = true; -                check_render.radio = true; -                check_render.width = 15; - -                check_render.toggled.connect((path) => { -                    Gtk.TreeIter toggled; -                    this.data.get_iter_from_string(out toggled, path); -                     -                    bool current = false; -                    this.data.get(toggled, DataPos.IS_QUICKACTION, out current); -                     -                    // set all others off -                    Gtk.TreeIter parent; -                    this.data.iter_parent(out parent, toggled); -                    string parent_pos = this.data.get_string_from_iter(parent); -                    int child_count = this.data.iter_n_children(parent); -                     -                    for (int i=0; i<child_count; ++i) { -                        Gtk.TreeIter child; -                        this.data.get_iter_from_string(out child, "%s:%d".printf(parent_pos, i)); -                        this.data.set(child, DataPos.IS_QUICKACTION, false); -                    } -                     -                    // toggle selected -                    this.data.set(toggled, DataPos.IS_QUICKACTION, !current); -                     -                    this.update_pie(toggled); -                }); -                 -                icon_column.pack_start(check_render, false); -                icon_column.add_attribute(check_render, "activatable", DataPos.QUICKACTION_ACTIVATABLE); -                icon_column.add_attribute(check_render, "sensitive", DataPos.QUICKACTION_ACTIVATABLE); -                icon_column.add_attribute(check_render, "visible", DataPos.QUICKACTION_VISIBLE); -                icon_column.add_attribute(check_render, "active", DataPos.IS_QUICKACTION); -                 +        this.set_events(Gdk.EventMask.POINTER_MOTION_MASK); -            // icon  -            var icon_render = new GnomePie.CellRendererIcon(); -                icon_render.editable = true; - -                icon_render.on_select.connect((path, icon_name) => { -                    Gtk.TreeIter iter; -                    this.data.get_iter_from_string(out iter, path); -                    int icon_size =  this.data.iter_depth(iter) == 0 ? this.large_icon : this.small_icon; -                     -                    this.data.set(iter, DataPos.ICON, icon_name); -                    this.data.set(iter, DataPos.ICON_PIXBUF, this.load_icon(icon_name, icon_size)); -                     -                    this.update_pie(iter); -                    this.update_linked(); -                }); -                 -                icon_column.pack_start(icon_render, false); -                icon_column.add_attribute(icon_render, "icon_name", DataPos.ICON); -                icon_column.add_attribute(icon_render, "pixbuf", DataPos.ICON_PIXBUF); -                icon_column.add_attribute(icon_render, "editable", DataPos.ICON_NAME_EDITABLE); -                icon_column.add_attribute(icon_render, "icon_sensitive", DataPos.ICON_NAME_EDITABLE); -                   -        // command column     -        var command_column = new Gtk.TreeViewColumn(); -            command_column.title = _("Command"); -            command_column.resizable = true; -            command_column.expand = true; -             -            // trigger  -            var command_renderer_trigger = new CellRendererTrigger(); -                command_renderer_trigger.editable = true; -                command_renderer_trigger.ellipsize = Pango.EllipsizeMode.END; - -                command_renderer_trigger.on_select.connect((path, trigger) => {                  -                    Gtk.TreeIter data_iter; -                    this.data.get_iter_from_string(out data_iter, path); -                     -                    this.data.set(data_iter, DataPos.DISPLAY_COMMAND_KEY, trigger.label_with_specials); -                    this.data.set(data_iter, DataPos.REAL_COMMAND_KEY, trigger.name); -                     -                    this.update_pie(data_iter); -                }); -                 -                command_column.pack_end(command_renderer_trigger, true); -                command_column.add_attribute(command_renderer_trigger, "weight", DataPos.FONT_WEIGHT); -                command_column.add_attribute(command_renderer_trigger, "markup", DataPos.DISPLAY_COMMAND_KEY); -                command_column.add_attribute(command_renderer_trigger, "visible", DataPos.TRIGGER_VISIBLE); -                command_column.add_attribute(command_renderer_trigger, "trigger", DataPos.REAL_COMMAND_KEY); -             -            // slice group  -            var command_renderer_group = new Gtk.CellRendererCombo(); -                command_renderer_group.editable = true; -                command_renderer_group.has_entry = false; -                command_renderer_group.text_column = 0; -                command_renderer_group.ellipsize = Pango.EllipsizeMode.END; -                command_renderer_group.model = this.groups; - -                command_renderer_group.changed.connect((path, iter) => { -                    string display_name; -                    string type; -                    string icon; -                     -                    this.groups.get(iter, GroupPos.NAME, out display_name); -                    this.groups.get(iter, GroupPos.TYPE, out type); -                    this.groups.get(iter, GroupPos.ICON, out icon); -                                      -                    Gtk.TreeIter data_iter; -                    this.data.get_iter_from_string(out data_iter, path); -                     -                    this.data.set(data_iter, DataPos.DISPLAY_COMMAND_GROUP, display_name); -                    this.data.set(data_iter, DataPos.REAL_COMMAND_GROUP, type); -                    this.data.set(data_iter, DataPos.NAME, display_name); -                    this.data.set(data_iter, DataPos.ICON, icon); -                     -                    this.update_pie(data_iter); -                }); -                 -                command_column.pack_end(command_renderer_group, true); -                command_column.add_attribute(command_renderer_group, "weight", DataPos.FONT_WEIGHT); -                command_column.add_attribute(command_renderer_group, "text", DataPos.DISPLAY_COMMAND_GROUP); -                command_column.add_attribute(command_renderer_group, "visible", DataPos.GROUP_VISIBLE); -                 -                 -            // app action  -            var command_renderer_app = new Gtk.CellRendererText(); -                command_renderer_app.editable = true; -                command_renderer_app.ellipsize = Pango.EllipsizeMode.END; - -                command_renderer_app.edited.connect((path, command) => {                  -                    Gtk.TreeIter data_iter; -                    this.data.get_iter_from_string(out data_iter, path); -                     -                    this.data.set(data_iter, DataPos.DISPLAY_COMMAND_APP, command); -                     -                    this.update_pie(data_iter); -                }); -                 -                command_column.pack_end(command_renderer_app, true); -                command_column.add_attribute(command_renderer_app, "weight", DataPos.FONT_WEIGHT); -                command_column.add_attribute(command_renderer_app, "text", DataPos.DISPLAY_COMMAND_APP); -                command_column.add_attribute(command_renderer_app, "visible", DataPos.APP_VISIBLE); -                 -                 -            // key action  -            var command_renderer_key = new Gtk.CellRendererAccel(); -                command_renderer_key.editable = true; -                command_renderer_key.ellipsize = Pango.EllipsizeMode.END; - -                command_renderer_key.accel_edited.connect((path, key, mods) => {                  -                    Gtk.TreeIter data_iter; -                    this.data.get_iter_from_string(out data_iter, path); -                     -                    string label = Gtk.accelerator_get_label(key, mods); -                    string accelerator = Gtk.accelerator_name(key, mods); -                     -                    this.data.set(data_iter, DataPos.DISPLAY_COMMAND_KEY, label); -                    this.data.set(data_iter, DataPos.REAL_COMMAND_KEY, accelerator); -                     -                    this.update_pie(data_iter); -                }); -                 -                command_renderer_key.accel_cleared.connect((path) => {                  -                    Gtk.TreeIter data_iter; -                    this.data.get_iter_from_string(out data_iter, path); -                     -                    this.data.set(data_iter, DataPos.DISPLAY_COMMAND_KEY, _("Not bound")); -                    this.data.set(data_iter, DataPos.REAL_COMMAND_KEY, ""); -                     -                    this.update_pie(data_iter); -                }); -                 -                command_column.pack_end(command_renderer_key, true); -                command_column.add_attribute(command_renderer_key, "weight", DataPos.FONT_WEIGHT); -                command_column.add_attribute(command_renderer_key, "text", DataPos.DISPLAY_COMMAND_KEY); -                command_column.add_attribute(command_renderer_key, "visible", DataPos.KEY_VISIBLE); -                 -                 -            // pie action  -            var command_renderer_pie = new Gtk.CellRendererCombo(); -                command_renderer_pie.editable = true; -                command_renderer_pie.has_entry = false; -                command_renderer_pie.text_column = 0; -                command_renderer_pie.ellipsize = Pango.EllipsizeMode.END; -                command_renderer_pie.model = this.pies; - -                command_renderer_pie.changed.connect((path, iter) => { -                    string name; -                    string id; -                     -                    this.pies.get(iter, PiePos.NAME, out name); -                    this.pies.get(iter, PiePos.ID, out id); -                                      -                    Gtk.TreeIter data_iter; -                    this.data.get_iter_from_string(out data_iter, path); -                     -                    this.data.set(data_iter, DataPos.DISPLAY_COMMAND_PIE, name); -                    this.data.set(data_iter, DataPos.REAL_COMMAND_PIE, id); -                     -                    this.update_pie(data_iter); -                    this.update_linked(); -                }); -                 -                command_column.pack_end(command_renderer_pie, true); -                command_column.add_attribute(command_renderer_pie, "weight", DataPos.FONT_WEIGHT); -                command_column.add_attribute(command_renderer_pie, "text", DataPos.DISPLAY_COMMAND_PIE); -                command_column.add_attribute(command_renderer_pie, "visible", DataPos.PIE_VISIBLE); -                 -                 -            // uri action  -            var command_renderer_uri = new Gtk.CellRendererText(); -                command_renderer_uri.editable = true; -                command_renderer_uri.ellipsize = Pango.EllipsizeMode.END; - -                command_renderer_uri.edited.connect((path, uri) => {                  -                    Gtk.TreeIter data_iter; -                    this.data.get_iter_from_string(out data_iter, path); -                     -                    this.data.set(data_iter, DataPos.DISPLAY_COMMAND_URI, uri); -                     -                    this.update_pie(data_iter); -                }); -                 -                command_column.pack_end(command_renderer_uri, true); -                command_column.add_attribute(command_renderer_uri, "weight", DataPos.FONT_WEIGHT); -                command_column.add_attribute(command_renderer_uri, "text", DataPos.DISPLAY_COMMAND_URI); -                command_column.add_attribute(command_renderer_uri, "visible", DataPos.URI_VISIBLE); -                 -         -        // type column    -        var type_column = new Gtk.TreeViewColumn(); -            type_column.title = _("Pie-ID / Action type"); -            type_column.resizable = true; -            type_column.expand = false; -                 -            var type_render = new Gtk.CellRendererCombo(); -                type_render.editable = true; -                type_render.has_entry = false; -                type_render.model = actions; -                type_render.text_column = 0; -                type_render.ellipsize = Pango.EllipsizeMode.END; - -                // change command_render's visibility accordingly -                type_render.changed.connect((path, iter) => { -                    string text = ""; -                    string type; -                    bool can_quickaction; -                    bool icon_name_editable; -                     -                    this.actions.get(iter, ActionPos.NAME, out text); -                    this.actions.get(iter, ActionPos.TYPE, out type); -                    this.actions.get(iter, ActionPos.CAN_QUICKACTION, out can_quickaction); -                    this.actions.get(iter, ActionPos.ICON_NAME_EDITABLE, out icon_name_editable); -                 -                    Gtk.TreeIter data_iter; -                    this.data.get_iter_from_string(out data_iter, path); -                     -                    this.data.set(data_iter, DataPos.TYPE_ID, text); -                    this.data.set(data_iter, DataPos.ACTION_TYPE, type); -                    this.data.set(data_iter, DataPos.QUICKACTION_ACTIVATABLE, can_quickaction); -                    this.data.set(data_iter, DataPos.ICON_NAME_EDITABLE, icon_name_editable); -                     -                    // set all command renderes invisible -                    this.data.set(data_iter, DataPos.GROUP_VISIBLE, false); -                    this.data.set(data_iter, DataPos.APP_VISIBLE, false); -                    this.data.set(data_iter, DataPos.KEY_VISIBLE, false); -                    this.data.set(data_iter, DataPos.PIE_VISIBLE, false); -                    this.data.set(data_iter, DataPos.URI_VISIBLE, false); -                     -                    // set one visible -                    int type_id = 0; -                    if(type == typeof(AppAction).name()) type_id = 1;  -                    else if(type == typeof(KeyAction).name()) type_id = 2;  -                    else if(type == typeof(PieAction).name()) type_id = 3;  -                    else if(type == typeof(UriAction).name()) type_id = 4;  -                    else type_id = 0; -                     -                    this.data.set(data_iter, DataPos.GROUP_VISIBLE + type_id, true); -                     -                    this.update_linked(); -                    this.update_pie(data_iter); -                     -                    //this.set_cursor(new Gtk.TreePath.from_string(path), command_column, true); -                }); -                 -                type_column.pack_start(type_render, true); -                type_column.add_attribute(type_render, "sensitive", DataPos.TYPE_VISIBLE); -                type_column.add_attribute(type_render, "editable", DataPos.TYPE_VISIBLE); -                type_column.add_attribute(type_render, "text", DataPos.TYPE_ID); -         -        // name column     -        var name_column = new Gtk.TreeViewColumn(); -            name_column.title = _("Name"); -            name_column.expand = true; -            name_column.resizable = true; +        var main_column = new Gtk.TreeViewColumn(); +            var icon_render = new Gtk.CellRendererPixbuf(); +                icon_render.xpad = 4; +                icon_render.ypad = 4; +                main_column.pack_start(icon_render, false);              var name_render = new Gtk.CellRendererText(); -                name_render.editable = true;                  name_render.ellipsize = Pango.EllipsizeMode.END; - -                name_render.edited.connect((path, text) => {                         -                    Gtk.TreeIter iter; -                    this.data.get_iter_from_string(out iter, path); -                     -                    this.data.set(iter, DataPos.NAME, text); -                     -                    // try to change icon to a fitting one -                    string icon; -                    this.data.get(iter, DataPos.ICON, out icon); -                    if (icon == "application-default-icon" && Gtk.IconTheme.get_default().has_icon(text.down())) { -                        this.data.set(iter, DataPos.ICON, text.down()); -                    } -                     -                    this.update_pie(iter); -                    this.update_linked(); -                     -                    //this.set_cursor(new Gtk.TreePath.from_string(path), type_column, true); -                }); -                 -                name_column.pack_start(name_render, true); -                name_column.add_attribute(name_render, "weight", DataPos.FONT_WEIGHT); -                name_column.add_attribute(name_render, "text", DataPos.NAME); -                name_column.add_attribute(name_render, "sensitive", DataPos.ICON_NAME_EDITABLE); -                name_column.add_attribute(name_render, "editable", DataPos.ICON_NAME_EDITABLE); -         -        this.append_column(icon_column); -        this.append_column(name_column); -        this.append_column(type_column); -        this.append_column(command_column); +                name_render.ellipsize_set = true; +                main_column.pack_start(name_render, true); -        this.realize.connect(this.load); -         -        // context menu -        var menu = new Gtk.Menu(); - -        var item = new Gtk.ImageMenuItem.with_label(_("Add new Pie")); -        item.set_image(new Gtk.Image.from_stock(Gtk.Stock.ADD, Gtk.IconSize.MENU)); -        item.activate.connect(this.add_empty_pie); -        menu.append(item); - -        item = new Gtk.ImageMenuItem.with_label(_("Add new Slice")); -        item.set_image(new Gtk.Image.from_stock(Gtk.Stock.ADD, Gtk.IconSize.MENU)); -        item.activate.connect(this.add_empty_slice); -        menu.append(item); -         -        var sepa = new Gtk.SeparatorMenuItem(); -        menu.append(sepa); - -        item = new Gtk.ImageMenuItem.with_label(_("Delete")); -        item.set_image(new Gtk.Image.from_stock(Gtk.Stock.DELETE, Gtk.IconSize.MENU)); -        item.activate.connect(this.delete_selection); -        menu.append(item); +        base.append_column(main_column); -        menu.show_all(); -         -        this.button_press_event.connect((event) => { -            if (event.type == Gdk.EventType.BUTTON_PRESS && event.button == 3) { -                menu.popup(null, null, null, event.button, event.time); -            } -            return false; -        }); +        main_column.add_attribute(icon_render, "pixbuf", DataPos.ICON); +        main_column.add_attribute(name_render, "markup", DataPos.NAME);          // setup drag'n'drop          Gtk.TargetEntry uri_source = {"text/uri-list", 0, 0};          Gtk.TargetEntry[] entries = { uri_source }; -         -        this.drag_data_received.connect(this.on_dnd_received); -        this.drag_data_get.connect(this.on_dnd_source); +        this.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, entries, Gdk.DragAction.LINK);          this.enable_model_drag_dest(entries, Gdk.DragAction.COPY | Gdk.DragAction.MOVE | Gdk.DragAction.LINK); +        this.drag_data_get.connect(this.on_dnd_source); +        this.drag_begin.connect_after(this.on_start_drag); +        this.drag_motion.connect(this.on_drag_move); +        this.drag_leave.connect(() => { +            this.last_hover = 0; +        });          this.get_selection().changed.connect(() => { -            Gtk.TreeIter selected; -            if (this.get_selection().get_selected(null, out selected)) { -                if (this.data.iter_depth(selected) == 0) { -                     this.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, entries, Gdk.DragAction.LINK);    -                } else { -                    this.unset_rows_drag_source(); -                } +            Gtk.TreeIter active; +            if (this.get_selection().get_selected(null, out active)) { +                string id = ""; +                this.data.get(active, DataPos.ID, out id); +                this.on_select(id);              }          }); -        this.drag_begin.connect(() => { -            this.unset_rows_drag_dest(); -        }); -         -        this.drag_end.connect(() => { -            this.enable_model_drag_dest(entries, Gdk.DragAction.COPY | Gdk.DragAction.MOVE | Gdk.DragAction.LINK); -        }); +        reload_all();      } -    // moves the selected slice up -    public void selection_up() { -        Gtk.TreeIter selected; -        if (this.get_selection().get_selected(null, out selected)) { -            Gtk.TreePath path = this.data.get_path(selected); -            Gtk.TreeIter? before = null;; -            if (path.prev() && this.data.get_iter(out before, path)) { -                this.data.swap(selected, before); -                this.get_selection().changed(); -                this.update_pie(selected); -            } -        } -    } +    ///////////////////////////////////////////////////////////////////// +    /// Loads all existing Pies to the list. +    ///////////////////////////////////////////////////////////////////// -    // moves the selected slice down -    public void selection_down() { -        Gtk.TreeIter selected; -        if (this.get_selection().get_selected(null, out selected)) { -            Gtk.TreePath path = this.data.get_path(selected); -            Gtk.TreeIter? after = null; -            path.next(); -            if (this.data.get_iter(out after, path)) { -                this.data.swap(selected, after); -                this.get_selection().changed(); -                this.update_pie(selected); -            } -        } -    } +    public void reload_all() { +        Gtk.TreeIter active; +        string id = ""; +        if (this.get_selection().get_selected(null, out active)) +            this.data.get(active, DataPos.ID, out id); -    // updates the entire list, checking for changed cross-references via PieActions -    // updates their names and icons if needed -    private void update_linked() { -        this.data.foreach((model, path, iter) => { -            string action_type; -            this.data.get(iter, DataPos.ACTION_TYPE, out action_type); -             -            if (action_type == typeof(PieAction).name()) { -                string command; -                this.data.get(iter, DataPos.REAL_COMMAND_PIE, out command); -                 -                var referee = PieManager.all_pies[command]; -                 -                if (referee != null) { -                    this.data.set(iter, DataPos.ICON, referee.icon); -                    this.data.set(iter, DataPos.NAME, referee.name); -                    this.data.set(iter, DataPos.ICON_PIXBUF, this.load_icon(referee.icon, this.small_icon)); -                    this.data.set(iter, DataPos.DISPLAY_COMMAND_PIE, referee.name); -                } else { -                    // referenced Pie does not exist anymore or no is selected; -                    // select the first one... -                    Gtk.TreeIter first_pie; -                    this.pies.get_iter_first(out first_pie); -                     -                    string name; -                    string id; -                     -                    this.pies.get(first_pie, PiePos.NAME, out name); -                    this.pies.get(first_pie, PiePos.ID, out id); -                     -                    this.data.set(iter, DataPos.DISPLAY_COMMAND_PIE, name); -                    this.data.set(iter, DataPos.REAL_COMMAND_PIE, id); -                     -                    update_linked(); -                } -            } else if (action_type == typeof(ActionGroup).name()) { -                string command; -                this.data.get(iter, DataPos.REAL_COMMAND_GROUP, out command); -                                -                if (command == "") { -                    // no group is selected, select the first one... -                    Gtk.TreeIter first_group; -                    this.groups.get_iter_first(out first_group); -                     -                    string name; -                    string type; -                    string icon; -                     -                    this.groups.get(first_group, GroupPos.NAME, out name); -                    this.groups.get(first_group, GroupPos.TYPE, out type); -                    this.groups.get(first_group, GroupPos.ICON, out icon); -                     -                    this.data.set(iter, DataPos.DISPLAY_COMMAND_GROUP, name); -                    this.data.set(iter, DataPos.NAME, name); -                    this.data.set(iter, DataPos.REAL_COMMAND_GROUP, type); -                    this.data.set(iter, DataPos.ICON, icon); -                } -            } -             -            return false; -        }); +        data.clear(); +        foreach (var pie in PieManager.all_pies.entries) { +            this.load_pie(pie.value); +        } +         +        select(id);      } -    // adds a new, empty pie to the list -    private void add_empty_pie() { -        var new_one = PieManager.create_persistent_pie(_("New Pie"), "application-default-icon", null); -         -        Gtk.TreeIter last; -        this.pies.append(out last); this.pies.set(last, 0, new_one.name, 1, new_one.id);  +    ///////////////////////////////////////////////////////////////////// +    /// Selects the first Pie. +    ///////////////////////////////////////////////////////////////////// -        Gtk.TreeIter parent; -        this.data.append(out parent, null); -        this.data.set(parent, DataPos.IS_QUICKACTION, false, -                                        DataPos.ICON, new_one.icon, -                                        DataPos.NAME, new_one.name, -                                     DataPos.TYPE_ID, "ID: " + new_one.id, -                                 DataPos.ACTION_TYPE, new_one.id, -                                 DataPos.ICON_PIXBUF, this.load_icon(new_one.icon, this.large_icon), -                                 DataPos.FONT_WEIGHT, 800, -                          DataPos.ICON_NAME_EDITABLE, true, -                         DataPos.QUICKACTION_VISIBLE, false, -                     DataPos.QUICKACTION_ACTIVATABLE, false, -                                DataPos.TYPE_VISIBLE, false, -                               DataPos.GROUP_VISIBLE, false, -                                 DataPos.APP_VISIBLE, false, -                                 DataPos.KEY_VISIBLE, false, -                                 DataPos.PIE_VISIBLE, false, -                                 DataPos.URI_VISIBLE, false, -                             DataPos.TRIGGER_VISIBLE, true, -                       DataPos.DISPLAY_COMMAND_GROUP, "", -                         DataPos.DISPLAY_COMMAND_APP, "", -                         DataPos.DISPLAY_COMMAND_KEY, PieManager.get_accelerator_label_of(new_one.id), -                         DataPos.DISPLAY_COMMAND_PIE, "", -                         DataPos.DISPLAY_COMMAND_URI, "", -                          DataPos.REAL_COMMAND_GROUP, "", -                            DataPos.REAL_COMMAND_PIE, "", -                            DataPos.REAL_COMMAND_KEY, PieManager.get_accelerator_of(new_one.id)); -                           +    public void select_first() { +        Gtk.TreeIter active; -        this.get_selection().select_iter(parent); -        this.scroll_to_cell(this.data.get_path(parent), null, true, 0.5f, 0.0f); -    } -     -    // adds a new empty slice to the list -    private void add_empty_slice() { -        Gtk.TreeIter selected; -        if (this.get_selection().get_selected(null, out selected)) { -            var path = this.data.get_path(selected); -            if (path != null) { -                if (path.get_depth() == 2) -                    this.data.iter_parent(out selected, selected); -                 -                this.load_action(selected, new AppAction(_("New Action"), "application-default-icon", "")); -                 -                Gtk.TreeIter new_one; -                this.data.iter_nth_child(out new_one, selected, this.data.iter_n_children(selected)-1); -                this.expand_to_path(this.data.get_path(new_one)); -                this.get_selection().select_iter(new_one); -                this.scroll_to_cell(this.data.get_path(new_one), null, true, 0.5f, 0.0f); -                 -                this.update_pie(selected); -            }  +        if(this.data.get_iter_first(out active) ) { +            this.get_selection().select_iter(active); +            string id = ""; +            this.data.get(active, DataPos.ID, out id); +            this.on_select(id);          } else { -            var dialog = new Gtk.MessageDialog((Gtk.Window)this.get_toplevel(), Gtk.DialogFlags.MODAL,  -                                                     Gtk.MessageType.INFO,  -                                                     Gtk.ButtonsType.CLOSE,  -                                                     _("You have to select a Pie to add a Slice to!")); -            dialog.run(); -            dialog.destroy(); +            this.on_select("");          }      } -    // writes the contents of action to the position pointed by slice -    private void write_action(Action action, Gtk.TreeIter slice) { -        this.data.set(slice, DataPos.IS_QUICKACTION, action.is_quick_action, -                                       DataPos.ICON, action.icon, -                                       DataPos.NAME, action.name, -                                    DataPos.TYPE_ID, ActionRegistry.names[action.get_type()], -                                DataPos.ACTION_TYPE, action.get_type().name(), -                                DataPos.ICON_PIXBUF, this.load_icon(action.icon, this.small_icon), -                                DataPos.FONT_WEIGHT, 400, -                         DataPos.ICON_NAME_EDITABLE, !(action is PieAction), -                        DataPos.QUICKACTION_VISIBLE, true, -                    DataPos.QUICKACTION_ACTIVATABLE, true, -                               DataPos.TYPE_VISIBLE, true, -                              DataPos.GROUP_VISIBLE, false, -                            DataPos.TRIGGER_VISIBLE, false, -                                DataPos.APP_VISIBLE, action is AppAction, -                                DataPos.KEY_VISIBLE, action is KeyAction, -                                DataPos.PIE_VISIBLE, action is PieAction, -                                DataPos.URI_VISIBLE, action is UriAction, -                      DataPos.DISPLAY_COMMAND_GROUP, "", -                        DataPos.DISPLAY_COMMAND_APP, (action is AppAction) ? action.display_command : "", -                        DataPos.DISPLAY_COMMAND_KEY, (action is KeyAction) ? action.display_command : _("Not bound"), -                        DataPos.DISPLAY_COMMAND_PIE, (action is PieAction) ? action.display_command : "", -                        DataPos.DISPLAY_COMMAND_URI, (action is UriAction) ? action.display_command : "", -                         DataPos.REAL_COMMAND_GROUP, "", -                           DataPos.REAL_COMMAND_PIE, (action is PieAction) ? action.real_command : "", -                           DataPos.REAL_COMMAND_KEY, (action is KeyAction) ? action.real_command : ""); -    } +    ///////////////////////////////////////////////////////////////////// +    /// Selects the Pie with the given ID. +    ///////////////////////////////////////////////////////////////////// -    // deletes the currently selected pie or slice -    private void delete_selection() { -        Gtk.TreeIter selected; -        if (this.get_selection().get_selected(null, out selected)) { -            var path = this.data.get_path(selected); -            if (path != null) { -                if (path.get_depth() == 1) -                    this.delete_pie(selected); -                else -                    this.delete_slice(selected); -            }  -        } else { -            var dialog = new Gtk.MessageDialog((Gtk.Window)this.get_toplevel(), Gtk.DialogFlags.MODAL,  -                                                     Gtk.MessageType.INFO,  -                                                     Gtk.ButtonsType.CLOSE,  -                                                     _("You have to select a Pie or a Slice to delete!")); -            dialog.run(); -            dialog.destroy(); -        } -    } - -    // deletes the given pie -    private void delete_pie(Gtk.TreeIter pie) { -        var dialog = new Gtk.MessageDialog((Gtk.Window)this.get_toplevel(), Gtk.DialogFlags.MODAL,  -                                                 Gtk.MessageType.QUESTION,  -                                                 Gtk.ButtonsType.YES_NO,  -                                                 _("Do you really want to delete the selected Pie with all contained Slices?")); -                                                  -        dialog.response.connect((response) => { -            if (response == Gtk.ResponseType.YES) { -                string id; -                this.data.get(pie, DataPos.ACTION_TYPE, out id); -                this.data.remove(pie); -                PieManager.remove_pie(id); -                 -                this.pies.foreach((model, path, iter) => { -                    string pies_id; -                    this.pies.get(iter, PiePos.ID, out pies_id); -                     -                    if (id == pies_id) { -                        this.pies.remove(iter); -                        return true; -                    } -                     -                    return false; -                }); -                 -                this.update_linked(); -            } -        }); -         -        dialog.run(); -        dialog.destroy(); -    } - -    // deletes the given slice -    private void delete_slice(Gtk.TreeIter slice) { -        var dialog = new Gtk.MessageDialog((Gtk.Window)this.get_toplevel(), Gtk.DialogFlags.MODAL,  -                                                 Gtk.MessageType.QUESTION,  -                                                 Gtk.ButtonsType.YES_NO,  -                                                 _("Do you really want to delete the selected Slice?")); -                                                  -        dialog.response.connect((response) => { -            if (response == Gtk.ResponseType.YES) { -                Gtk.TreeIter parent; -                this.data.iter_parent(out parent, slice); -                this.data.remove(slice); -                this.update_pie(parent); +    public void select(string id) { +        this.data.foreach((model, path, iter) => { +            string pie_id; +            this.data.get(iter, DataPos.ID, out pie_id); +             +            if (id == pie_id) { +                this.get_selection().select_iter(iter); +                return true;              } +             +            return false;          }); -         -        dialog.run(); -        dialog.destroy();      } -    // loads all pies to the list -    private void load() { -        foreach (var pie in PieManager.all_pies.entries) { -            this.load_pie(pie.value); -        } -    } +    ///////////////////////////////////////////////////////////////////// +    /// Loads one given pie to the list. +    ///////////////////////////////////////////////////////////////////// -    // loads one given pie to the list      private void load_pie(Pie pie) {          if (pie.id.length == 3) { -                      Gtk.TreeIter last; -            this.pies.append(out last); this.pies.set(last, PiePos.NAME, pie.name,  -                                                              PiePos.ID, pie.id);  -         -            Gtk.TreeIter parent; -            this.data.append(out parent, null); -            this.data.set(parent, DataPos.IS_QUICKACTION, false, -                                            DataPos.ICON, pie.icon, -                                            DataPos.NAME, pie.name, -                                         DataPos.TYPE_ID, "ID: " + pie.id, -                                     DataPos.ACTION_TYPE, pie.id, -                                     DataPos.ICON_PIXBUF, this.load_icon(pie.icon, this.large_icon), -                                     DataPos.FONT_WEIGHT, 800, -                              DataPos.ICON_NAME_EDITABLE, true, -                             DataPos.QUICKACTION_VISIBLE, false, -                         DataPos.QUICKACTION_ACTIVATABLE, false, -                                    DataPos.TYPE_VISIBLE, false, -                                   DataPos.GROUP_VISIBLE, false, -                                     DataPos.APP_VISIBLE, false, -                                     DataPos.KEY_VISIBLE, false, -                                     DataPos.PIE_VISIBLE, false, -                                     DataPos.URI_VISIBLE, false, -                                 DataPos.TRIGGER_VISIBLE, true, -                           DataPos.DISPLAY_COMMAND_GROUP, "", -                             DataPos.DISPLAY_COMMAND_APP, "", -                             DataPos.DISPLAY_COMMAND_KEY, PieManager.get_accelerator_label_of(pie.id), -                             DataPos.DISPLAY_COMMAND_PIE, "", -                             DataPos.DISPLAY_COMMAND_URI, "", -                              DataPos.REAL_COMMAND_GROUP, "", -                                DataPos.REAL_COMMAND_PIE, "", -                                DataPos.REAL_COMMAND_KEY, PieManager.get_accelerator_of(pie.id)); -                              -            foreach (var group in pie.action_groups) { -                this.load_group(parent, group); -            } +            this.data.append(out last); +            var icon = new Icon(pie.icon, 24); +            this.data.set(last, DataPos.ICON, icon.to_pixbuf(),  +                                DataPos.ICON_NAME, pie.icon, +                                DataPos.NAME, pie.name, +                                DataPos.ID, pie.id);           }      } -    // loads a given group -    private void load_group(Gtk.TreeIter parent, ActionGroup group) { -        if (group.get_type() == typeof(ActionGroup)) { -            foreach (var action in group.actions) { -                this.load_action(parent, action); -            } -        } else { -            Gtk.TreeIter child; -            this.data.append(out child, parent); -            this.data.set(child, DataPos.IS_QUICKACTION, false, -                                           DataPos.ICON, GroupRegistry.icons[group.get_type()], -                                           DataPos.NAME, GroupRegistry.names[group.get_type()], -                                        DataPos.TYPE_ID, _("Slice group"), -                                    DataPos.ACTION_TYPE, typeof(ActionGroup).name(), -                                    DataPos.ICON_PIXBUF, this.load_icon(GroupRegistry.icons[group.get_type()], this.small_icon), -                                    DataPos.FONT_WEIGHT, 400, -                             DataPos.ICON_NAME_EDITABLE, false, -                            DataPos.QUICKACTION_VISIBLE, true, -                        DataPos.QUICKACTION_ACTIVATABLE, false, -                                   DataPos.TYPE_VISIBLE, true, -                                  DataPos.GROUP_VISIBLE, true, -                                    DataPos.APP_VISIBLE, false, -                                    DataPos.KEY_VISIBLE, false, -                                    DataPos.PIE_VISIBLE, false, -                                    DataPos.URI_VISIBLE, false, -                                DataPos.TRIGGER_VISIBLE, false, -                          DataPos.DISPLAY_COMMAND_GROUP, GroupRegistry.names[group.get_type()], -                            DataPos.DISPLAY_COMMAND_APP, "", -                            DataPos.DISPLAY_COMMAND_KEY, _("Not bound"), -                            DataPos.DISPLAY_COMMAND_PIE, "", -                            DataPos.DISPLAY_COMMAND_URI, "", -                             DataPos.REAL_COMMAND_GROUP, group.get_type().name(), -                               DataPos.REAL_COMMAND_PIE, "", -                               DataPos.REAL_COMMAND_KEY, ""); +    ///////////////////////////////////////////////////////////////////// +    /// Called when a drag which started on this Widget was successfull. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_dnd_source(Gdk.DragContext context, Gtk.SelectionData selection_data, uint info, uint time_) { +        if (this.drag_start != null) { +            string id = ""; +            this.data.get(this.drag_start, DataPos.ID, out id); +            selection_data.set_uris({"file://" + Paths.launchers + "/" + id + ".desktop"});          }      } -    // loads a given slice -    private void load_action(Gtk.TreeIter parent, Action action) { -        Gtk.TreeIter child; -        this.data.append(out child, parent); -        this.write_action(action, child); -    } +    ///////////////////////////////////////////////////////////////////// +    /// Called when a drag operation is started on this Widget. +    ///////////////////////////////////////////////////////////////////// -    // applies all changes done to the given pie -    private void update_pie(Gtk.TreeIter slice_or_pie) { -        // get pie iter -        var path = this.data.get_path(slice_or_pie); -        if (path != null) { -            var pie = slice_or_pie; -            if (path.get_depth() == 2) -                this.data.iter_parent(out pie, slice_or_pie); +    private void on_start_drag(Gdk.DragContext ctx) { +        if (this.get_selection().get_selected(null, out this.drag_start)) { +            string icon_name = ""; +            this.data.get(this.drag_start, DataPos.ICON_NAME, out icon_name); -            // get information on pie -            string id; -            string icon; -            string name; -            string hotkey; -             -            this.data.get(pie, DataPos.ICON, out icon); -            this.data.get(pie, DataPos.NAME, out name); -            this.data.get(pie, DataPos.ACTION_TYPE, out id); -            this.data.get(pie, DataPos.REAL_COMMAND_KEY, out hotkey); -             -            // remove pie -            PieManager.remove_pie(id); -              -            this.pies.foreach((model, path, iter) => { -                string pies_id; -                this.pies.get(iter, PiePos.ID, out pies_id); -                 -                if (id == pies_id) { -                    this.pies.set(iter, PiePos.NAME, name); -                    return true; -                } -                 -                return false; -            }); -                 -            // create new pie -            var new_pie = PieManager.create_persistent_pie(name, icon, new Trigger.from_string(hotkey), id); -             -            // add actions accordingly -            if (this.data.iter_has_child(pie)) { -                Gtk.TreeIter child; -                this.data.iter_children(out child, pie); -                 -                do { -                    // get slice information -                    string slice_type; -                    string slice_icon; -                    string slice_name; -                    bool is_quick_action; -                     -                    this.data.get(child, DataPos.ICON, out slice_icon); -                    this.data.get(child, DataPos.NAME, out slice_name); -                    this.data.get(child, DataPos.ACTION_TYPE, out slice_type); -                    this.data.get(child, DataPos.IS_QUICKACTION, out is_quick_action); -                     -                    if (slice_type == typeof(AppAction).name()) { -                        string slice_command; -                        this.data.get(child, DataPos.DISPLAY_COMMAND_APP, out slice_command); -                        var group = new ActionGroup(new_pie.id); -                        group.add_action(new AppAction(slice_name, slice_icon, slice_command, is_quick_action)); -                        new_pie.add_group(group); -                    } else if (slice_type == typeof(KeyAction).name()) { -                        string slice_command; -                        this.data.get(child, DataPos.REAL_COMMAND_KEY, out slice_command); -                        var group = new ActionGroup(new_pie.id); -                        group.add_action(new KeyAction(slice_name, slice_icon, slice_command, is_quick_action)); -                        new_pie.add_group(group); -                    } else if (slice_type == typeof(PieAction).name()) { -                        string slice_command; -                        this.data.get(child, DataPos.REAL_COMMAND_PIE, out slice_command); -                        var group = new ActionGroup(new_pie.id); -                        group.add_action(new PieAction(slice_command, is_quick_action)); -                        new_pie.add_group(group); -                    } else if (slice_type == typeof(UriAction).name()) { -                        string slice_command; -                        this.data.get(child, DataPos.DISPLAY_COMMAND_URI, out slice_command); -                        var group = new ActionGroup(new_pie.id); -                        group.add_action(new UriAction(slice_name, slice_icon, slice_command, is_quick_action)); -                        new_pie.add_group(group); -                    } else if (slice_type == typeof(ActionGroup).name()) { -                        string slice_command; -                        this.data.get(child, DataPos.REAL_COMMAND_GROUP, out slice_command); -                         -                        var group = GLib.Object.new(GLib.Type.from_name(slice_command), "parent_id", new_pie.id); -                        new_pie.add_group(group as ActionGroup); -                    }  -                     -                } while (this.data.iter_next(ref child)); -            } -        }          +            var icon = new Icon(icon_name, 48); +            var pixbuf = icon.to_pixbuf(); +            Gtk.drag_set_icon_pixbuf(ctx, pixbuf, icon.size()/2, icon.size()/2); +        }      } -    // creates new action when the list receives a drag'n'drop event -    private void on_dnd_received(Gdk.DragContext context, int x, int y, Gtk.SelectionData selection_data, uint info, uint time_) { -        string[] uris = selection_data.get_uris(); -         +    ///////////////////////////////////////////////////////////////////// +    /// Called when something is dragged over this Widget. +    ///////////////////////////////////////////////////////////////////// +     +    private bool on_drag_move(Gdk.DragContext context, int x, int y, uint time) { +     +        Gtk.TreeViewDropPosition position;          Gtk.TreePath path; -        Gtk.TreeViewDropPosition pos; -        // check for valid position -        if (!this.get_dest_row_at_pos(x, y, out path, out pos) -            || (path.to_string() == "0" && pos == Gtk.TreeViewDropPosition.BEFORE)) { -             -            warning("Failed to insert Slice: Invalid location!"); -            return; -        } -         -        // get position to insert (when child: after, when parent: as first child) -        Gtk.TreeIter parent; -        int insert_pos = 0; -        if (path.get_depth() == 1) { -            if (pos == Gtk.TreeViewDropPosition.BEFORE) { -                path.prev(); -                this.data.get_iter(out parent, path); -                insert_pos = this.data.iter_n_children(parent); -            } else { -                this.data.get_iter(out parent, path); -            } -        } else { -            if (pos == Gtk.TreeViewDropPosition.BEFORE) { -                insert_pos = path.get_indices()[1]; -            } else { -                insert_pos = path.get_indices()[1]+1; -            } -             -            path.up(); -            this.data.get_iter(out parent, path); -        } +        if (!this.get_dest_row_at_pos(x, y, out path, out position)) +            return false; -        foreach (var uri in uris) { -            Gtk.TreeIter new_child; -            this.data.insert(out new_child, parent, insert_pos); -            this.write_action(ActionRegistry.new_for_uri(uri), new_child); -        } +        if (position == Gtk.TreeViewDropPosition.BEFORE) +            this.set_drag_dest_row(path, Gtk.TreeViewDropPosition.INTO_OR_BEFORE); +        else if (position == Gtk.TreeViewDropPosition.AFTER) +            this.set_drag_dest_row(path, Gtk.TreeViewDropPosition.INTO_OR_AFTER); + +        Gdk.drag_status(context, context.get_suggested_action(), time); -        this.update_pie(parent); -    } -     -    private void on_dnd_source(Gdk.DragContext context, Gtk.SelectionData selection_data, uint info, uint time_) { -        Gtk.TreeIter selected; -        if (this.get_selection().get_selected(null, out selected)) { -            string id = ""; -            this.data.get(selected, DataPos.ACTION_TYPE, out id); -            selection_data.set_uris({"file://" + Paths.launchers + "/" + id + ".desktop"}); -        } -    } -     -    private Gdk.Pixbuf load_icon(string name, int size) { -        Gdk.Pixbuf pixbuf = null; +        // avoid too frequent selection... +        this.last_hover = time; -        try { -            if (name.contains("/")) -                pixbuf = new Gdk.Pixbuf.from_file_at_size(name, size, size); -            else -                pixbuf = new Gdk.Pixbuf.from_file_at_size(Icon.get_icon_file(name, size), size, size); -        } catch (GLib.Error e) { -            warning(e.message); -        } +        GLib.Timeout.add(150, () => { +            if (this.last_hover == time) +                this.get_selection().select_path(path);  +            return false; +        }); -        return pixbuf; +        return true;      }  } 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); +        } +    } +    +} + +} diff --git a/src/gui/piePreviewAddSign.vala b/src/gui/piePreviewAddSign.vala new file mode 100644 index 0000000..ee8c14b --- /dev/null +++ b/src/gui/piePreviewAddSign.vala @@ -0,0 +1,220 @@ +/*  +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/>.  +*/ + +using GLib.Math; + +namespace GnomePie { + +/////////////////////////////////////////////////////////////////////////     +/// A liitle plus-sign displayed on the preview widget to indicate where +/// the user may add a new Slice. +///////////////////////////////////////////////////////////////////////// + +public class PiePreviewAddSign : GLib.Object { + +    ///////////////////////////////////////////////////////////////////// +    /// Gets emitted, when the users clicks on this object. +    ///////////////////////////////////////////////////////////////////// + +    public signal void on_clicked(int position); +     +    ///////////////////////////////////////////////////////////////////// +    /// The image used to display this oject. +    ///////////////////////////////////////////////////////////////////// +     +    public Image icon { get; private set; } +     +    ///////////////////////////////////////////////////////////////////// +    /// True, when the add sign is currently visible. +    ///////////////////////////////////////////////////////////////////// +     +    public bool visible { get; private set; default=false; } +     +    ///////////////////////////////////////////////////////////////////// +    /// The position of the sign in its parent Pie. May be 2.5 for +    /// example. +    ///////////////////////////////////////////////////////////////////// +     +    private double position = 0; +     +    ///////////////////////////////////////////////////////////////////// +    /// The parent renderer. +    ///////////////////////////////////////////////////////////////////// + +    private unowned PiePreviewRenderer parent;   +     +    ///////////////////////////////////////////////////////////////////// +    /// Some values used for displaying this sign. +    ///////////////////////////////////////////////////////////////////// +     +    private double time = 0; +    private double max_size = 0;  +    private double angle = 0;  +    private AnimatedValue size;  +    private AnimatedValue alpha;  +    private AnimatedValue activity;  +    private AnimatedValue clicked;  +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, sets everything up. +    ///////////////////////////////////////////////////////////////////// + +    public PiePreviewAddSign(PiePreviewRenderer parent) { +        this.parent = parent; +         +        this.size = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 0, 0, 0, 2.0); +        this.alpha = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 0, 0, 0, 0.0); +        this.activity = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, -3, -3, 0, 0.0); +        this.clicked = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 1, 1, 0, 0.0); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Loads the desired icon for this sign. +    ///////////////////////////////////////////////////////////////////// + +    public void load() { +        this.icon = new Icon("add", 36); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Updates the position where this object should be displayed. +    ///////////////////////////////////////////////////////////////////// + +    public void set_position(int position) { +        double new_position = position; +         +        if (!this.parent.drag_n_drop_mode) +            new_position += 0.5; + +        this.position = new_position; +        this.angle = 2.0 * PI * new_position/parent.slice_count(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes this object visible. +    ///////////////////////////////////////////////////////////////////// +     +    public void show() { +        this.visible = true; +        this.size.reset_target(this.max_size, 0.3);  +        this.alpha.reset_target(1.0, 0.3);    +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes this object invisible. +    ///////////////////////////////////////////////////////////////////// +     +    public void hide() { +        this.visible = false; +        this.size.reset_target(0.0, 0.3);  +        this.alpha.reset_target(0.0, 0.3);      +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Updates the size of this object. All transitions will be smooth. +    ///////////////////////////////////////////////////////////////////// + +    public void set_size(double size) { +        this.max_size = size; +        this.size.reset_target(size, 0.5); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Draws the sign to the given context. +    ///////////////////////////////////////////////////////////////////// + +    public void draw(double frame_time, Cairo.Context ctx) { + +        this.time += frame_time; +         +        this.size.update(frame_time); +        this.alpha.update(frame_time); +        this.activity.update(frame_time); +        this.clicked.update(frame_time); +         +        if (this.parent.slice_count() == 0) { +            ctx.save(); +             +            double scale = this.clicked.val  +                         + GLib.Math.sin(this.time*10)*0.02*this.alpha.val +                         + this.alpha.val*0.08 - 0.1; +            ctx.scale(scale, scale); +         +            // paint the image +            icon.paint_on(ctx); +                 +            ctx.restore(); +             +        } else if (this.alpha.val*this.activity.val > 0) { +            ctx.save(); +             +            // distance from the center +            double radius = 120; +             +            // transform the context +            ctx.translate(cos(this.angle)*radius, sin(this.angle)*radius); +            double scale = this.size.val*this.clicked.val  +                         + this.activity.val*0.07 +                         + GLib.Math.sin(this.time*10)*0.03*this.activity.val +                         - 0.1; +            ctx.scale(scale, scale); +         +            // paint the image +            icon.paint_on(ctx, this.alpha.val*this.activity.val); +                 +            ctx.restore(); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the mouse moves to another position. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_mouse_move(double angle) { +        double direction = 2.0 * PI * position/parent.slice_count(); +        double diff = fabs(angle-direction); +         +        if (diff > PI) +	        diff = 2 * PI - diff; +	     +	    if (diff < 0.5*PI/parent.slice_count()) this.activity.reset_target(1.0, 1.0); +        else                                    this.activity.reset_target(-3.0, 1.5); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a button of the mouse is pressed. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_button_press(double x, double y) { +        if (this.activity.end == 1.0) { +            this.clicked.reset_target(0.9, 0.1); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a button of the mouse is released. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_button_release(double x, double y) { +        if (this.clicked.end == 0.9) { +            this.clicked.reset_target(1.0, 0.1); +            this.on_clicked((int)this.position); +        } +    } +} + +} diff --git a/src/gui/piePreviewCenter.vala b/src/gui/piePreviewCenter.vala new file mode 100644 index 0000000..21bbd78 --- /dev/null +++ b/src/gui/piePreviewCenter.vala @@ -0,0 +1,108 @@ +/*  +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/>.  +*/ + +using GLib.Math; + +namespace GnomePie { + +/////////////////////////////////////////////////////////////////////////     +///  +///////////////////////////////////////////////////////////////////////// + +public class PiePreviewCenter : GLib.Object { +     +    ///////////////////////////////////////////////////////////////////// +    /// THe Images displayed. When the displayed text changes the +    /// currently displayed text becomes the old_text. So it's possible +    /// to create a smooth transitions. +    ///////////////////////////////////////////////////////////////////// +     +    private RenderedText text = null; +    private RenderedText old_text = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// Stores the currently displayed text in order to avoid frequent +    /// and useless updates. +    ///////////////////////////////////////////////////////////////////// +     +    private string current_text = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// An AnimatedValue for smooth transitions. +    ///////////////////////////////////////////////////////////////////// +     +    private AnimatedValue blend;  +     +    ///////////////////////////////////////////////////////////////////// +    /// The parent renderer. +    ///////////////////////////////////////////////////////////////////// +     +    private unowned PiePreviewRenderer parent;   +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, sets everything up. +    ///////////////////////////////////////////////////////////////////// +     +    public PiePreviewCenter(PiePreviewRenderer parent) { +        this.parent = parent; +        this.blend = new AnimatedValue.linear(0, 0, 0); +         +        this.text = new RenderedText("", 1, 1, "", new Color(), 1.0); +        this.old_text = text; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Updates the currently displayed text. It will be smoothly +    /// blended and may contain pango markup.  +    ///////////////////////////////////////////////////////////////////// +     +    public void set_text(string text) { +        if (text != this.current_text) { +             +            var style = new Gtk.Style(); +             +            this.old_text = this.text; +            this.text = new RenderedText.with_markup(text, 180, 180, style.font_desc.get_family()+" 10",  +                                                     new Color.from_gdk(style.fg[0]), 1.0); +            this.current_text = text; +             +            this.blend.reset_target(0.0, 0.0); +            this.blend.reset_target(1.0, 0.1); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Draws the center to the given context. +    ///////////////////////////////////////////////////////////////////// + +    public void draw(double frame_time, Cairo.Context ctx) { + +        this.blend.update(frame_time); +         +        ctx.save(); +         +        if (this.parent.slice_count() == 0)  +            ctx.translate(0, 40); +         +        this.old_text.paint_on(ctx, 1-this.blend.val); +        this.text.paint_on(ctx, this.blend.val); +             +        ctx.restore(); +    } +} + +} diff --git a/src/gui/piePreviewDeleteSign.vala b/src/gui/piePreviewDeleteSign.vala new file mode 100644 index 0000000..2ff3321 --- /dev/null +++ b/src/gui/piePreviewDeleteSign.vala @@ -0,0 +1,195 @@ +/*  +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/>.  +*/ + +using GLib.Math; + +namespace GnomePie { + +/////////////////////////////////////////////////////////////////////////     +/// The delete sign, displayed in the upper right corner of each +/// Slice. +///////////////////////////////////////////////////////////////////////// + +public class PiePreviewDeleteSign : GLib.Object { + +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user clicked on this sign. +    ///////////////////////////////////////////////////////////////////// + +    public signal void on_clicked(); +     +    ///////////////////////////////////////////////////////////////////// +    /// The image used to display this oject. +    ///////////////////////////////////////////////////////////////////// +     +    public Image icon { get; private set; } +     +    ///////////////////////////////////////////////////////////////////// +    /// Some constants determining the look and behaviour of this Slice. +    ///////////////////////////////////////////////////////////////////// +     +    private const int radius = 18; +    private const double globale_scale = 0.8; +    private const double click_cancel_treshold = 5; + +    ///////////////////////////////////////////////////////////////////// +    /// True, when the add sign is currently visible. +    ///////////////////////////////////////////////////////////////////// + +    private bool visible = false; +     +    ///////////////////////////////////////////////////////////////////// +    /// Some AnimatedValues for smooth transitions. +    ///////////////////////////////////////////////////////////////////// +     +    private AnimatedValue size; +    private AnimatedValue alpha;  +    private AnimatedValue activity;  +    private AnimatedValue clicked;  +     +    ///////////////////////////////////////////////////////////////////// +    /// Storing the position where a mouse click was executed. Useful for +    /// canceling the click when the mouse moves some pixels. +    ///////////////////////////////////////////////////////////////////// +     +    private double clicked_x = 0.0; +    private double clicked_y = 0.0; +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, sets everything up. +    ///////////////////////////////////////////////////////////////////// + +    public PiePreviewDeleteSign() { +        this.size = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 0, 0, 0, 2.0); +        this.alpha = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 0, 0, 0, 0.0); +        this.activity = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, -3, -3, 0, 0.0); +        this.clicked = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 1, 1, 0, 0.0); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Loads an Action. All members are initialized accordingly. +    ///////////////////////////////////////////////////////////////////// + +    public void load() { +        this.icon = new Icon("stock_delete", radius*2); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes this object visible. +    ///////////////////////////////////////////////////////////////////// +     +    public void show() { +        if (!this.visible) { +            this.visible = true; +            this.alpha.reset_target(1.0, 0.3);    +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes this object invisible. +    ///////////////////////////////////////////////////////////////////// +     +    public void hide() { +        if (this.visible) { +            this.visible = false; +            this.alpha.reset_target(0.0, 0.3);      +        } +    } + +    ///////////////////////////////////////////////////////////////////// +    /// Updates the size of this object. All transitions will be smooth. +    ///////////////////////////////////////////////////////////////////// + +    public void set_size(double size) { +        this.size.reset_target(size, 0.2); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Draws the sign to the given context. +    ///////////////////////////////////////////////////////////////////// + +    public void draw(double frame_time, Cairo.Context ctx) { +        this.size.update(frame_time); +        this.alpha.update(frame_time); +        this.activity.update(frame_time); +        this.clicked.update(frame_time); +         +        if (this.alpha.val > 0) { +            ctx.save(); +             +            // transform the context +            double scale = (this.size.val*this.clicked.val  +                         + this.activity.val*0.2 - 0.2)*globale_scale; +            ctx.scale(scale, scale); +         +            // paint the image +            icon.paint_on(ctx, this.alpha.val); +                 +            ctx.restore(); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the mouse moves to another position. +    ///////////////////////////////////////////////////////////////////// +     +    public bool on_mouse_move(double x, double y) { +        if (this.clicked.end == 0.9) { +            double dist = GLib.Math.pow(x-this.clicked_x, 2) + GLib.Math.pow(y-this.clicked_y, 2); +            if (dist > this.click_cancel_treshold*this.click_cancel_treshold) +                this.clicked.reset_target(1.0, 0.1); +        } +     +	    if (GLib.Math.fabs(x) <= radius*globale_scale && GLib.Math.fabs(y) <= radius*globale_scale) { +	        this.activity.reset_target(1.0, 0.2); +	        return true; +        }  +         +        this.activity.reset_target(0.0, 0.2); +        return false; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a button of the mouse is pressed. +    ///////////////////////////////////////////////////////////////////// +     +    public bool on_button_press(double x, double y) { +        if (this.activity.end == 1.0) { +            this.clicked.reset_target(0.9, 0.1); +            this.clicked_x = x; +            this.clicked_y = y; +            return true; +        } +        return false; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a button of the mouse is released. +    ///////////////////////////////////////////////////////////////////// +     +    public bool on_button_release(double x, double y) { +        if (this.clicked.end == 0.9) { +            this.clicked.reset_target(1.0, 0.1); +            this.on_clicked(); +             +            return true; +        } +        return false; +    } +} + +} diff --git a/src/gui/piePreviewRenderer.vala b/src/gui/piePreviewRenderer.vala new file mode 100644 index 0000000..1cf83ff --- /dev/null +++ b/src/gui/piePreviewRenderer.vala @@ -0,0 +1,436 @@ +/*  +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/>.  +*/ + +using GLib.Math; + +namespace GnomePie { + +/////////////////////////////////////////////////////////////////////////     +/// A complex class which is able to draw the preview of a Pie. It can +/// manipulate the displayed Pie as well. +///////////////////////////////////////////////////////////////////////// + +public class PiePreviewRenderer : GLib.Object { +     +    ///////////////////////////////////////////////////////////////////// +    /// These signals get emitted when a slice is added, removed or +    /// manipulated. +    ///////////////////////////////////////////////////////////////////// +     +    public signal void on_add_slice(int position); +    public signal void on_remove_slice(int position); +    public signal void on_edit_slice(int position); +     +    ///////////////////////////////////////////////////////////////////// +    /// True, when there is currently a drag going on. +    ///////////////////////////////////////////////////////////////////// +     +    public bool drag_n_drop_mode { get; private set; default=false; } +     +    ///////////////////////////////////////////////////////////////////// +    /// A list containing all SliceRenderers of this Pie. +    ///////////////////////////////////////////////////////////////////// +     +    public Gee.ArrayList<PiePreviewSliceRenderer?> slices; +     +    ///////////////////////////////////////////////////////////////////// +    /// When a Slice is moved within a Pie it is temporarily removed. +    /// If so, it is stored in this member. +    ///////////////////////////////////////////////////////////////////// +     +    public PiePreviewSliceRenderer hidden_group { get; private set; default=null; } +     +    ///////////////////////////////////////////////////////////////////// +    /// The add sign which indicates that a new Slice could be added. +    ///////////////////////////////////////////////////////////////////// +     +    private PiePreviewAddSign add_sign = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// The object which renders the name of the currently selected Slice +    /// in the middle. +    ///////////////////////////////////////////////////////////////////// +     +    private PiePreviewCenter center_renderer = null; +    private enum CenterDisplay { NONE, ACTIVE_SLICE, DROP, ADD, DELETE } +     +    ///////////////////////////////////////////////////////////////////// +    /// Some members storing some inter-frame-information. +    ///////////////////////////////////////////////////////////////////// + +    private int active_slice = -1; +    private double angle = 0.0;     +    private double mouse_x = 0.0; +    private double mouse_y = 0.0; +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, initializes members. +    ///////////////////////////////////////////////////////////////////// +     +    public PiePreviewRenderer() { +        this.slices = new Gee.ArrayList<PiePreviewSliceRenderer?>();  +        this.center_renderer = new PiePreviewCenter(this); +        this.add_sign = new PiePreviewAddSign(this); +        this.add_sign.load(); +         +        this.add_sign.on_clicked.connect((pos) => { +            this.on_add_slice(pos); +        }); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Loads an Pie. All members are initialized accordingly. +    ///////////////////////////////////////////////////////////////////// +     +    public void load_pie(Pie pie) { +        this.slices.clear(); +     +        foreach (var group in pie.action_groups) { +            var renderer = new PiePreviewSliceRenderer(this); +            renderer.load(group); +             +            this.add_slice_renderer(renderer); +            this.connect_siganls(renderer); +        } +         +        this.active_slice = -1; +        this.update_sizes(); +        this.update_positions(false); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Enables or disables the drag n dropn mode. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_dnd_mode(bool dnd) { +        if (this.drag_n_drop_mode != dnd) { +            this.drag_n_drop_mode = dnd; +            this.update_positions(); +            this.update_sizes(); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Returns the number of Slices. +    ///////////////////////////////////////////////////////////////////// +     +    public int slice_count() { +        if (this.drag_n_drop_mode && !(this.slices.size == 0))  +            return slices.size+1; +         +        return slices.size; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Returns the index of the currently hovered Slice. +    ///////////////////////////////////////////////////////////////////// +     +    public int get_active_slice() { +        if (this.slices.size == 0) +            return 0; +     +        if (this.drag_n_drop_mode) +            return (int)(this.angle/(2*PI)*this.slice_count() + 0.5) % this.slice_count(); +             +        return this.active_slice; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Returns the Icon of the currently hovered Slice. +    ///////////////////////////////////////////////////////////////////// +     +    public Icon get_active_icon() { +        if (this.active_slice >= 0 && this.active_slice < this.slices.size) +            return this.slices[this.active_slice].icon; +        else +            return new Icon("", 24); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Draws the entire Pie to the given context. +    ///////////////////////////////////////////////////////////////////// +     +    public void draw(double frame_time, Cairo.Context ctx) { +        this.add_sign.draw(frame_time, ctx); +        this.center_renderer.draw(frame_time, ctx); +         +        foreach (var slice in this.slices) +            slice.draw(frame_time, ctx); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the mouse leaves the drawing area of this renderer. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_mouse_leave() { +        this.add_sign.hide(); +        this.update_positions(); +        this.update_center(CenterDisplay.NONE); +         +        foreach (var slice in this.slices) +            slice.on_mouse_leave(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the mouse enters the drawing area of this renderer. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_mouse_enter() { +        this.add_sign.show(); +        this.update_positions(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the mouse moves in the drawing area of this renderer. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_mouse_move(double x, double y) { +        this.mouse_x = x; +        this.mouse_y = y; +         +        this.angle = acos(x/sqrt(x*x + y*y)); +        if (y < 0) this.angle = 2*PI - this.angle; +     +        if (!this.drag_n_drop_mode) +            this.active_slice = -1; +         +        bool delete_hovered = false; +         +        for (int i=0; i<this.slices.size; ++i) +            if (slices[i].on_mouse_move(this.angle, x, y) && !this.drag_n_drop_mode) { +                this.active_slice = i; +                delete_hovered = slices[i].delete_hovered; +            } +         +        if (this.drag_n_drop_mode)      this.update_center(CenterDisplay.DROP); +        else if (this.active_slice < 0) this.update_center(CenterDisplay.ADD); +        else if (delete_hovered)        this.update_center(CenterDisplay.DELETE); +        else                            this.update_center(CenterDisplay.ACTIVE_SLICE); +             +        this.add_sign.on_mouse_move(this.angle); +         +        this.update_positions(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a mouse button is pressed over this renderer. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_button_press() { +        for (int i=0; i<this.slices.size; ++i) +            this.slices[i].on_button_press(this.mouse_x, this.mouse_y); +        this.add_sign.on_button_press(this.mouse_x, this.mouse_y); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a mouse button is released over this renderer. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_button_release() { +        for (int i=0; i<this.slices.size; ++i) +            this.slices[i].on_button_release(this.mouse_x, this.mouse_y); +        this.add_sign.on_button_release(this.mouse_x, this.mouse_y); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Adds a new Slice to the renderer. +    ///////////////////////////////////////////////////////////////////// +     +    public void add_group(ActionGroup group, int at_position = -1) { +        var renderer = new PiePreviewSliceRenderer(this); +        renderer.load(group); +        this.add_slice_renderer(renderer, at_position); +        this.connect_siganls(renderer); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Removes a Slice from the renderer. +    ///////////////////////////////////////////////////////////////////// +     +    public void remove_group(int index) { +        if (this.slices.size > index) { +            this.slices.remove_at(index); +            this.update_positions(); +            this.update_sizes(); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Hides the Slice at the given position temporarily. +    ///////////////////////////////////////////////////////////////////// +     +    public void hide_group(int index) { +        if (this.slices.size > index) { +            this.hidden_group = this.slices[index]; +            this.remove_group(index); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Re-shows a Slice which has been hidden before. +    ///////////////////////////////////////////////////////////////////// +     +    public void show_hidden_group_at(int index) { +        if (this.slices.size >= index && this.hidden_group != null) { +            this.hidden_group.set_position(index, false); +            this.add_slice_renderer(this.hidden_group, index); +            this.hidden_group = null; +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Updates a Slice at the given position. +    ///////////////////////////////////////////////////////////////////// +     +    public void update_group(ActionGroup group, int index) { +        if (this.slices.size > index) { +            var renderer = new PiePreviewSliceRenderer(this); +            this.slices.set(index, renderer); +            renderer.load(group); +             +            this.connect_siganls(renderer); +             +            this.update_positions(false); +            this.update_sizes(); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Disables all quickactions of this pie preview. +    ///////////////////////////////////////////////////////////////////// +     +    public void disable_quickactions() { +        foreach (var slice in this.slices) +            slice.disable_quickactions(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Helper method which adds a new Slice to the given position. +    ///////////////////////////////////////////////////////////////////// +     +    private void add_slice_renderer(PiePreviewSliceRenderer renderer, int at_position = -1) { +        if (at_position < 0 || at_position >= this.slices.size) +            this.slices.add(renderer); +        else +            this.slices.insert(at_position, renderer); +         +        this.update_positions(false); +        this.update_sizes(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Helper method which connects all neccessary signals of a newly +    /// added Slice. +    ///////////////////////////////////////////////////////////////////// +     +    private void connect_siganls(PiePreviewSliceRenderer renderer) { +        renderer.on_clicked.connect((pos) => { +            this.on_edit_slice(pos); +        }); +         +        renderer.on_remove.connect((pos) => { +            this.on_remove_slice(pos); +        }); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Moves all slices to their positions. This may happen smoothly if +    /// desired. +    ///////////////////////////////////////////////////////////////////// +     +    private void update_positions(bool smoothly = true) { +        if (this.slices.size > 0) { +            if (this.add_sign.visible) { +                int add_position = 0; +                add_position = (int)(this.angle/(2*PI)*this.slice_count()) % this.slice_count(); +                this.add_sign.set_position(add_position); +                 +                for (int i=0; i<this.slices.size; ++i) { +                    this.slices[i].set_position(i, smoothly); +                } +             +            } else if (this.drag_n_drop_mode) { +                int add_position = 0; +                add_position = (int)(this.angle/(2*PI)*this.slice_count() + 0.5) % this.slice_count(); + +                for (int i=0; i<this.slices.size; ++i) { +                    this.slices[i].set_position(i >= add_position ? i+1 : i, smoothly); +                } +                 +                this.update_center(CenterDisplay.DROP); +                 +            } else { +                for (int i=0; i<this.slices.size; ++i) { +                    this.slices[i].set_position(i, smoothly); +                } +                 +                if (this.active_slice < 0)  this.update_center(CenterDisplay.NONE); +                else                        this.update_center(CenterDisplay.ACTIVE_SLICE); +            } +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Resizes all slices to their new sizes. This may happen smoothly  +    /// if desired. +    ///////////////////////////////////////////////////////////////////// +         +    private void update_sizes() { +        double size = 1.0; +        if (this.slice_count() > 20)     size = 0.5; +        else if (this.slice_count() > 8) size = 1.0 - (double)(this.slice_count() - 8)/24.0; +         +        this.add_sign.set_size(size); +         +        for (int i=0; i<this.slices.size; ++i)  +            this.slices[i].set_size(size); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Displays a new text in the middle of the preview. +    ///////////////////////////////////////////////////////////////////// +     +    private void update_center(CenterDisplay display) { +        switch (display) { +            case CenterDisplay.ACTIVE_SLICE: +                if (this.active_slice >= 0 && this.active_slice < this.slices.size) +                    this.center_renderer.set_text("<b>" + slices[this.active_slice].name + "</b>\n<small>"  +                                            + _("Click to edit") + "\n" + _("Drag to move") + "</small>"); +                break; +            case CenterDisplay.ADD: +                this.center_renderer.set_text("<small>" + _("Click to add a new Slice") + "</small>"); +                break; +            case CenterDisplay.DROP: +                if (hidden_group == null) +                    this.center_renderer.set_text("<small>" + _("Drop to add as new Slice") + "</small>"); +                else +                    this.center_renderer.set_text("<b>" + this.hidden_group.name + "</b>\n<small>" +                                            + _("Drop to move Slice") + "</small>"); +                break; +            case CenterDisplay.DELETE: +                if (this.active_slice >= 0 && this.active_slice < this.slices.size) +                    this.center_renderer.set_text("<b>" + slices[this.active_slice].name + "</b>\n<small>"  +                                            + _("Click to delete") + "\n" + _("Drag to move") + "</small>"); +                break; +            default: +                this.center_renderer.set_text(""); +                break; +        } +    } +} + +} diff --git a/src/gui/piePreviewSliceRenderer.vala b/src/gui/piePreviewSliceRenderer.vala new file mode 100644 index 0000000..af39c1f --- /dev/null +++ b/src/gui/piePreviewSliceRenderer.vala @@ -0,0 +1,276 @@ +/*  +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/>.  +*/ + +using GLib.Math; + +namespace GnomePie { + +/////////////////////////////////////////////////////////////////////////     +/// Displays the preview of a Slice. +///////////////////////////////////////////////////////////////////////// + +public class PiePreviewSliceRenderer : GLib.Object { + +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user clicked on this Slice. +    ///////////////////////////////////////////////////////////////////// + +    public signal void on_clicked(int position); +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user clicked on the delete sign. +    ///////////////////////////////////////////////////////////////////// +     +    public signal void on_remove(int position); +     +    ///////////////////////////////////////////////////////////////////// +    /// The image used to display this oject. +    ///////////////////////////////////////////////////////////////////// +     +    public Icon icon { get; private set; } +    public ActionGroup action_group { get; private set; } +    public string name { get; private set; default=""; } +    public bool delete_hovered { get; private set; default=false; } + +    ///////////////////////////////////////////////////////////////////// +    /// The parent renderer. +    ///////////////////////////////////////////////////////////////////// + +    private unowned PiePreviewRenderer parent;   +     +    ///////////////////////////////////////////////////////////////////// +    /// The delete sign, displayed in the upper right corner of each +    /// Slice. +    ///////////////////////////////////////////////////////////////////// +     +    private PiePreviewDeleteSign delete_sign = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// Some AnimatedValues for smooth transitions. +    ///////////////////////////////////////////////////////////////////// +     +    private AnimatedValue angle;  +    private AnimatedValue size;  +    private AnimatedValue activity;  +    private AnimatedValue clicked;  +     +    ///////////////////////////////////////////////////////////////////// +    /// Some constants determining the look and behaviour of this Slice. +    ///////////////////////////////////////////////////////////////////// +     +    private const double pie_radius = 126; +    private const double radius = 24; +    private const double delete_x = 13; +    private const double delete_y = -13; +    private const double click_cancel_treshold = 5; +     +    ///////////////////////////////////////////////////////////////////// +    /// Storing the position where a mouse click was executed. Useful for +    /// canceling the click when the mouse moves some pixels. +    ///////////////////////////////////////////////////////////////////// +     +    private double clicked_x = 0.0; +    private double clicked_y = 0.0; +     +    ///////////////////////////////////////////////////////////////////// +    /// The index of this slice in a pie. Clockwise assigned, starting +    /// from the right-most slice. +    ///////////////////////////////////////////////////////////////////// +     +    private int position; + +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, sets everything up. +    ///////////////////////////////////////////////////////////////////// + +    public PiePreviewSliceRenderer(PiePreviewRenderer parent) { +        this.delete_sign = new PiePreviewDeleteSign(); +        this.delete_sign.load(); +        this.delete_sign.on_clicked.connect(() => { +            this.on_remove(this.position); +        }); +     +        this.parent = parent; +        this.angle = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 0, 0, 0, 0.5); +        this.size = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 0, 0, 0, 1.0); +        this.activity = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 0, 0, 0, 0.0); +        this.clicked = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, 1, 1, 0, 1.0); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Loads an Action. All members are initialized accordingly. +    ///////////////////////////////////////////////////////////////////// + +    public void load(ActionGroup group) { +        this.action_group = group; +         +        // if it's a custom ActionGroup +        if (group.get_type().depth() == 2 && group.actions.size > 0) { +            this.icon = new Icon(group.actions[0].icon, (int)(radius*2)); +            this.name = group.actions[0].name; +        } else { +            this.icon = new Icon(GroupRegistry.descriptions[group.get_type().name()].icon, (int)(radius*2)); +            this.name = GroupRegistry.descriptions[group.get_type().name()].name; +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Updates the position where this object should be displayed. +    ///////////////////////////////////////////////////////////////////// + +    public void set_position(int position, bool smoothly = true) { +        double direction = 2.0 * PI * position/parent.slice_count(); +         +        if (direction != this.angle.end) { +            this.position = position; +            this.angle.reset_target(direction, smoothly ? 0.5 : 0.0); +             +            if (!smoothly) +                this.angle.update(1.0); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Updates the size of this object. All transitions will be smooth. +    ///////////////////////////////////////////////////////////////////// + +    public void set_size(double size) { +        this.size.reset_target(size, 0.5); +        this.delete_sign.set_size(size); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Notifies that all quick actions should be disabled. +    ///////////////////////////////////////////////////////////////////// +     +    public void disable_quickactions() { +        this.action_group.disable_quickactions(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Draws the slice to the given context. +    ///////////////////////////////////////////////////////////////////// + +    public void draw(double frame_time, Cairo.Context ctx) { +        this.size.update(frame_time); +        this.angle.update(frame_time); +        this.activity.update(frame_time); +        this.clicked.update(frame_time); + +        ctx.save(); +         +            // transform the context +            ctx.translate(cos(this.angle.val)*pie_radius, sin(this.angle.val)*pie_radius); +             +            double scale = this.size.val*this.clicked.val  +                         + this.activity.val*0.1 - 0.1; +            ctx.save();      +                         +                ctx.scale(scale, scale); +             +                // paint the image +                icon.paint_on(ctx); +             +            ctx.restore(); +             +            ctx.translate(delete_x*this.size.val, delete_y*this.size.val); +            this.delete_sign.draw(frame_time, ctx); +             +        ctx.restore(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the mouse moves to another position. +    ///////////////////////////////////////////////////////////////////// +     +    public bool on_mouse_move(double angle, double x, double y) { +        double direction = 2.0 * PI * position/parent.slice_count(); +        double diff = fabs(angle-direction); +         +        if (diff > PI) +	        diff = 2 * PI - diff; +	         +	    bool active = diff < 0.5*PI/parent.slice_count(); +	     +	    if (active) { +	        this.activity.reset_target(1.0, 0.3); +	        this.delete_sign.show(); +        } else { +            this.activity.reset_target(0.0, 0.3); +            this.delete_sign.hide(); +        }                                   +         +        if (this.clicked.end == 0.9) { +            double dist = GLib.Math.pow(x-this.clicked_x, 2) + GLib.Math.pow(y-this.clicked_y, 2); +            if (dist > this.click_cancel_treshold*this.click_cancel_treshold) +                this.clicked.reset_target(1.0, 0.1); +        } +         +        double own_x = cos(this.angle.val)*pie_radius; +        double own_y = sin(this.angle.val)*pie_radius; +        this.delete_hovered = this.delete_sign.on_mouse_move(x - own_x - delete_x*this.size.val,  +                                                             y - own_y - delete_y*this.size.val); +                                        +        return active; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the mouse leaves the area of this widget. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_mouse_leave() { +        this.activity.reset_target(0.0, 0.3); +        this.delete_sign.hide(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a button of the mouse is pressed. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_button_press(double x, double y) { +        bool delete_pressed = false; +        if (this.activity.end == 1.0) { +            double own_x = cos(this.angle.val)*pie_radius; +            double own_y = sin(this.angle.val)*pie_radius; +            delete_pressed = this.delete_sign.on_button_press(x - own_x - delete_x*this.size.val,  +                                                              y - own_y - delete_y*this.size.val); +        } +     +        if (!delete_pressed && this.activity.end == 1.0) { +            this.clicked.reset_target(0.9, 0.1); +            this.clicked_x = x; +            this.clicked_y = y; +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a button of the mouse is released. +    ///////////////////////////////////////////////////////////////////// +     +    public void on_button_release(double x, double y) { +        bool deleted = false; +        if (this.activity.end == 1.0) +            deleted = this.delete_sign.on_button_release(x, y); +         +        if (!deleted && this.clicked.end == 0.9) { +            this.clicked.reset_target(1.0, 0.1); +            this.on_clicked(this.position); +        } +    } +} + +} diff --git a/src/gui/preferences.vala b/src/gui/preferences.vala deleted file mode 100644 index 9444fac..0000000 --- a/src/gui/preferences.vala +++ /dev/null @@ -1,325 +0,0 @@ -/*  -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 { - -/////////////////////////////////////////////////////////////////////////     -/// The Gtk settings menu of Gnome-Pie. -///////////////////////////////////////////////////////////////////////// - -public class Preferences : Gtk.Window { -     -    ///////////////////////////////////////////////////////////////////// -    /// C'tor, constructs the whole dialog. Many thanks to the -    /// synapse-project, since some of this code is taken from there!  -    ///////////////////////////////////////////////////////////////////// -     -    public Preferences() { -        this.title = _("Gnome-Pie - Settings"); -        this.set_position(Gtk.WindowPosition.CENTER); -        this.set_size_request(550, 550); -        this.resizable = false; -        this.icon_name = "gnome-pie"; -        this.delete_event.connect(hide_on_delete); -         -        // main container -        var main_vbox = new Gtk.VBox(false, 12); -            main_vbox.border_width = 12; -            add(main_vbox); - -            // tab container -            var tabs = new Gtk.Notebook(); -             -                // general tab -                var general_tab = new Gtk.VBox(false, 6); -                    general_tab.border_width = 12; -                     -                    // behavior frame -                    var behavior_frame = new Gtk.Frame(null); -                        behavior_frame.set_shadow_type(Gtk.ShadowType.NONE); -                        var behavior_frame_label = new Gtk.Label(null); -                        behavior_frame_label.set_markup(Markup.printf_escaped ("<b>%s</b>", _("Behavior"))); -                        behavior_frame.set_label_widget(behavior_frame_label); - -                        var behavior_vbox = new Gtk.VBox (false, 6); -                        var align = new Gtk.Alignment (0.5f, 0.5f, 1.0f, 1.0f); -                        align.set_padding (6, 12, 12, 12); -                        align.add (behavior_vbox); -                        behavior_frame.add (align); - -                        // Autostart checkbox -                        var autostart = new Gtk.CheckButton.with_label (_("Startup on Login")); -                            autostart.tooltip_text = _("If checked, Gnome-Pie will start when you log in."); -                            autostart.active = Config.global.auto_start; -                            autostart.toggled.connect(autostart_toggled); -                            behavior_vbox.pack_start(autostart, false); - -                        // Indicator icon  -                        var indicator = new Gtk.CheckButton.with_label (_("Show Indicator")); -                            indicator.tooltip_text = _("If checked, an indicator for easy access of the settings menu is shown in your panel."); -                            indicator.active = Config.global.show_indicator; -                            indicator.toggled.connect(indicator_toggled); -                            behavior_vbox.pack_start(indicator, false); -                             -                        // Open Pies at Mouse -                        var open_at_mouse = new Gtk.CheckButton.with_label (_("Open Pies at Mouse")); -                            open_at_mouse.tooltip_text = _("If checked, pies will open at your pointer. Otherwise they'll pop up in the middle of the screen."); -                            open_at_mouse.active = Config.global.open_at_mouse; -                            open_at_mouse.toggled.connect(open_at_mouse_toggled); -                            behavior_vbox.pack_start(open_at_mouse, false); -                             -                        // Slider -                        var slider_hbox = new Gtk.HBox (false, 6); -                            behavior_vbox.pack_start(slider_hbox); -                             -                            var scale_label = new Gtk.Label(_("Global Scale")); -                                slider_hbox.pack_start(scale_label, false, false); -                             -                            var scale_slider = new Gtk.HScale.with_range(0.5, 2.0, 0.05); -                                scale_slider.set_value(Config.global.global_scale); -                                scale_slider.value_pos = Gtk.PositionType.RIGHT; -                                 -                                bool changing = false; -                                bool changed_again = false; - -                                scale_slider.value_changed.connect(() => { -                                    if (!changing) { -                                        changing = true; -                                        Timeout.add(300, () => { -                                            if (changed_again) { -                                                changed_again = false; -                                                return true; -                                            } - -                                            Config.global.global_scale = scale_slider.get_value(); -                                            Config.global.load_themes(Config.global.theme.name); -                                            changing = false; -                                            return false; -                                        }); -                                    } else { -                                        changed_again = true; -                                    } -                                }); -                                 -                                slider_hbox.pack_end(scale_slider, true, true); - -                    general_tab.pack_start (behavior_frame, false); -                     -                    // theme frame -                    var theme_frame = new Gtk.Frame(null); -                        theme_frame.set_shadow_type(Gtk.ShadowType.NONE); -                        var theme_frame_label = new Gtk.Label(null); -                        theme_frame_label.set_markup(Markup.printf_escaped("<b>%s</b>", _("Themes"))); -                        theme_frame.set_label_widget(theme_frame_label); -                         -                        // scrollable frame -                        var scroll = new Gtk.ScrolledWindow (null, null); -                            align = new Gtk.Alignment(0.5f, 0.5f, 1.0f, 1.0f); -                            align.set_padding(6, 12, 12, 12); -                            align.add(scroll); -                            theme_frame.add(align); - -                            scroll.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); -                            scroll.set_shadow_type (Gtk.ShadowType.IN); -                             -                            // themes list -                            var theme_list = new ThemeList(); -                                scroll.add(theme_list); - -                general_tab.pack_start (theme_frame, true, true); -                tabs.append_page(general_tab, new Gtk.Label(_("General"))); -                 -                // pies tab -                var pies_tab = new Gtk.VBox(false, 6); -                    pies_tab.border_width = 12; -                    tabs.append_page(pies_tab, new Gtk.Label(_("Pies"))); -                         -                    // scrollable frame -                    scroll = new Gtk.ScrolledWindow (null, null); -                        align = new Gtk.Alignment(0.5f, 0.5f, 1.0f, 1.0f); -                        align.add(scroll); -                        pies_tab.add(align); - -                        scroll.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC); -                        scroll.set_shadow_type (Gtk.ShadowType.IN); -                         -                        // pies list -                        var pie_list = new PieList(); -                            scroll.add(pie_list); -                         -                    // bottom box -                    var info_box = new Gtk.HBox (false, 6); -                     -                        // info image -                        var info_image = new Gtk.Image.from_stock (Gtk.Stock.INFO, Gtk.IconSize.MENU); -                            info_box.pack_start (info_image, false); - -                        // info label -                        var info_label = new TipViewer({ -                                _("You can right-click in the list for adding or removing entries."), -                                _("You can reset Gnome-Pie to its default options with the terminal command \"gnome-pie --reset\"."), -                                _("The radiobutton at the beginning of each slice-line indicates the QuickAction of the pie."), -                                _("Pies can be opened with the terminal command \"gnome-pie --open=ID\"."), -                                _("Feel free to visit Gnome-Pie's homepage at %s!").printf("<a href='http://gnome-pie.simonschneegans.de'>gnome-pie.simonschneegans.de</a>"), -                                _("You can drag'n'drop applications from your main menu to the list above."), -                                _("If you want to give some feedback, please write an e-mail to %s!").printf("<a href='mailto:code@simonschneegans.de'>code@simonschneegans.de</a>"), -                                _("You may drag'n'drop URLs and bookmarks from your internet browser to the list above."), -                                _("Bugs can be reported at %s!").printf("<a href='https://github.com/Simmesimme/Gnome-Pie'>Github</a>"), -                                _("It's possible to drag'n'drop files and folders from your file browser to the list above."), -                                _("It's recommended to keep your Pies small (at most 6-8 Slices). Else they will become hard to navigate."), -                                _("In order to create a launcher for a Pie, drag the Pie from the list to your desktop!") -                            }); -                            this.show.connect(info_label.start_slide_show); -                            this.hide.connect(info_label.stop_slide_show); -                             -                            info_box.pack_start (info_label); -                         -                        // down Button -                        var down_button = new Gtk.Button(); -                            down_button.tooltip_text = _("Moves the selected Slice down"); -                            down_button.sensitive = false; -                            var down_image = new Gtk.Image.from_stock (Gtk.Stock.GO_DOWN, Gtk.IconSize.LARGE_TOOLBAR); -                            down_button.add(down_image); -                            down_button.clicked.connect (() => { -                                pie_list.selection_down(); -                            }); - -                            info_box.pack_end(down_button, false, false); -                         -                        // up Button -                        var up_button = new Gtk.Button(); -                            up_button.tooltip_text = _("Moves the selected Slice up"); -                            up_button.sensitive = false; -                            var up_image = new Gtk.Image.from_stock (Gtk.Stock.GO_UP, Gtk.IconSize.LARGE_TOOLBAR); -                            up_button.add(up_image); -                            up_button.clicked.connect (() => { -                                pie_list.selection_up(); -                            }); -                             -                            info_box.pack_end(up_button, false, false); -                             -                        pie_list.get_selection().changed.connect(() => { -                            Gtk.TreeIter selected; -                            if (pie_list.get_selection().get_selected(null, out selected)) { -                                Gtk.TreePath path = pie_list.model.get_path(selected); -                                if (path.get_depth() == 1) { -                                    up_button.sensitive = false; -                                    down_button.sensitive = false; -                                } else { -                                    up_button.sensitive = true; -                                    down_button.sensitive = true; -                                     -                                    int child_pos = path.get_indices()[1]; - -                                    if (child_pos == 0) -                                        up_button.sensitive = false; -                                     -                                    path.up(); -                                    Gtk.TreeIter parent_iter; -                                    pie_list.model.get_iter(out parent_iter, path); -                                    if (child_pos == pie_list.model.iter_n_children(parent_iter)-1) -                                        down_button.sensitive = false; -                                     -                                } -                            } -                        }); -                         -                        pies_tab.pack_start (info_box, false); -                 -                main_vbox.pack_start(tabs); - -            // close button  -            var bbox = new Gtk.HButtonBox (); -                bbox.set_layout (Gtk.ButtonBoxStyle.END); -                var close_button = new Gtk.Button.from_stock(Gtk.Stock.CLOSE); -                close_button.clicked.connect (() => {  -                    hide(); -                }); -                bbox.pack_start (close_button); - -                main_vbox.pack_start(bbox, false); -                 -            main_vbox.show_all(); -             -        this.hide.connect(() => { -            // save settings on close -            Config.global.save(); -            Pies.save(); -        }); -    } - -    ///////////////////////////////////////////////////////////////////// -    /// Creates or deletes the autostart file. This code is inspired -    /// by project synapse as well. -    ///////////////////////////////////////////////////////////////////// -     -    private void autostart_toggled(Gtk.ToggleButton check_box) { -        bool active = check_box.active; -        if (!active && FileUtils.test(Paths.autostart, FileTest.EXISTS)) { -            // delete the autostart file -            FileUtils.remove (Paths.autostart); -        } -        else if (active && !FileUtils.test(Paths.autostart, FileTest.EXISTS)) { -            string autostart_entry =  -                "#!/usr/bin/env xdg-open\n" +  -                "[Desktop Entry]\n" + -                "Name=Gnome-Pie\n" + -                "Exec=gnome-pie\n" + -                "Encoding=UTF-8\n" + -                "Type=Application\n" + -                "X-GNOME-Autostart-enabled=true\n" + -                "Icon=gnome-pie\n"; - -            // create the autostart file -            string autostart_dir = GLib.Path.get_dirname(Paths.autostart); -            if (!FileUtils.test(autostart_dir, FileTest.EXISTS | FileTest.IS_DIR)) { -                DirUtils.create_with_parents(autostart_dir, 0755); -            } -             -            try { -                FileUtils.set_contents(Paths.autostart, autostart_entry); -                FileUtils.chmod(Paths.autostart, 0755); -            } catch (Error e) { -                var d = new Gtk.MessageDialog (this, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, -                                           "%s", e.message); -                d.run (); -                d.destroy (); -            } -        } -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Shows or hides the indicator. -    ///////////////////////////////////////////////////////////////////// -     -    private void indicator_toggled(Gtk.ToggleButton check_box) { -        var check = check_box as Gtk.CheckButton; -        Config.global.show_indicator = check.active; -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Toggles whether the Pies are shown at the mouse or in the middle -    /// of the screen. -    ///////////////////////////////////////////////////////////////////// -     -    private void open_at_mouse_toggled(Gtk.ToggleButton check_box) { -        var check = check_box as Gtk.CheckButton; -        Config.global.open_at_mouse = check.active; -    } -} - -} diff --git a/src/gui/preferencesWindow.vala b/src/gui/preferencesWindow.vala new file mode 100644 index 0000000..022e44b --- /dev/null +++ b/src/gui/preferencesWindow.vala @@ -0,0 +1,311 @@ +/*  +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 { + +/////////////////////////////////////////////////////////////////////////     +/// The settings menu of Gnome-Pie. +///////////////////////////////////////////////////////////////////////// + +public class PreferencesWindow : GLib.Object { +     +    ///////////////////////////////////////////////////////////////////// +    /// The ID of the currently selected Pie. +    ///////////////////////////////////////////////////////////////////// +     +    private string selected_id = ""; +     +    ///////////////////////////////////////////////////////////////////// +    /// Some Gtk widgets used by this window. +    ///////////////////////////////////////////////////////////////////// +     +    private Gtk.Window? window = null; +    private Gtk.Label? id_label = null; +    private Gtk.Label? name_label = null; +    private Gtk.Label? hotkey_label = null; +    private Gtk.Label? no_pie_label = null; +    private Gtk.Label? no_slice_label = null; +    private Gtk.VBox? preview_box = null; +    private Gtk.Image? icon = null; +    private Gtk.EventBox? preview_background = null; +    private Gtk.Button? icon_button = null; +    private Gtk.Button? name_button = null; +    private Gtk.Button? hotkey_button = null; +    private Gtk.ToolButton? remove_pie_button = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// Some custom widgets and dialogs used by this window. +    ///////////////////////////////////////////////////////////////////// +     +    private PiePreview? preview = null; +    private PieList? pie_list = null; +    private SettingsWindow? settings_window = null; +    private TriggerSelectWindow? trigger_window = null; +    private IconSelectWindow? icon_window = null; +    private RenameWindow? rename_window = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, creates the window. +    ///////////////////////////////////////////////////////////////////// +     +    public PreferencesWindow() { +        try { +            var builder = new Gtk.Builder(); + +            builder.add_from_file (Paths.ui_files + "/preferences.ui"); + +            this.window = builder.get_object("window") as Gtk.Window; +             +            this.window.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK | +                        Gdk.EventMask.KEY_RELEASE_MASK | +                        Gdk.EventMask.KEY_PRESS_MASK | +                        Gdk.EventMask.POINTER_MOTION_MASK); +             +            #if HAVE_GTK_3 +                var toolbar = builder.get_object ("toolbar") as Gtk.Widget; +                toolbar.get_style_context().add_class("primary-toolbar"); +                 +                var inline_toolbar = builder.get_object ("pies-toolbar") as Gtk.Widget; +                inline_toolbar.get_style_context().add_class("inline-toolbar"); +            #endif +             +            this.pie_list = new PieList(); +            this.pie_list.on_select.connect(this.on_pie_select); +             +            var scroll_area = builder.get_object("pies-scrolledwindow") as Gtk.ScrolledWindow; +            scroll_area.add(this.pie_list); +             +            this.preview = new PiePreview(); +            this.preview.on_first_slice_added.connect(() => { +                this.no_slice_label.hide(); +            }); +             +            this.preview.on_last_slice_removed.connect(() => { +                this.no_slice_label.show(); +            }); +             +            preview_box = builder.get_object("preview-box") as Gtk.VBox; +            this.preview_box.pack_start(preview, true, true); +             +            this.id_label = builder.get_object("id-label") as Gtk.Label; +            this.name_label = builder.get_object("pie-name-label") as Gtk.Label; +            this.hotkey_label = builder.get_object("hotkey-label") as Gtk.Label; +            this.no_pie_label = builder.get_object("no-pie-label") as Gtk.Label; +            this.no_slice_label = builder.get_object("no-slice-label") as Gtk.Label; +            this.icon = builder.get_object("icon") as Gtk.Image; +            this.preview_background = builder.get_object("preview-background") as Gtk.EventBox; +                     +            (builder.get_object("settings-button") as Gtk.ToolButton).clicked.connect(on_settings_button_clicked); +             +            this.hotkey_button = builder.get_object("key-button") as Gtk.Button; +            this.hotkey_button.clicked.connect(on_key_button_clicked); +             +            this.icon_button = builder.get_object("icon-button") as Gtk.Button; +            this.icon_button.clicked.connect(on_icon_button_clicked); +             +            this.name_button = builder.get_object("rename-button") as Gtk.Button; +            this.name_button.clicked.connect(on_rename_button_clicked); +             +            this.remove_pie_button = builder.get_object("remove-pie-button") as Gtk.ToolButton; +            this.remove_pie_button.clicked.connect(on_remove_pie_button_clicked); +             +            (builder.get_object("add-pie-button") as Gtk.ToolButton).clicked.connect(on_add_pie_button_clicked); +             +            this.window.hide.connect(() => { +                // save settings on close +                Config.global.save(); +                Pies.save(); +            }); +             +            this.window.delete_event.connect(this.window.hide_on_delete); +                 +        } catch (GLib.Error e) { +            error("Could not load UI: %s\n", e.message); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Shows the window. +    ///////////////////////////////////////////////////////////////////// +     +    public void show() { +        this.preview.draw_loop(); +        this.window.show_all(); +        this.pie_list.select_first(); +        this.preview_background.modify_bg(Gtk.StateType.NORMAL, Gtk.rc_get_style(this.window).light[0]); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when a new Pie is selected in the PieList. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_pie_select(string id) { +        selected_id = id; +         +        this.no_slice_label.hide(); +        this.no_pie_label.hide(); +        this.preview_box.hide(); +         +        this.name_button.sensitive = false; +        this.hotkey_button.sensitive = false; +        this.icon_button.sensitive = false; +        this.remove_pie_button.sensitive = false; +         +        if (id == "") { +            this.id_label.label = ""; +            this.name_label.label = _("No Pie selected."); +            this.hotkey_label.set_markup(""); +            this.icon.icon_name = "application-default-icon"; + +            this.no_pie_label.show(); +        } else { +            var pie = PieManager.all_pies[selected_id]; +            this.id_label.label = ("ID: %s").printf(pie.id); +            this.name_label.label = PieManager.get_name_of(pie.id); +            this.hotkey_label.set_markup(PieManager.get_accelerator_label_of(pie.id)); +             +            if (pie.icon.contains("/")) +                try { +                    this.icon.pixbuf = new Gdk.Pixbuf.from_file_at_scale(pie.icon,  +                                                                         this.icon.get_pixel_size(),  +                                                                         this.icon.get_pixel_size(),  +                                                                         true); +                } catch (GLib.Error error) { +                    warning(error.message); +                } +            else +                this.icon.icon_name = pie.icon; +             +            this.preview.set_pie(id); +            this.preview_box.show(); +             +            if (pie.action_groups.size == 0) { +                this.no_slice_label.show(); +            } +             +            this.name_button.sensitive = true; +            this.hotkey_button.sensitive = true; +            this.icon_button.sensitive = true; +            this.remove_pie_button.sensitive = true; +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the add Pie button is clicked. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_add_pie_button_clicked(Gtk.ToolButton button) { +        var new_pie = PieManager.create_persistent_pie(_("New Pie"), "application-default-icon", null); +        this.pie_list.reload_all(); +        this.pie_list.select(new_pie.id); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the remove Pie button is clicked. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_remove_pie_button_clicked(Gtk.ToolButton button) { +        if (this.selected_id != "") { +            var dialog = new Gtk.MessageDialog((Gtk.Window)this.window.get_toplevel(), Gtk.DialogFlags.MODAL, +                         Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, +                         _("Do you really want to delete the selected Pie with all contained Slices?")); +                                                      +            dialog.response.connect((response) => { +                if (response == Gtk.ResponseType.YES) { +                    PieManager.remove_pie(selected_id); +                    this.pie_list.reload_all(); +                    this.pie_list.select_first(); +                } +            }); +             +            dialog.run(); +            dialog.destroy(); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when rename Pie button is clicked. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_rename_button_clicked(Gtk.Button button) { +        if (this.rename_window == null) { +            this.rename_window = new RenameWindow(); +            this.rename_window.set_parent(window); +            this.rename_window.on_ok.connect((name) => { +                var pie = PieManager.all_pies[selected_id]; +                pie.name = name; +                PieManager.create_launcher(pie.id); +                this.name_label.label = name; +                this.pie_list.reload_all(); +            }); +        } +         +        this.rename_window.set_pie(selected_id); +        this.rename_window.show(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the hotkey button is clicked. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_key_button_clicked(Gtk.Button button) { +        if (this.trigger_window == null) { +            this.trigger_window = new TriggerSelectWindow(); +            this.trigger_window.set_parent(window); +            this.trigger_window.on_ok.connect((trigger) => { +                PieManager.bind_trigger(trigger, selected_id); +                this.hotkey_label.set_markup(trigger.label_with_specials); +            }); +        } +         +        this.trigger_window.set_pie(selected_id); +        this.trigger_window.show(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the general settings button is clicked. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_settings_button_clicked(Gtk.ToolButton button) { +        if (this.settings_window == null) { +            this.settings_window = new SettingsWindow(); +            this.settings_window.set_parent(this.window.get_toplevel() as Gtk.Window); +        } +         +        this.settings_window.show(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the icon button is clicked. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_icon_button_clicked(Gtk.Button button) { +        if (this.icon_window == null) { +            this.icon_window = new IconSelectWindow(this.window); +            this.icon_window.on_ok.connect((icon) => { +                var pie = PieManager.all_pies[selected_id]; +                pie.icon = icon; +                PieManager.create_launcher(pie.id); +                this.pie_list.reload_all(); +            }); +        } +         +        this.icon_window.show(); +    } +} + +} diff --git a/src/gui/renameWindow.vala b/src/gui/renameWindow.vala new file mode 100644 index 0000000..389b460 --- /dev/null +++ b/src/gui/renameWindow.vala @@ -0,0 +1,109 @@ +/*  +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 window which allows selection of a new name for a Pie. +///////////////////////////////////////////////////////////////////////// + +public class RenameWindow : GLib.Object { +     +    ///////////////////////////////////////////////////////////////////// +    /// Gets emitted when the user selects a new name. +    ///////////////////////////////////////////////////////////////////// +     +    public signal void on_ok(string new_name); +     +    ///////////////////////////////////////////////////////////////////// +    /// Some Widgets used by this dialog. +    ///////////////////////////////////////////////////////////////////// +     +    private Gtk.Dialog window = null; +    private Gtk.Entry entry = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, constructs the Widget. +    ///////////////////////////////////////////////////////////////////// +     +    public RenameWindow() { +        try { +         +            Gtk.Builder builder = new Gtk.Builder(); + +            builder.add_from_file (Paths.ui_files + "/rename_pie.ui"); + +            window = builder.get_object("window") as Gtk.Dialog; +            entry = builder.get_object("name-entry") as Gtk.Entry; +             +            entry.activate.connect(this.on_ok_button_clicked); +             +            (builder.get_object("ok-button") as Gtk.Button).clicked.connect(on_ok_button_clicked); +            (builder.get_object("cancel-button") as Gtk.Button).clicked.connect(on_cancel_button_clicked); +             +            this.window.delete_event.connect(this.window.hide_on_delete); +                 +        } catch (GLib.Error e) { +            error("Could not load UI: %s\n", e.message); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Sets the parent window, in order to make this window stay in +    /// front. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_parent(Gtk.Window parent) { +        this.window.set_transient_for(parent); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Displays the window on the screen. +    ///////////////////////////////////////////////////////////////////// +     +    public void show() { +        this.window.show_all(); +        this.entry.is_focus = true; +    }   +     +    ///////////////////////////////////////////////////////////////////// +    /// Make the text entry display the name of the Pie with given ID. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_pie(string id) { +        entry.text = PieManager.get_name_of(id); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the ok button is pressed. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_ok_button_clicked() { +        this.on_ok(entry.text); +        this.window.hide(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the cancel button is pressed. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_cancel_button_clicked() { +        this.window.hide(); +    } +} + +} diff --git a/src/gui/settingsWindow.vala b/src/gui/settingsWindow.vala new file mode 100644 index 0000000..1eaa0b4 --- /dev/null +++ b/src/gui/settingsWindow.vala @@ -0,0 +1,174 @@ +/*  +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 { + +/////////////////////////////////////////////////////////////////////////     +/// The settings menu of Gnome-Pie, with options for theme switching and +/// some general options. +///////////////////////////////////////////////////////////////////////// + +public class SettingsWindow : GLib.Object { +     +    ///////////////////////////////////////////////////////////////////// +    /// Some widgets. +    ///////////////////////////////////////////////////////////////////// +     +    private Gtk.Dialog? window = null; +    private ThemeList? theme_list = null; +    private Gtk.ToggleButton? indicator = null; +    private Gtk.ToggleButton? autostart = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor creates, the dialog. +    ///////////////////////////////////////////////////////////////////// +     +    public SettingsWindow() { +        try { +         +            Gtk.Builder builder = new Gtk.Builder(); + +            builder.add_from_file (Paths.ui_files + "/settings.ui"); + +            this.window = builder.get_object("window") as Gtk.Dialog; +             +            this.theme_list = new ThemeList(); +             +            var scroll_area = builder.get_object("theme-scrolledwindow") as Gtk.ScrolledWindow; +                scroll_area.add(this.theme_list); +                 +            (builder.get_object("close-button") as Gtk.Button).clicked.connect(on_close_button_clicked); +             +            this.autostart = (builder.get_object("autostart-checkbox") as Gtk.ToggleButton); +            this.autostart.toggled.connect(on_autostart_toggled); +             +            this.indicator = (builder.get_object("indicator-checkbox") as Gtk.ToggleButton); +            this.indicator.toggled.connect(on_indicator_toggled); +             +            var scale_slider = (builder.get_object("scale-hscale") as Gtk.HScale); +                scale_slider.set_range(0.5, 2.0); +                scale_slider.set_increments(0.05, 0.25); +                scale_slider.set_value(Config.global.global_scale); +                 +                bool changing = false; +                bool changed_again = false; + +                scale_slider.value_changed.connect(() => { +                    if (!changing) { +                        changing = true; +                        Timeout.add(300, () => { +                            if (changed_again) { +                                changed_again = false; +                                return true; +                            } + +                            Config.global.global_scale = scale_slider.get_value(); +                            Config.global.load_themes(Config.global.theme.name); +                            changing = false; +                            return false; +                        }); +                    } else { +                        changed_again = true; +                    } +                }); +                 +            this.window.delete_event.connect(this.window.hide_on_delete); +                 +        } catch (GLib.Error e) { +            error("Could not load UI: %s\n", e.message); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Sets the parent window, in order to make this window stay in +    /// front. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_parent(Gtk.Window parent) { +        this.window.set_transient_for(parent); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Displays the window on the screen. +    ///////////////////////////////////////////////////////////////////// +     +    public void show() { +        this.indicator.active = Config.global.show_indicator; +        this.autostart.active = Config.global.auto_start; +     +        this.window.show_all();  +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the close button is clicked. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_close_button_clicked() { +        this.window.hide(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Creates or deletes the autostart file. This code is inspired +    /// by project synapse as well. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_autostart_toggled(Gtk.ToggleButton check_box) { +        bool active = check_box.active; +        if (!active && FileUtils.test(Paths.autostart, FileTest.EXISTS)) { +            // delete the autostart file +            FileUtils.remove (Paths.autostart); +        } +        else if (active && !FileUtils.test(Paths.autostart, FileTest.EXISTS)) { +            string autostart_entry =  +                "#!/usr/bin/env xdg-open\n" +  +                "[Desktop Entry]\n" + +                "Name=Gnome-Pie\n" + +                "Exec=gnome-pie\n" + +                "Encoding=UTF-8\n" + +                "Type=Application\n" + +                "X-GNOME-Autostart-enabled=true\n" + +                "Icon=gnome-pie\n"; + +            // create the autostart file +            string autostart_dir = GLib.Path.get_dirname(Paths.autostart); +            if (!FileUtils.test(autostart_dir, FileTest.EXISTS | FileTest.IS_DIR)) { +                DirUtils.create_with_parents(autostart_dir, 0755); +            } +             +            try { +                FileUtils.set_contents(Paths.autostart, autostart_entry); +                FileUtils.chmod(Paths.autostart, 0755); +            } catch (Error e) { +                var d = new Gtk.MessageDialog (this.window, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, +                                           "%s", e.message); +                d.run (); +                d.destroy (); +            } +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Shows or hides the indicator. +    ///////////////////////////////////////////////////////////////////// +     +    private void on_indicator_toggled(Gtk.ToggleButton check_box) { +        var check = check_box as Gtk.CheckButton; +        Config.global.show_indicator = check.active; +    } +} + +} diff --git a/src/gui/sliceTypeList.vala b/src/gui/sliceTypeList.vala new file mode 100644 index 0000000..541658e --- /dev/null +++ b/src/gui/sliceTypeList.vala @@ -0,0 +1,172 @@ +/*  +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 list displaying all available Action types and ActionGroup types. +///////////////////////////////////////////////////////////////////////// + +class SliceTypeList : Gtk.TreeView { + +    ///////////////////////////////////////////////////////////////////// +    /// This signal gets emitted when the user selects a new Type. +    ///////////////////////////////////////////////////////////////////// +     +    public signal void on_select(string id, string icon_name); +     +    ///////////////////////////////////////////////////////////////////// +    /// The listore which staroes all types internally. +    ///////////////////////////////////////////////////////////////////// +     +    private Gtk.ListStore data; +    private enum DataPos {ICON, ICON_NAME, NAME, ID} + +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, constructs the Widget. +    ///////////////////////////////////////////////////////////////////// + +    public SliceTypeList() { +        GLib.Object(); +         +        this.data = new Gtk.ListStore(4, typeof(Gdk.Pixbuf),    +                                         typeof(string), +                                         typeof(string), +                                         typeof(string)); +                                          +        this.data.set_sort_column_id(2, Gtk.SortType.ASCENDING); +         +        base.set_model(this.data); +        base.set_headers_visible(true); +        base.set_grid_lines(Gtk.TreeViewGridLines.NONE); +        this.set_fixed_height_mode(true); +         +        var main_column = new Gtk.TreeViewColumn(); +            main_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED); +            main_column.title = _("Slice types"); +            var icon_render = new Gtk.CellRendererPixbuf(); +                main_column.pack_start(icon_render, false); +         +            var name_render = new Gtk.CellRendererText(); +                main_column.pack_start(name_render, true); +         +        base.append_column(main_column); +         +        main_column.add_attribute(icon_render, "pixbuf", DataPos.ICON); +        main_column.add_attribute(name_render, "markup", DataPos.NAME); +         +        this.get_selection().changed.connect(() => { +            Gtk.TreeIter active; +            if (this.get_selection().get_selected(null, out active)) { +                string id = ""; +                string icon = ""; +                this.data.get(active, DataPos.ID, out id); +                this.data.get(active, DataPos.ICON_NAME, out icon); +                this.on_select(id, icon); +            } +        }); +         +        reload_all(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Loads a registered actions and action groups. +    ///////////////////////////////////////////////////////////////////// +     +    public void reload_all() { +        Gtk.TreeIter active; +        string current_id = ""; +        if (this.get_selection().get_selected(null, out active)) +            this.data.get(active, DataPos.ID, out current_id); +     +        data.clear(); +         +        foreach (var action_type in ActionRegistry.types) { +            var description = ActionRegistry.descriptions[action_type]; +             +            Gtk.TreeIter current; +            data.append(out current); +            var icon = new Icon(description.icon, 36); +            data.set(current, DataPos.ICON, icon.to_pixbuf());  +            data.set(current, DataPos.ICON_NAME, description.icon);  +            data.set(current, DataPos.NAME, "<b>" + description.name + "</b>\n" +                                 + "<small>" + description.description + "</small>");  +            data.set(current, DataPos.ID, description.id);  +        } +         +        foreach (var group_type in GroupRegistry.types) { +            var description = GroupRegistry.descriptions[group_type]; +             +            Gtk.TreeIter current; +            data.append(out current); +            var icon = new Icon(description.icon, 36); +            data.set(current, DataPos.ICON, icon.to_pixbuf());  +            data.set(current, DataPos.ICON_NAME, description.icon); +            data.set(current, DataPos.NAME, "<b>" + description.name + "</b>\n" +                                 + "<small>" + description.description + "</small>");  +            data.set(current, DataPos.ID, description.id);  +        } +         +        select_first(); +        select(current_id); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Selects the first type in the list. +    ///////////////////////////////////////////////////////////////////// +     +    public void select_first() { +        Gtk.TreeIter active; +         +        if(this.data.get_iter_first(out active) ) { +            this.get_selection().select_iter(active); +            string id = ""; +            string icon = ""; +            this.data.get(active, DataPos.ID, out id); +            this.data.get(active, DataPos.ICON_NAME, out icon); +            this.on_select(id, icon); +        } else { +            this.on_select("", "application-default-icon"); +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Select the given slice type. +    ///////////////////////////////////////////////////////////////////// +     +    public void select(string id) { +        this.data.foreach((model, path, iter) => { +            string pie_id; +            this.data.get(iter, DataPos.ID, out pie_id); +             +            if (id == pie_id) { +                this.get_selection().select_iter(iter); +                string icon = ""; +                this.data.get(iter, DataPos.ICON_NAME, out icon); +                this.on_select(pie_id, icon); +                this.scroll_to_cell(path, null, true, 0.5f, 0.5f); +                this.has_focus = true; +                 +                return true; +            } +             +            return false; +        }); +    } +} + +} diff --git a/src/gui/themeList.vala b/src/gui/themeList.vala index 7eadcdb..62e0721 100644 --- a/src/gui/themeList.vala +++ b/src/gui/themeList.vala @@ -28,6 +28,12 @@ class ThemeList : Gtk.TreeView {      /////////////////////////////////////////////////////////////////////      private Gtk.TreeIter active { private get; private set; } +     +    ///////////////////////////////////////////////////////////////////// +    /// The positions in the data list store. +    ///////////////////////////////////////////////////////////////////// +     +    private enum DataPos {ICON, NAME}      /////////////////////////////////////////////////////////////////////      /// C'tor, constructs the Widget. @@ -36,58 +42,50 @@ class ThemeList : Gtk.TreeView {      public ThemeList() {          GLib.Object(); -        var data = new Gtk.ListStore(2, typeof(bool),    // selected -                                        typeof(string)); // description -        base.set_model(data); -        base.set_headers_visible(false); -        base.set_rules_hint(true); -        base.set_grid_lines(Gtk.TreeViewGridLines.NONE); +        var data = new Gtk.ListStore(2, typeof(Gdk.Pixbuf),  +                                        typeof(string)); +        this.set_model(data); +        this.set_headers_visible(true); +        this.set_grid_lines(Gtk.TreeViewGridLines.NONE); +        this.set_fixed_height_mode(true);          var main_column = new Gtk.TreeViewColumn(); -            var check_render = new Gtk.CellRendererToggle(); -                check_render.set_radio(true); -                check_render.set_activatable(true); -                main_column.pack_start(check_render, false); -                 -                // switch the theme if the entry has been toggled -                check_render.toggled.connect((r, path) => { -                    Gtk.TreeIter toggled; -                    data.get_iter(out toggled, new Gtk.TreePath.from_string(path)); -                     -                    if (toggled != this.active) { -                        Timeout.add(10, () => { -                            int index = int.parse(path); -                            Config.global.theme = Config.global.themes[index]; -                            Config.global.theme.load(); -                            Config.global.theme.load_images(); -                            return false; -                        }); -                         -                        data.set(this.active, 0, false);  -                        data.set(toggled, 0, true); -                         -                        this.active = toggled; -                    } -                }); +            main_column.title = _("Themes"); +            main_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED); +            var icon_render = new Gtk.CellRendererPixbuf(); +                main_column.pack_start(icon_render, false);              var theme_render = new Gtk.CellRendererText();                  main_column.pack_start(theme_render, true); -        base.append_column(main_column); +        this.append_column(main_column); +         +        main_column.add_attribute(icon_render, "pixbuf", DataPos.ICON); +        main_column.add_attribute(theme_render, "markup", DataPos.NAME); -        main_column.add_attribute(check_render, "active", 0); -        main_column.add_attribute(theme_render, "markup", 1); +        this.get_selection().changed.connect(() => { +            Gtk.TreeIter active; +            if (this.get_selection().get_selected(null, out active)) { +                Timeout.add(10, () => { +                    int index = int.parse(data.get_path(active).to_string()); +                    Config.global.theme = Config.global.themes[index]; +                    Config.global.theme.load(); +                    Config.global.theme.load_images(); +                    return false; +                });   +            } +        });          // load all themes into the list          var themes = Config.global.themes;          foreach(var theme in themes) {              Gtk.TreeIter current;              data.append(out current); -            data.set(current, 0, theme == Config.global.theme);  -            data.set(current, 1, "<b>" + theme.name + "</b>\n" + theme.description -                                 + "  <small> - " + _("by") + " " + theme.author + "</small>");  +            data.set(current, DataPos.ICON, theme.preview_icon.to_pixbuf());  +            data.set(current, DataPos.NAME, "<b>"+theme.name+"</b><small>  -  "+theme.description+"\n" +                                           +"<i>"+_("By")+" "+theme.author+"</i></small>");               if(theme == Config.global.theme) -                this.active = current; +                get_selection().select_iter(current);          }        }  } diff --git a/src/gui/tipViewer.vala b/src/gui/tipViewer.vala deleted file mode 100644 index c653dd9..0000000 --- a/src/gui/tipViewer.vala +++ /dev/null @@ -1,172 +0,0 @@ -/*  -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 widget showing tips. The tips are beautifully faded in and out. -///////////////////////////////////////////////////////////////////////// - -public class TipViewer : Gtk.Label { - -    ///////////////////////////////////////////////////////////////////// -    /// Some settings tweaking the behavior of the TipViewer. -    ///////////////////////////////////////////////////////////////////// - -    private const double fade_time = 0.5; -    private const double frame_rate = 20.0; -    private const double delay = 7.0; - -    ///////////////////////////////////////////////////////////////////// -    /// False, if the playback of tips is stopped. -    ///////////////////////////////////////////////////////////////////// -     -    private bool playing = false; -     -    ///////////////////////////////////////////////////////////////////// -    /// An array containing all tips. -    ///////////////////////////////////////////////////////////////////// -     -    private string[] tips; -     -    ///////////////////////////////////////////////////////////////////// -    /// The index of the currently displayed tip. -    ///////////////////////////////////////////////////////////////////// -     -    private int index = -1; -     -    ///////////////////////////////////////////////////////////////////// -    /// Colors of the font and the background. Used for fading effects. -    ///////////////////////////////////////////////////////////////////// -     -    private Gdk.Color fg; -    private Gdk.Color bg; -     -    ///////////////////////////////////////////////////////////////////// -    /// The fading value. -    ///////////////////////////////////////////////////////////////////// -     -    private AnimatedValue alpha; -     -    ///////////////////////////////////////////////////////////////////// -    /// C'tor, initializes all members and sets the basic layout. -    ///////////////////////////////////////////////////////////////////// -     -    public TipViewer(string[] tips) { -        this.tips = tips; -        this.fg = this.get_style().fg[0]; -        this.bg = this.get_style().bg[0]; -         -        this.alpha = new AnimatedValue.linear(1.0, 0.0, this.fade_time); -         -        this.set_alignment (0.0f, 0.5f); -        this.wrap = true; -        this.set_use_markup(true); -        this.modify_font(Pango.FontDescription.from_string("9")); -         -        this.set_random_tip(); -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Starts the playback of tips. -    ///////////////////////////////////////////////////////////////////// -     -    public void start_slide_show() { -        if (!this.playing && tips.length > 1) { -            this.playing = true; -            GLib.Timeout.add((uint)(this.delay*1000.0), () => { -                this.fade_out(); -                 -                GLib.Timeout.add((uint)(1000.0*this.fade_time), () => { -                    this.set_random_tip(); -                    this.fade_in(); -                    return false; -                }); -                 -                return this.playing; -            }); -        } -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Stops the playback of tips. -    ///////////////////////////////////////////////////////////////////// -     -    public void stop_slide_show() { -        this.playing = false; -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Starts the fading in. -    ///////////////////////////////////////////////////////////////////// -     -    private void fade_in() { -        this.alpha = new AnimatedValue.linear(this.alpha.val, 1.0, this.fade_time); -         -        GLib.Timeout.add((uint)(1000.0/this.frame_rate), () => { -            this.alpha.update(1.0/this.frame_rate); -            this.update_label(); -             -            return (this.alpha.val != 1.0); -        }); -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Starts the fading out. -    ///////////////////////////////////////////////////////////////////// -     -    private void fade_out() { -        this.alpha = new AnimatedValue.linear(this.alpha.val, 0.0, this.fade_time); -         -        GLib.Timeout.add((uint)(1000.0/this.frame_rate), () => { -            this.alpha.update(1.0/this.frame_rate); -            this.update_label(); -             -            return (this.alpha.val != 0.0); -        }); -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Updates the color of the label. Called every frame while fading. -    ///////////////////////////////////////////////////////////////////// -     -    private void update_label() { -        Gdk.Color color = {(uint32)(fg.pixel*this.alpha.val + bg.pixel*(1.0 - this.alpha.val)), -                           (uint16)(fg.red*this.alpha.val + bg.red*(1.0 - this.alpha.val)), -                           (uint16)(fg.green*this.alpha.val + bg.green*(1.0 - this.alpha.val)), -                           (uint16)(fg.blue*this.alpha.val + bg.blue*(1.0 - this.alpha.val))}; -         -        this.modify_fg(Gtk.StateType.NORMAL, color); -    } -     -    ///////////////////////////////////////////////////////////////////// -    /// Chooses the next random tip. -    ///////////////////////////////////////////////////////////////////// -     -    private void set_random_tip() { -        if (tips.length > 1) { -            int next_index = -1; -            do { -                next_index = GLib.Random.int_range(0, tips.length); -            } while (next_index == this.index); -            this.index = next_index; -            this.label = tips[this.index]; -        } -    } -} - -} diff --git a/src/gui/triggerSelectButton.vala b/src/gui/triggerSelectButton.vala new file mode 100644 index 0000000..eeb37e2 --- /dev/null +++ b/src/gui/triggerSelectButton.vala @@ -0,0 +1,161 @@ +/*  +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 { + +/////////////////////////////////////////////////////////////////////////     +/// This window allows the selection of a hotkey. It is returned in form +/// of a Trigger. Therefore it can be either a keyboard driven hotkey or +/// a mouse based hotkey. +///////////////////////////////////////////////////////////////////////// + +public class TriggerSelectButton : Gtk.ToggleButton { +     +    ///////////////////////////////////////////////////////////////////// +    /// This signal is emitted when the user selects a new hot key. +    ///////////////////////////////////////////////////////////////////// +     +    public signal void on_select(Trigger trigger); +     +    ///////////////////////////////////////////////////////////////////// +    /// The currently contained Trigger. +    ///////////////////////////////////////////////////////////////////// +     +    private Trigger trigger = null; +     +    ///////////////////////////////////////////////////////////////////// +    /// True, if mouse buttons can be bound as well. +    ///////////////////////////////////////////////////////////////////// +      +    private bool enable_mouse = false; +     +    ///////////////////////////////////////////////////////////////////// +    /// These modifiers are ignored. +    ///////////////////////////////////////////////////////////////////// +     +    private Gdk.ModifierType lock_modifiers = Gdk.ModifierType.MOD2_MASK +                                             |Gdk.ModifierType.LOCK_MASK +                                             |Gdk.ModifierType.MOD5_MASK; +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, constructs a new TriggerSelectWindow. +    ///////////////////////////////////////////////////////////////////// +     +    public TriggerSelectButton(bool enable_mouse) { +        this.enable_mouse = enable_mouse; +     +        this.toggled.connect(() => { +            if (this.active) { +                this.set_label(_("Press a hotkey ...")); +                Gtk.grab_add(this); +                FocusGrabber.grab(this.get_window(), true, true, true); +            } +        }); +         +        this.button_press_event.connect(this.on_button_press); +        this.key_press_event.connect(this.on_key_press); +        this.set_trigger(new Trigger()); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes the button display the given Trigger. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_trigger(Trigger trigger) { +        this.trigger = trigger; +        this.set_label(trigger.label); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Can be called to cancel the selection process. +    ///////////////////////////////////////////////////////////////////// +     +    private void cancel() { +        this.set_label(trigger.label); +        this.set_active(false); +        Gtk.grab_remove(this); +        FocusGrabber.ungrab(true, true); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Makes the button display the given Trigger. +    ///////////////////////////////////////////////////////////////////// +     +    private void update_trigger(Trigger trigger) { +        if (this.trigger.name != trigger.name) { +            this.set_trigger(trigger); +            this.on_select(this.trigger); +        } +         +        this.cancel(); +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user presses a keyboard key. +    ///////////////////////////////////////////////////////////////////// +     +    private bool on_key_press(Gdk.EventKey event) { +        if (this.active) { +            if (Gdk.keyval_name(event.keyval) == "Escape") { +                this.cancel(); +            } else if (Gdk.keyval_name(event.keyval) == "BackSpace") { +                this.update_trigger(new Trigger()); +            } else if (event.is_modifier == 0) { +                Gdk.ModifierType state = event.state & ~ this.lock_modifiers; +                this.update_trigger(new Trigger.from_values(event.keyval, state, false, false, false, false)); +            } +             +            return true; +        } +        return false; +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// Called when the user presses a button of the mouse. +    ///////////////////////////////////////////////////////////////////// +     +    private bool on_button_press(Gdk.EventButton event) { +        if (this.active) { +                Gtk.Allocation rect; +                this.get_allocation(out rect); +                if (event.x < rect.x || event.x > rect.x + rect.width +                 || event.y < rect.y || event.y > rect.y + rect.height) { +                  +                    this.cancel(); +                    return true; +                } +            } +             +            if (this.active && this.enable_mouse) { +                Gdk.ModifierType state = event.state & ~ this.lock_modifiers; +                var new_trigger = new Trigger.from_values((int)event.button, state, true, +                                                          false, false, false); +                                                           +                if (new_trigger.key_code != 1) this.update_trigger(new_trigger); +                else                           this.cancel(); +                 +                return true; +            } else if (this.active) { +                this.cancel(); +                return true; +            } +             +            return false; +    } +} + +} diff --git a/src/gui/triggerSelectWindow.vala b/src/gui/triggerSelectWindow.vala index e003a84..23eea3c 100644 --- a/src/gui/triggerSelectWindow.vala +++ b/src/gui/triggerSelectWindow.vala @@ -23,21 +23,23 @@ namespace GnomePie {  /// a mouse based hotkey.  ///////////////////////////////////////////////////////////////////////// -public class TriggerSelectWindow : Gtk.Dialog { +public class TriggerSelectWindow : GLib.Object {      /////////////////////////////////////////////////////////////////////      /// This signal is emitted when the user selects a new hot key.      ///////////////////////////////////////////////////////////////////// -    public signal void on_select(Trigger trigger); +    public signal void on_ok(Trigger trigger);      /////////////////////////////////////////////////////////////////////      /// Some private members which are needed by other methods.      ///////////////////////////////////////////////////////////////////// +    private Gtk.Dialog window;      private Gtk.CheckButton turbo;      private Gtk.CheckButton delayed; -    private Gtk.Label preview; +    private Gtk.CheckButton centered; +    private TriggerSelectButton button;      /////////////////////////////////////////////////////////////////////      /// The currently configured trigger. @@ -54,204 +56,133 @@ public class TriggerSelectWindow : Gtk.Dialog {      private Trigger original_trigger = null;      ///////////////////////////////////////////////////////////////////// -    /// These modifiers are ignored. -    ///////////////////////////////////////////////////////////////////// -     -    private Gdk.ModifierType lock_modifiers = Gdk.ModifierType.MOD2_MASK -                                             |Gdk.ModifierType.LOCK_MASK -                                             |Gdk.ModifierType.MOD5_MASK; -     -    /////////////////////////////////////////////////////////////////////      /// C'tor, constructs a new TriggerSelectWindow.      /////////////////////////////////////////////////////////////////////      public TriggerSelectWindow() { -        this.title = _("Define an open-command"); -        this.resizable = false; -        this.delete_event.connect(hide_on_delete); -        this.key_press_event.connect(on_key_press); -        this.button_press_event.connect(on_button_press); -         -        this.show.connect_after(() => { -            FocusGrabber.grab(this); -        }); +        try { -        this.hide.connect(() => { -            FocusGrabber.ungrab(this); -        }); +            Gtk.Builder builder = new Gtk.Builder(); + +            builder.add_from_file (Paths.ui_files + "/trigger_select.ui"); -        var container = new Gtk.VBox(false, 6); -            container.set_border_width(6); -              -             // click area -             var click_frame = new Gtk.Frame(_("Click here if you want to bind a mouse button!")); +            this.window = builder.get_object("window") as Gtk.Dialog; +            this.button = new TriggerSelectButton(true); +            this.button.show(); -                var click_box = new Gtk.EventBox(); -                    click_box.height_request = 100; -                    click_box.button_press_event.connect(on_area_clicked); -                     -                    this.preview = new Gtk.Label(null); -                     -                    click_box.add(this.preview); -                     -                click_frame.add(click_box); -                 -            container.pack_start(click_frame, false); +            this.button.on_select.connect((trigger) => { +                this.trigger = new Trigger.from_values(trigger.key_sym, +                                                       trigger.modifiers, +                                                       trigger.with_mouse, +                                                       this.turbo.active, +                                                       this.delayed.active, +                                                       this.centered.active); +            }); -            // turbo checkbox -            this.turbo = new Gtk.CheckButton.with_label (_("Turbo mode")); -                this.turbo.tooltip_text = _("If checked, the Pie will close when you " +  -                                            "release the chosen hot key."); -                this.turbo.active = false; -                this.turbo.toggled.connect(() => { -                	if (this.trigger != null) -		            	this.update_trigger(new Trigger.from_values( -		            		this.trigger.key_sym, this.trigger.modifiers, -							this.trigger.with_mouse, this.turbo.active, -							this.delayed.active)); -                }); -                 -            container.pack_start(turbo, false); +            (builder.get_object("trigger-box") as Gtk.VBox).pack_start(this.button, true, true); +             +            (builder.get_object("ok-button") as Gtk.Button).clicked.connect(this.on_ok_button_clicked); +            (builder.get_object("cancel-button") as Gtk.Button).clicked.connect(this.on_cancel_button_clicked); +             +            this.turbo = builder.get_object("turbo-check") as Gtk.CheckButton; +            this.turbo.toggled.connect(this.on_check_toggled); -            // delayed checkbox -            this.delayed = new Gtk.CheckButton.with_label (_("Long press for activation")); -                this.delayed.tooltip_text = _("If checked, the Pie will only open if you " +  -                                              "press this hot key a bit longer."); -                this.delayed.active = false; -                this.delayed.toggled.connect(() => { -                	if (this.trigger != null) -		            	this.update_trigger(new Trigger.from_values( -		            		this.trigger.key_sym, this.trigger.modifiers, -							this.trigger.with_mouse, this.turbo.active, -							this.delayed.active)); -                }); +            this.delayed = builder.get_object("delay-check") as Gtk.CheckButton; +            this.delayed.toggled.connect(this.on_check_toggled); +             +            this.centered = builder.get_object("center-check") as Gtk.CheckButton; +            this.centered.toggled.connect(this.on_check_toggled); +             +            this.window.delete_event.connect(this.window.hide_on_delete); -            container.pack_start(delayed, false); - -        container.show_all(); -         -        this.vbox.pack_start(container, true, true); -         -        this.add_button(Gtk.Stock.CANCEL, 1); -        this.add_button(Gtk.Stock.OK, 0); -         -        // select a new trigger on OK, hide on CANCEL -        this.response.connect((id) => { -        	if (id == 1) -        		this.hide(); -        	else if (id == 0) { -        		var assigned_id = PieManager.get_assigned_id(this.trigger); +        } catch (GLib.Error e) { +            error("Could not load UI: %s\n", e.message); +        } +    } -				if (this.trigger == this.original_trigger) { -					// nothing did change -					this.hide(); -				} else if (this.trigger.key_code == this.original_trigger.key_code -						   && this.trigger.modifiers == this.original_trigger.modifiers -						   && this.trigger.with_mouse == this.original_trigger.with_mouse) { -					// only turbo and/or delayed mode changed, no need to check for double assignment -					this.on_select(this.trigger); -				    this.hide(); -				} else if (assigned_id != "") { -					// it's already assigned -				    var error = _("This hotkey is already assigned to the pie \"%s\"! \n\nPlease select " + -				                  "another one or cancel your selection.").printf(PieManager.get_name_of(assigned_id)); -				    var dialog = new Gtk.MessageDialog((Gtk.Window)this.get_toplevel(),  -				    									Gtk.DialogFlags.MODAL,  -				                                        Gtk.MessageType.ERROR,  -				                                        Gtk.ButtonsType.CANCEL,  -				                                        error); -				    dialog.run(); -				    dialog.destroy(); -				} else { -					// a unused hot key has been chosen, great! -				    this.on_select(this.trigger); -				    this.hide(); -				} -        	} -        }); +    ///////////////////////////////////////////////////////////////////// +    /// Sets the parent window, in order to make this window stay in +    /// front. +    ///////////////////////////////////////////////////////////////////// +     +    public void set_parent(Gtk.Window parent) { +        this.window.set_transient_for(parent);      }      ///////////////////////////////////////////////////////////////////// -    /// Used to set the currently selected trigger on opening. +    /// Displays the window on the screen.      ///////////////////////////////////////////////////////////////////// -    public void set_trigger(Trigger trigger) { -        this.turbo.active = trigger.turbo; -        this.delayed.active = trigger.delayed; -        this.original_trigger = trigger; -        this.update_trigger(trigger); +    public void show() { +        this.window.show_all();      }      ///////////////////////////////////////////////////////////////////// -    /// Called when the user clicks in the click area. +    /// Initilizes all members to match the Trigger of the Pie with the +    /// given ID.      ///////////////////////////////////////////////////////////////////// -    private bool on_area_clicked(Gdk.EventButton event) { -        Gdk.ModifierType state = event.state & ~ this.lock_modifiers; +    public void set_pie(string id) { +        var trigger = new Trigger.from_string(PieManager.get_accelerator_of(id)); -        var new_trigger = new Trigger.from_values((int)event.button, state, true,  -                                                  this.turbo.active, this.delayed.active); -        if (new_trigger.key_code == 1) { -            var dialog = new Gtk.MessageDialog((Gtk.Window)this.get_toplevel(), Gtk.DialogFlags.MODAL,  -                                                Gtk.MessageType.WARNING,  -                                                Gtk.ButtonsType.YES_NO,  -                                                _("It possible to make your system unusable if " + -                                                  "you bind a Pie to your left mouse button. Do " + -                                                  "you really want to do this?")); -                                                  -            dialog.response.connect((response) => { -                if (response == Gtk.ResponseType.YES) { -                    this.update_trigger(new_trigger); -                } -            }); -             -            dialog.run(); -            dialog.destroy(); -        } else { -            this.update_trigger(new_trigger); -        } +        this.turbo.active = trigger.turbo; +        this.delayed.active = trigger.delayed; +        this.centered.active = trigger.centered; +        this.original_trigger = trigger; +        this.trigger = trigger; -        return true; +        this.button.set_trigger(trigger);      }      ///////////////////////////////////////////////////////////////////// -    /// Called when the user presses a keyboard key. +    /// Called when one of the three checkoxes is toggled.      ///////////////////////////////////////////////////////////////////// -    private bool on_key_press(Gdk.EventKey event) { -        if (Gdk.keyval_name(event.keyval) == "Escape") { -            this.hide(); -        } else if (Gdk.keyval_name(event.keyval) == "BackSpace") { -            this.update_trigger(new Trigger()); -        } else if (event.is_modifier == 0) { -            Gdk.ModifierType state = event.state & ~ this.lock_modifiers; -            this.update_trigger(new Trigger.from_values((int)event.keyval, state, false,  -                                                   this.turbo.active, this.delayed.active)); -        } -         -        return true; +    private void on_check_toggled() { +        if (this.trigger != null) +            this.trigger = new Trigger.from_values(this.trigger.key_sym, this.trigger.modifiers, +                                                   this.trigger.with_mouse, this.turbo.active, +                                                   this.delayed.active, this.centered.active);      }      ///////////////////////////////////////////////////////////////////// -    /// Called when the user presses a mouse button. +    /// Called when the OK-button is pressed.      ///////////////////////////////////////////////////////////////////// -    private bool on_button_press(Gdk.EventButton event) { -        int width = 0, height = 0; -        this.window.get_geometry(null, null, out width, out height, null); -        if (event.x < 0 || event.x > width || event.y < 0 || event.y > height) -            this.hide(); -        return true; +    private void on_ok_button_clicked() { +        var assigned_id = PieManager.get_assigned_id(this.trigger); +     +        if (this.trigger == this.original_trigger) { +            // nothing did change +            this.window.hide(); +        } else if (this.trigger.key_code == this.original_trigger.key_code +                && this.trigger.modifiers == this.original_trigger.modifiers +                && this.trigger.with_mouse == this.original_trigger.with_mouse) { +            // only turbo and/or delayed mode changed, no need to check for double assignment +            this.on_ok(this.trigger); +            this.window.hide(); +        } else if (assigned_id != "") { +            // it's already assigned +            var error = _("This hotkey is already assigned to the pie \"%s\"! \n\nPlease select " + +                          "another one or cancel your selection.").printf(PieManager.get_name_of(assigned_id)); +            var dialog = new Gtk.MessageDialog((Gtk.Window)this.window.get_toplevel(), Gtk.DialogFlags.MODAL, +                                               Gtk.MessageType.ERROR, Gtk.ButtonsType.CANCEL, error); +            dialog.run(); +            dialog.destroy(); +        } else { +            // a unused hot key has been chosen, great! +            this.on_ok(this.trigger); +            this.window.hide(); +        }      }      ///////////////////////////////////////////////////////////////////// -    /// Helper method to update the content of the trigger preview label. +    /// Called when the cancel button is pressed.      ///////////////////////////////////////////////////////////////////// -    private void update_trigger(Trigger new_trigger) { -    	this.trigger = new_trigger; -        this.preview.set_markup("<big><b>" + this.trigger.label + "</b></big>"); -    }      +    private void on_cancel_button_clicked() { +        this.window.hide(); +    }   }  } diff --git a/src/images/icon.vala b/src/images/icon.vala index 1c8a9f4..81eb2d9 100644 --- a/src/images/icon.vala +++ b/src/images/icon.vala @@ -80,7 +80,17 @@ public class Icon : Image {      public static string get_icon_file(string icon_name, int size) {          string result = ""; -     +         +        if (icon_name.contains("/")) { +            var file = GLib.File.new_for_path(icon_name); +            if(file.query_exists()) +                return icon_name; +             +            warning("Icon \"" + icon_name + "\" not found! Using default icon..."); +            icon_name = "application-default-icon"; +        } +             +                  var icon_theme = Gtk.IconTheme.get_default();          var file = icon_theme.lookup_icon(icon_name, size, 0);          if (file != null) result = file.get_filename(); diff --git a/src/images/image.vala b/src/images/image.vala index 836e4e2..1d9674b 100644 --- a/src/images/image.vala +++ b/src/images/image.vala @@ -65,14 +65,39 @@ public class Image : GLib.Object {      /////////////////////////////////////////////////////////////////////      public Image.from_pixbuf(Gdk.Pixbuf pixbuf) { -        this.load_pixbuf(pixbuf); +        if (pixbuf != null) this.load_pixbuf(pixbuf); +        else                this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 1, 1);      } -    public Image.capture_screen(int posx, int posy, int width, int height) { +    ///////////////////////////////////////////////////////////////////// +    /// Captures a part of the screen. +    ///////////////////////////////////////////////////////////////////// +     +    public Image.capture_screen(int posx, int posy, int width, int height, bool hide_pies = true) {          Gdk.Window root = Gdk.get_default_root_window(); -        Gdk.Pixbuf pixbuf = Gdk.pixbuf_get_from_drawable(null, root, null, posx, posy, 0, 0, width, height); +        #if HAVE_GTK_3 +            Gdk.Pixbuf pixbuf = Gdk.pixbuf_get_from_window(root, posx, posy, width, height); +        #else +            Gdk.Pixbuf pixbuf = Gdk.pixbuf_get_from_drawable(null, root, null, posx, posy, 0, 0, width, height); +        #endif +          this.load_pixbuf(pixbuf); +         +        if (hide_pies) { +            // check for opened pies +            foreach (var window in PieManager.opened_windows) { +                if (window.background != null) { +                    int x=0, y=0, dx=0, dy=0; +                    window.get_position(out x, out y); +                    window.get_size(out dx, out dy); + +                    var ctx = this.context(); +                    ctx.translate((int)(x-posx + (dx+3)/2), (int)(y-posy + (dy+3)/2));                     +                    window.background.paint_on(ctx); +                } +            } +        }      }      ///////////////////////////////////////////////////////////////////// @@ -87,6 +112,7 @@ public class Image : GLib.Object {                  this.load_pixbuf(pixbuf);              } else {                  warning("Failed to load " + filename + "!"); +                this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 1, 1);              }          } catch (GLib.Error e) {              message("Error loading image file: %s", e.message); @@ -106,6 +132,7 @@ public class Image : GLib.Object {                  this.load_pixbuf(pixbuf);              } else {                  warning("Failed to load " + filename + "!"); +                this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height);              }          } catch (GLib.Error e) {              message("Error loading image file: %s", e.message); @@ -130,17 +157,39 @@ public class Image : GLib.Object {      /////////////////////////////////////////////////////////////////////      public void paint_on(Cairo.Context ctx, double alpha = 1.0) { -        ctx.set_source_surface(this.surface, -0.5*this.width()-1, -0.5*this.height()-1); +        ctx.set_source_surface(this.surface, (int)(-0.5*this.width()-1), (int)(-0.5*this.height()-1));          if (alpha >= 1.0) ctx.paint();          else              ctx.paint_with_alpha(alpha);      }      ///////////////////////////////////////////////////////////////////// +    /// Converts the image to a Gdk.Pixbuf. +    ///////////////////////////////////////////////////////////////////// +     +    public Gdk.Pixbuf to_pixbuf() { +        var pixbuf = new Gdk.Pixbuf.from_data(surface.get_data(), Gdk.Colorspace.RGB, true, 8,  +                                              width(), height(), surface.get_stride(), null); +         +        pixbuf = pixbuf.copy(); +                                        +        // funny stuff here --- need to swap Red end Blue because Cairo  +        // and Gdk are different... +        uint8* p = pixbuf.pixels; +        for (int i=0; i<width()*height()*4-4; i+=4) { +            var tmp = *(p + i); +            *(p + i) = *(p + i + 2); +            *(p + i + 2) = tmp; +        } +		 +		return pixbuf; +    } +     +    /////////////////////////////////////////////////////////////////////      /// Returns a Cairo.Context for the Image.      /////////////////////////////////////////////////////////////////////      public Cairo.Context context() { -        return new Cairo.Context(this.surface);; +        return new Cairo.Context(this.surface);      }      ///////////////////////////////////////////////////////////////////// @@ -148,7 +197,9 @@ public class Image : GLib.Object {      /////////////////////////////////////////////////////////////////////      public int width() { -        return this.surface.get_width(); +        if (this.surface != null) +            return this.surface.get_width(); +        return 0;      }      ///////////////////////////////////////////////////////////////////// @@ -156,7 +207,9 @@ public class Image : GLib.Object {      /////////////////////////////////////////////////////////////////////      public int height() { -        return this.surface.get_height(); +        if (this.surface != null) +            return this.surface.get_height(); +        return 0;      }  } diff --git a/src/images/renderedText.vala b/src/images/renderedText.vala index 924742a..e4bb4cb 100644 --- a/src/images/renderedText.vala +++ b/src/images/renderedText.vala @@ -55,12 +55,36 @@ public class RenderedText : Image {      /// C'tor, creates a new image representation of a string.      ///////////////////////////////////////////////////////////////////// -    public RenderedText(string text, int width, int height, string font) { -        var cached = this.cache.get("%s@%ux%u:%s".printf(text, width, height, font)); +    public RenderedText(string text, int width, int height, string font, +                        Color color, double scale) { +                         +        var cached = this.cache.get("%s@%ux%u@%f:%s:%f:%f:%f:%f".printf(text, width, height, scale, font, +                                                               color.r, color.g, color.b, color.a));          if (cached == null) { -            this.render_text(text, width, height, font); -            this.cache.set("%s@%ux%u:%s".printf(text, width, height, font), this.surface); +            this.render_text(text, width, height, font, color, scale); +            this.cache.set("%s@%ux%u@%f:%s:%f:%f:%f:%f".printf(text, width, height, scale, font, +                                                 color.r, color.g, color.b, color.a), this.surface); +        } else { +            this.surface = cached; +        } +    } +     +    ///////////////////////////////////////////////////////////////////// +    /// C'tor, creates a new image representation of a string. This +    /// string may contain markup information. +    ///////////////////////////////////////////////////////////////////// +     +    public RenderedText.with_markup(string text, int width, int height, string font, +                        Color color, double scale) { +                         +        var cached = this.cache.get("%s@%ux%u@%f:%s:%f:%f:%f:%f".printf(text, width, height, scale, font, +                                                               color.r, color.g, color.b, color.a)); +         +        if (cached == null) { +            this.render_markup(text, width, height, font, color, scale); +            this.cache.set("%s@%ux%u@%f:%s:%f:%f:%f:%f".printf(text, width, height, scale, font, +                                                 color.r, color.g, color.b, color.a), this.surface);          } else {              this.surface = cached;          } @@ -70,20 +94,21 @@ public class RenderedText : Image {      /// Creates a new transparent image, with text written onto.      ///////////////////////////////////////////////////////////////////// -    public void render_text(string text, int width, int height, string font) { +    public void render_text(string text, int width, int height, string font,  +                            Color color, double scale) { +                                      this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height);          var ctx = this.context(); -        // set the color as specified in the current theme -        Color color = Config.global.theme.caption_color; +        // set the color          ctx.set_source_rgb(color.r, color.g, color.g);          var layout = Pango.cairo_create_layout(ctx);                  layout.set_width(Pango.units_from_double(width));          var font_description = Pango.FontDescription.from_string(font); -        font_description.set_size((int)(font_description.get_size() * Config.global.global_scale)); +        font_description.set_size((int)(font_description.get_size() * scale));          layout.set_font_description(font_description);          layout.set_text(text, -1); @@ -105,6 +130,40 @@ public class RenderedText : Image {          Pango.cairo_update_layout(ctx, layout);          Pango.cairo_show_layout(ctx, layout);      } +     +    ///////////////////////////////////////////////////////////////////// +    /// Creates a new transparent image, with text written onto. +    ///////////////////////////////////////////////////////////////////// +     +    public void render_markup(string text, int width, int height, string font,  +                            Color color, double scale) { +                             +        this.surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height); + +        var ctx = this.context(); +         +        // set the color  +        ctx.set_source_rgb(color.r, color.g, color.g); +         +        var layout = Pango.cairo_create_layout(ctx);         +        layout.set_width(Pango.units_from_double(width)); +         +        var font_description = Pango.FontDescription.from_string(font); +        font_description.set_size((int)(font_description.get_size() * scale)); +         +        layout.set_font_description(font_description); +        layout.set_markup(text, -1); +         +        layout.set_ellipsize(Pango.EllipsizeMode.END); +        layout.set_alignment(Pango.Alignment.CENTER); +         +        Pango.Rectangle extents; +        layout.get_pixel_extents(null, out extents); +        ctx.move_to(0, (int)(0.5*(height - extents.height))); +         +        Pango.cairo_update_layout(ctx, layout); +        Pango.cairo_show_layout(ctx, layout); +    }  }  } diff --git a/src/images/themedIcon.vala b/src/images/themedIcon.vala index 29ae380..6c904a6 100644 --- a/src/images/themedIcon.vala +++ b/src/images/themedIcon.vala @@ -76,7 +76,7 @@ public class ThemedIcon : Image {          var layers = active ? Config.global.theme.active_slice_layers : Config.global.theme.inactive_slice_layers;          // get max size -        int size = 0; +        int size = 1;          foreach (var layer in layers) {              if (layer.image.width() > size) size = layer.image.width();          } diff --git a/src/pies/load.vala b/src/pies/load.vala index 98fd72f..b606cf5 100644 --- a/src/pies/load.vala +++ b/src/pies/load.vala @@ -80,7 +80,7 @@ namespace Pies {          string name = "";          string icon = "";          string id = ""; -        int quick_action = -1; +        int quickaction = -1;          // parse all attributes of this node          for (Xml.Attr* attribute = node->properties; attribute != null; attribute = attribute->next) { @@ -92,7 +92,7 @@ namespace Pies {                      hotkey = attr_content;                      break;                  case "quickaction": -                    quick_action = int.parse(attr_content); +                    quickaction = int.parse(attr_content);                      break;                  case "name":                      name = attr_content; @@ -145,7 +145,7 @@ namespace Pies {          string icon="";          string command="";          string type=""; -        bool quick_action = false; +        bool quickaction = false;          // parse all attributes of this node          for (Xml.Attr* attribute = slice->properties; attribute != null; attribute = attribute->next) { @@ -166,7 +166,7 @@ namespace Pies {                      type = attr_content;                      break;                  case "quickaction": -                    quick_action = bool.parse(attr_content); +                    quickaction = bool.parse(attr_content);                      break;                  default:                      warning("Invalid attribute \"" + attr_name + "\" in <slice> element in pies.conf!"); @@ -174,19 +174,8 @@ namespace Pies {              }          } -        Action action = null; -                  // create a new Action according to the loaded type -        foreach (var action_type in ActionRegistry.types) { -            if (ActionRegistry.settings_names[action_type] == type) { -             -                action = GLib.Object.new(action_type, "name", name,  -                                                      "icon", icon,  -                                              "real_command", command,  -                                           "is_quick_action", quick_action) as Action; -                break; -            }  -        } +        Action action = ActionRegistry.create_action(type, name, icon, command, quickaction);          if (action != null) pie.add_action(action);      } @@ -213,15 +202,7 @@ namespace Pies {              }          } -        ActionGroup group = null; -         -        // create a new ActionGroup according to the loaded type -        foreach (var group_type in GroupRegistry.types) { -            if (GroupRegistry.settings_names[group_type] == type) { -                group = GLib.Object.new(group_type, "parent_id", pie.id) as ActionGroup; -                break; -            }  -        } +        ActionGroup group = GroupRegistry.create_group(type, pie.id);          if (group != null) pie.add_group(group);      } diff --git a/src/pies/pie.vala b/src/pies/pie.vala index 0f87d8f..fa205c7 100644 --- a/src/pies/pie.vala +++ b/src/pies/pie.vala @@ -29,14 +29,14 @@ public class Pie : GLib.Object {      /// The name of this Pie. It has not to be unique.      ///////////////////////////////////////////////////////////////////// -    public string name { get; construct; } +    public string name { get; set; }      /////////////////////////////////////////////////////////////////////      /// The name of the icon to be used for this Pie. It should exist in      /// the users current icon theme, else a standard icon will be used.      ///////////////////////////////////////////////////////////////////// -    public string icon { get; construct; } +    public string icon { get; set; }      /////////////////////////////////////////////////////////////////////      /// The ID of this Pie. It has to be unique among all Pies. This ID @@ -77,18 +77,44 @@ public class Pie : GLib.Object {      /// Adds an Action to this Pie.      ///////////////////////////////////////////////////////////////////// -    public void add_action(Action action) { +    public void add_action(Action action, int at_position = -1) {          var group = new ActionGroup(this.id);              group.add_action(action); -        this.add_group(group); +        this.add_group(group, at_position);      }      /////////////////////////////////////////////////////////////////////      /// Adds an ActionGroup to this Pie.      ///////////////////////////////////////////////////////////////////// -    public void add_group(ActionGroup group) { -        this.action_groups.add(group); +    public void add_group(ActionGroup group, int at_position = -1) {      +        if (group.has_quickaction()) { +            foreach (var action_group in action_groups) +                action_group.disable_quickactions(); +        } +             +        if (at_position < 0 || at_position >= this.action_groups.size)  +            this.action_groups.add(group); +        else +            this.action_groups.insert(at_position, group); +    } +     +    public void remove_group(int index) { +        if (this.action_groups.size > index) +            this.action_groups.remove_at(index); +    } +     +    public void move_group(int from, int to) { +        if (this.action_groups.size > from && this.action_groups.size > to) { +            var tmp = this.action_groups[from]; +            this.remove_group(from); +            this.add_group(tmp, to); +        } +    } +     +    public void update_group(ActionGroup group, int index) { +        if (this.action_groups.size > index) +            this.action_groups.set(index, group);      }  } diff --git a/src/pies/pieManager.vala b/src/pies/pieManager.vala index 5f84ea0..0263d23 100644 --- a/src/pies/pieManager.vala +++ b/src/pies/pieManager.vala @@ -32,6 +32,13 @@ public class PieManager : GLib.Object {      public static Gee.HashMap<string, Pie?> all_pies { get; private set; }      ///////////////////////////////////////////////////////////////////// +    /// Stores all PieWindows which are currently opened. Should be +    /// rarely more than two... +    ///////////////////////////////////////////////////////////////////// +     +    public static Gee.HashSet<PieWindow?> opened_windows { get; private set; } +     +    /////////////////////////////////////////////////////////////////////      /// Stores all global hotkeys.      ///////////////////////////////////////////////////////////////////// @@ -42,7 +49,7 @@ public class PieManager : GLib.Object {      /// will be false already.      ///////////////////////////////////////////////////////////////////// -    private static bool a_pie_is_opened = false; +    private static bool a_pie_is_active = false;      /////////////////////////////////////////////////////////////////////      /// Initializes all Pies. They are loaded from the pies.conf file. @@ -50,6 +57,7 @@ public class PieManager : GLib.Object {      public static void init() {          all_pies = new Gee.HashMap<string, Pie?>(); +        opened_windows = new Gee.HashSet<PieWindow?>();          bindings = new BindingManager();          // load all Pies from th pies.conf file @@ -66,19 +74,27 @@ public class PieManager : GLib.Object {      /////////////////////////////////////////////////////////////////////      public static void open_pie(string id) { -        if (!a_pie_is_opened) { +        if (!a_pie_is_active) {              Pie? pie = all_pies[id];              if (pie != null) { -                a_pie_is_opened = true; +                a_pie_is_active = true;                  var window = new PieWindow();                  window.load_pie(pie);                  window.open(); +                opened_windows.add(window); +                 +                window.on_closed.connect(() => { +                    opened_windows.remove(window); +                }); +                                  window.on_closing.connect(() => { -                    a_pie_is_opened = false; +                    a_pie_is_active = false;                  }); +                 +                              } else {                  warning("Failed to open pie with ID \"" + id + "\": ID does not exist!");              } @@ -103,6 +119,15 @@ public class PieManager : GLib.Object {      }      ///////////////////////////////////////////////////////////////////// +    /// Bind the Pie with the given ID to the given trigger. +    ///////////////////////////////////////////////////////////////////// +     +    public static void bind_trigger(Trigger trigger, string id) { +        bindings.unbind(id); +        bindings.bind(trigger, id); +    } +     +    /////////////////////////////////////////////////////////////////////      /// Returns true if the pie with the given id is in turbo mode.      ///////////////////////////////////////////////////////////////////// @@ -111,6 +136,15 @@ public class PieManager : GLib.Object {      }      ///////////////////////////////////////////////////////////////////// +    /// Returns true if the pie with the given id opens in the middle of +    /// the screen. +    ///////////////////////////////////////////////////////////////////// +     +    public static bool get_is_centered(string id) { +        return bindings.get_is_centered(id); +    } +     +    /////////////////////////////////////////////////////////////////////      /// Returns the name of the Pie with the given ID.      ///////////////////////////////////////////////////////////////////// @@ -212,7 +246,11 @@ public class PieManager : GLib.Object {          }      } -    private static void create_launcher(string id) { +    ///////////////////////////////////////////////////////////////////// +    /// Creates a desktop file for which opens the Pie with given ID. +    ///////////////////////////////////////////////////////////////////// +     +    public static void create_launcher(string id) {          if (all_pies.has_key(id)) {              Pie? pie = all_pies[id]; @@ -220,7 +258,7 @@ public class PieManager : GLib.Object {                  "#!/usr/bin/env xdg-open\n" +                   "[Desktop Entry]\n" +                  "Name=%s\n".printf(pie.name) + -                "Exec=gnome-pie -o %s\n".printf(pie.id) + +                "Exec=%s -o %s\n".printf(Paths.executable, pie.id) +                  "Encoding=UTF-8\n" +                  "Type=Application\n" +                  "Icon=%s\n".printf(pie.icon); @@ -237,6 +275,10 @@ public class PieManager : GLib.Object {          }      } +    ///////////////////////////////////////////////////////////////////// +    /// Deletes the desktop file for the Pie with the given ID. +    ///////////////////////////////////////////////////////////////////// +          private static void remove_launcher(string id) {          string launcher = Paths.launchers + "/%s.desktop".printf(id);          if (FileUtils.test(launcher, FileTest.EXISTS)) { diff --git a/src/pies/save.vala b/src/pies/save.vala index d691a95..c940e5a 100644 --- a/src/pies/save.vala +++ b/src/pies/save.vala @@ -55,18 +55,18 @@ namespace Pies {                      if (group.get_type().depth() == 2) {                          foreach (var action in group.actions) {                              writer.start_element("slice"); -                            writer.write_attribute("type", ActionRegistry.settings_names[action.get_type()]); -                            if (ActionRegistry.icon_name_editables[action.get_type()]) { +                            writer.write_attribute("type", ActionRegistry.descriptions[action.get_type().name()].id); +                            if (ActionRegistry.descriptions[action.get_type().name()].icon_name_editable) {                                  writer.write_attribute("name", action.name);                                  writer.write_attribute("icon", action.icon);                              }                              writer.write_attribute("command", action.real_command); -                            writer.write_attribute("quickAction", action.is_quick_action ? "true" : "false"); +                            writer.write_attribute("quickAction", action.is_quickaction ? "true" : "false");                              writer.end_element();                          }                      } else {                          writer.start_element("group"); -                            writer.write_attribute("type", GroupRegistry.settings_names[group.get_type()]); +                            writer.write_attribute("type", GroupRegistry.descriptions[group.get_type().name()].id);                          writer.end_element();                      }                  } diff --git a/src/renderers/pieRenderer.vala b/src/renderers/pieRenderer.vala index ffaf776..67a6b56 100644 --- a/src/renderers/pieRenderer.vala +++ b/src/renderers/pieRenderer.vala @@ -31,7 +31,7 @@ public class PieRenderer : GLib.Object {      /// gets executed when the user clicks on the middle of the pie)      ///////////////////////////////////////////////////////////////////// -    public int quick_action { get; private set; } +    public int quickaction { get; private set; }      /////////////////////////////////////////////////////////////////////      /// The index of the currently active slice. @@ -83,13 +83,13 @@ public class PieRenderer : GLib.Object {      public PieRenderer() {          this.slices = new Gee.ArrayList<SliceRenderer?>();           this.center = new CenterRenderer(this); -        this.quick_action = -1; +        this.quickaction = -1;          this.active_slice = -2;          this.size = 0;      }      ///////////////////////////////////////////////////////////////////// -    /// Loads an Pie. All members are initialized accordingly. +    /// Loads a Pie. All members are initialized accordingly.      /////////////////////////////////////////////////////////////////////      public void load_pie(Pie pie) { @@ -102,8 +102,8 @@ public class PieRenderer : GLib.Object {                  this.slices.add(renderer);                  renderer.load(action, slices.size-1); -                if (action.is_quick_action) { -                    this.quick_action = count; +                if (action.is_quickaction) { +                    this.quickaction = count;                  }                  ++count; @@ -112,7 +112,7 @@ public class PieRenderer : GLib.Object {          this.turbo_mode = PieManager.get_is_turbo(pie.id); -        this.set_highlighted_slice(this.quick_action); +        this.set_highlighted_slice(this.quickaction);          this.size = (int)fmax(2*Config.global.theme.radius + 2*Config.global.theme.slice_radius*Config.global.theme.max_zoom,                                2*Config.global.theme.center_radius); @@ -227,39 +227,41 @@ public class PieRenderer : GLib.Object {      /////////////////////////////////////////////////////////////////////      public void draw(double frame_time, Cairo.Context ctx, int mouse_x, int mouse_y) { -	    double distance = sqrt(mouse_x*mouse_x + mouse_y*mouse_y); -	    double angle = 0.0; +        if (this.size > 0) { +	        double distance = sqrt(mouse_x*mouse_x + mouse_y*mouse_y); +	        double angle = 0.0; -	    if (this.key_board_control) { -	        angle = 2.0*PI*this.active_slice/(double)slice_count(); -	    } else { -	     -	        if (distance > 0) { -	            angle = acos(mouse_x/distance); -		        if (mouse_y < 0)  -		            angle = 2*PI - angle; -	        } +	        if (this.key_board_control) { +	            angle = 2.0*PI*this.active_slice/(double)slice_count(); +	        } else { -	        int next_active_slice = this.active_slice; +	            if (distance > 0) { +	                angle = acos(mouse_x/distance); +		            if (mouse_y < 0)  +		                angle = 2*PI - angle; +	            } +	             +	            int next_active_slice = this.active_slice; +	             +	            if (distance < Config.global.theme.active_radius +	                && this.quickaction >= 0 && this.quickaction < this.slices.size) { +	              +	                next_active_slice = this.quickaction;    +	                angle = 2.0*PI*quickaction/(double)slice_count(); +	            } else if (distance > Config.global.theme.active_radius && this.slice_count() > 0) { +	                next_active_slice = (int)(angle*slices.size/(2*PI) + 0.5) % this.slice_count(); +	            } else { +	                next_active_slice = -1; +	            } -	        if (distance < Config.global.theme.active_radius -	            && this.quick_action >= 0 && this.quick_action < this.slices.size) { -	          -	            next_active_slice = this.quick_action;    -	            angle = 2.0*PI*quick_action/(double)slice_count(); -	        } else if (distance > Config.global.theme.active_radius && this.slice_count() > 0) { -	            next_active_slice = (int)(angle*slices.size/(2*PI) + 0.5) % this.slice_count(); -	        } else { -	            next_active_slice = -1; +	            this.set_highlighted_slice(next_active_slice);  	        } -	     -	        this.set_highlighted_slice(next_active_slice); -	    } -        center.draw(frame_time, ctx, angle, distance); -	     -	    foreach (var slice in this.slices) -		    slice.draw(frame_time, ctx, angle, distance); +            center.draw(frame_time, ctx, angle, distance); +	         +	        foreach (var slice in this.slices) +		        slice.draw(frame_time, ctx, angle, distance); +		}      }      ///////////////////////////////////////////////////////////////////// @@ -278,8 +280,8 @@ public class PieRenderer : GLib.Object {          if (index != this.active_slice) {              if (index >= 0 && index < this.slice_count())                   this.active_slice = index; -            else if (this.quick_action >= 0) -                this.active_slice = this.quick_action; +            else if (this.quickaction >= 0) +                this.active_slice = this.quickaction;              else                  this.active_slice = -1; diff --git a/src/renderers/pieWindow.vala b/src/renderers/pieWindow.vala index 59117df..0a26110 100644 --- a/src/renderers/pieWindow.vala +++ b/src/renderers/pieWindow.vala @@ -32,6 +32,19 @@ public class PieWindow : Gtk.Window {      public signal void on_closing();      ///////////////////////////////////////////////////////////////////// +    /// Signal which gets emitted when the PieWindow is closed. +    ///////////////////////////////////////////////////////////////////// +     +    public signal void on_closed(); +     +    ///////////////////////////////////////////////////////////////////// +    /// The background image used for fake transparency if +    /// has_compositing is false. +    ///////////////////////////////////////////////////////////////////// +     +    public Image background { get; private set; default=null; } +     +    /////////////////////////////////////////////////////////////////////      /// The owned renderer.      ///////////////////////////////////////////////////////////////////// @@ -56,13 +69,6 @@ public class PieWindow : Gtk.Window {      private bool has_compositing = false;      ///////////////////////////////////////////////////////////////////// -    /// The background image used for fake transparency if -    /// has_compositing is false. -    ///////////////////////////////////////////////////////////////////// -     -    private Image background = null; -     -    /////////////////////////////////////////////////////////////////////      /// C'tor, sets up the window.      ///////////////////////////////////////////////////////////////////// @@ -73,7 +79,7 @@ public class PieWindow : Gtk.Window {          this.set_skip_taskbar_hint(true);          this.set_skip_pager_hint(true);          this.set_keep_above(true); -        this.set_type_hint(Gdk.WindowTypeHint.SPLASHSCREEN); +        this.set_type_hint(Gdk.WindowTypeHint.UTILITY);          this.set_decorated(false);          this.set_resizable(false);          this.icon_name = "gnome-pie"; @@ -81,7 +87,11 @@ public class PieWindow : Gtk.Window {          // check for compositing          if (this.screen.is_composited()) { -            this.set_colormap(this.screen.get_rgba_colormap()); +            #if HAVE_GTK_3 +                this.set_visual(this.screen.get_rgba_visual()); +            #else +                this.set_colormap(this.screen.get_rgba_colormap()); +            #endif              this.has_compositing = true;          } @@ -128,9 +138,18 @@ public class PieWindow : Gtk.Window {              this.renderer.on_mouse_move();              return true;          }); +         +        this.show.connect_after(() => { +            Gtk.grab_add(this); +            FocusGrabber.grab(this.get_window(), true, true, false); +        });          // draw the pie on expose -        this.expose_event.connect(this.draw); +        #if HAVE_GTK_3 +            this.draw.connect(this.draw_window); +        #else +            this.expose_event.connect(this.draw_window); +        #endif      }      ///////////////////////////////////////////////////////////////////// @@ -139,7 +158,7 @@ public class PieWindow : Gtk.Window {      public void load_pie(Pie pie) {          this.renderer.load_pie(pie); -        this.set_window_position(); +        this.set_window_position(pie);          this.set_size_request(renderer.size, renderer.size);      } @@ -160,7 +179,6 @@ public class PieWindow : Gtk.Window {          // capture the input focus          this.show(); -        FocusGrabber.grab(this);          // start the timer          this.timer = new GLib.Timer(); @@ -178,10 +196,13 @@ public class PieWindow : Gtk.Window {      /// Draw the Pie.      ///////////////////////////////////////////////////////////////////// -    private bool draw(Gtk.Widget da, Gdk.EventExpose event) {     -        // clear the window -        var ctx = Gdk.cairo_create(this.window); - +    #if HAVE_GTK_3 +        private bool draw_window(Cairo.Context ctx) {  +    #else +        private bool draw_window(Gtk.Widget da, Gdk.EventExpose event) {     +            // clear the window +            var ctx = Gdk.cairo_create(this.get_window()); +    #endif          // paint the background image if there is no compositing          if (this.has_compositing) {              ctx.set_operator (Cairo.Operator.CLEAR); @@ -219,12 +240,13 @@ public class PieWindow : Gtk.Window {          if (!this.closing) {              this.closing = true;              this.on_closing(); -            FocusGrabber.ungrab(this); +            Gtk.grab_remove(this); +            FocusGrabber.ungrab();              this.renderer.activate();              Timeout.add((uint)(Config.global.theme.fade_out_time*1000), () => { +                this.on_closed();                  this.destroy(); -                ThemedIcon.clear_cache();                  return false;              });          } @@ -238,12 +260,13 @@ public class PieWindow : Gtk.Window {          if (!this.closing) {              this.closing = true;              this.on_closing(); -            FocusGrabber.ungrab(this); +            Gtk.grab_remove(this); +            FocusGrabber.ungrab();              this.renderer.cancel();              Timeout.add((uint)(Config.global.theme.fade_out_time*1000), () => { +                this.on_closed();                  this.destroy(); -                ThemedIcon.clear_cache();                  return false;              });          } @@ -254,9 +277,9 @@ public class PieWindow : Gtk.Window {      /// the mouse.      ///////////////////////////////////////////////////////////////////// -    private void set_window_position() { -        if(Config.global.open_at_mouse) this.set_position(Gtk.WindowPosition.MOUSE); -        else                            this.set_position(Gtk.WindowPosition.CENTER); +    private void set_window_position(Pie pie) { +        if(PieManager.get_is_centered(pie.id)) this.set_position(Gtk.WindowPosition.CENTER); +        else                                   this.set_position(Gtk.WindowPosition.MOUSE);      }      ///////////////////////////////////////////////////////////////////// diff --git a/src/renderers/sliceRenderer.vala b/src/renderers/sliceRenderer.vala index 61c50b1..4803070 100644 --- a/src/renderers/sliceRenderer.vala +++ b/src/renderers/sliceRenderer.vala @@ -20,7 +20,7 @@ using GLib.Math;  namespace GnomePie {  /////////////////////////////////////////////////////////////////////////     -///  Renders a Slice of a Pie. According to the current theme. +/// Renders a Slice of a Pie. According to the current theme.  /////////////////////////////////////////////////////////////////////////  public class SliceRenderer : GLib.Object { @@ -123,7 +123,9 @@ public class SliceRenderer : GLib.Object {              this.caption = new RenderedText(action.name,                                               Config.global.theme.caption_width,                                              Config.global.theme.caption_height, -                                            Config.global.theme.caption_font); +                                            Config.global.theme.caption_font, +                                            Config.global.theme.caption_color, +                                            Config.global.global_scale);          this.active_icon = new ThemedIcon(action.icon, true);          this.inactive_icon = new ThemedIcon(action.icon, false); @@ -138,7 +140,8 @@ public class SliceRenderer : GLib.Object {          }          this.hotkey = new RenderedText(hotkey_label, (int)Config.global.theme.slice_radius*2, -                         (int)Config.global.theme.slice_radius*2, "sans 20"); +                         (int)Config.global.theme.slice_radius*2, "sans 20", +                         Config.global.theme.caption_color, Config.global.global_scale);      }      ///////////////////////////////////////////////////////////////////// @@ -185,6 +188,13 @@ public class SliceRenderer : GLib.Object {      /////////////////////////////////////////////////////////////////////      public void draw(double frame_time, Cairo.Context ctx, double angle, double distance) { +     +        // update the AnimatedValues +        this.scale.update(frame_time); +        this.alpha.update(frame_time); +        this.fade.update(frame_time); +        this.fade_scale.update(frame_time); +        this.fade_rotation.update(frame_time);  	    double direction = 2.0 * PI * position/parent.slice_count() + this.fade_rotation.val;  	    double max_scale = 1.0/Config.global.theme.max_zoom; @@ -205,13 +215,6 @@ public class SliceRenderer : GLib.Object {          if (fabs(this.scale.end - max_scale) > Config.global.theme.max_zoom*0.005)              this.scale.reset_target(max_scale, Config.global.theme.transition_time); -        // update the AnimatedValues -        this.scale.update(frame_time); -        this.alpha.update(frame_time); -        this.fade.update(frame_time); -        this.fade_scale.update(frame_time); -        this.fade_rotation.update(frame_time); -	              ctx.save();          // distance from the center diff --git a/src/themes/theme.vala b/src/themes/theme.vala index 284e1ef..269a574 100644 --- a/src/themes/theme.vala +++ b/src/themes/theme.vala @@ -55,6 +55,7 @@ public class Theme : GLib.Object {      public int    caption_height   {get; private set; default=100;}      public double caption_position {get; private set; default=0.0;}      public Color  caption_color    {get; private set; default=new Color();} +    public Icon   preview_icon     {get; private set; default=new Icon("gnome-pie", 36);}      public Gee.ArrayList<CenterLayer?> center_layers         {get; private set;}      public Gee.ArrayList<SliceLayer?>  active_slice_layers   {get; private set;} @@ -82,6 +83,8 @@ public class Theme : GLib.Object {          this.center_layers.clear();          this.active_slice_layers.clear();          this.inactive_slice_layers.clear(); +         +        this.preview_icon = new Icon(this.directory + "/preview.png", 36);          Xml.Parser.init();          string path = this.directory + "/theme.xml"; diff --git a/src/utilities/animatedValue.vala b/src/utilities/animatedValue.vala index 32ab889..7acc7a7 100644 --- a/src/utilities/animatedValue.vala +++ b/src/utilities/animatedValue.vala @@ -116,10 +116,16 @@ public class AnimatedValue : GLib.Object {      /////////////////////////////////////////////////////////////////////      public void reset_target(double end, double duration) { -        this.start = this.val;          this.end = end;          this.duration = duration; -        this.state = 0.0; +        this.start = this.val; +         +        if (duration == 0.0) { +            this.val = end; +            this.state = 1.0; +        } else { +            this.state = 0.0; +        }      }      ///////////////////////////////////////////////////////////////////// @@ -129,7 +135,7 @@ public class AnimatedValue : GLib.Object {      public void update(double time) {          this.state += time/this.duration; -        if (state < 1) { +        if (this.state < 1) {              switch (this.type) {                  case Type.LINEAR: @@ -152,6 +158,7 @@ public class AnimatedValue : GLib.Object {                  }                  break;              } +                      } else if (this.val != this.end) {               this.val = this.end;          }   diff --git a/src/utilities/bindingManager.vala b/src/utilities/bindingManager.vala index 437f4c1..5a4548e 100644 --- a/src/utilities/bindingManager.vala +++ b/src/utilities/bindingManager.vala @@ -54,6 +54,12 @@ public class BindingManager : GLib.Object {          Gdk.ModifierType.MOD2_MASK|Gdk.ModifierType.LOCK_MASK|Gdk.ModifierType.MOD5_MASK      }; +    ///////////////////////////////////////////////////////////////////// +    /// Some variables to remember which delayed binding was delayed. +    /// When the delay passes without another event indicating that the +    /// Trigger was released, the stored binding will be activated. +    ///////////////////////////////////////////////////////////////////// +          private uint32 delayed_count = 0;      private X.Event? delayed_event = null;      private Keybinding? delayed_binding = null; @@ -91,9 +97,8 @@ public class BindingManager : GLib.Object {      public void bind(Trigger trigger, string id) {          if(trigger.key_code != 0) { -            Gdk.Window rootwin = Gdk.get_default_root_window(); -            X.Display display = Gdk.x11_drawable_get_xdisplay(rootwin); -            X.ID xid = Gdk.x11_drawable_get_xid(rootwin); +            X.Display display = Gdk.x11_get_default_xdisplay(); +            X.ID xid = Gdk.x11_get_default_root_xwindow();              Gdk.error_trap_push(); @@ -121,9 +126,9 @@ public class BindingManager : GLib.Object {      /////////////////////////////////////////////////////////////////////      public void unbind(string id) { -        Gdk.Window rootwin = Gdk.get_default_root_window(); -        X.Display display = Gdk.x11_drawable_get_xdisplay(rootwin); -        X.ID xid = Gdk.x11_drawable_get_xid(rootwin); +        X.Display display = Gdk.x11_get_default_xdisplay(); +        X.ID xid = Gdk.x11_get_default_root_xwindow(); +                      Gee.List<Keybinding> remove_bindings = new Gee.ArrayList<Keybinding>();          foreach(var binding in bindings) {              if(id == binding.id) { @@ -185,6 +190,20 @@ public class BindingManager : GLib.Object {      }      ///////////////////////////////////////////////////////////////////// +    /// Returns whether the pie with the given ID opens centered. +    ///////////////////////////////////////////////////////////////////// +     +    public bool get_is_centered(string id) { +        foreach (var binding in bindings) { +            if (binding.id == id) { +                return binding.trigger.centered; +            } +        } +         +        return false; +    } +     +    /////////////////////////////////////////////////////////////////////      /// Returns the name ID of the Pie bound to the given Trigger.      /// Returns "" if there is nothing bound to this trigger.      ///////////////////////////////////////////////////////////////////// @@ -258,8 +277,7 @@ public class BindingManager : GLib.Object {          	// if the trigger is released and an event is currently waiting  		    // simulate that the trigger has been pressed without any inter-  		    // ference of Gnome-Pie -            Gdk.Window rootwin = Gdk.get_default_root_window(); -       		X.Display display = Gdk.x11_drawable_get_xdisplay(rootwin); +       		X.Display display = Gdk.x11_get_default_xdisplay();         		// unbind the trigger, else we'll capture that event again ;)         		unbind(delayed_binding.id); diff --git a/src/utilities/config.vala b/src/utilities/config.vala index cf4311d..5790eef 100644 --- a/src/utilities/config.vala +++ b/src/utilities/config.vala @@ -55,7 +55,6 @@ public class Config : GLib.Object {      public double refresh_rate { get; set; default = 60.0; }      public double global_scale { get; set; default = 1.0; }      public bool show_indicator { get; set; default = true; } -    public bool open_at_mouse { get; set; default = true; }      public bool auto_start { get; set; default = false; }      public Gee.ArrayList<Theme?> themes { get; private set; } @@ -71,7 +70,6 @@ public class Config : GLib.Object {                  writer.write_attribute("refresh_rate", refresh_rate.to_string());                  writer.write_attribute("global_scale", global_scale.to_string());                  writer.write_attribute("show_indicator", show_indicator ? "true" : "false"); -                writer.write_attribute("open_at_mouse", open_at_mouse ? "true" : "false");              writer.end_element();          writer.end_document();      } @@ -114,9 +112,6 @@ public class Config : GLib.Object {                          case "show_indicator":                              show_indicator = bool.parse(attr_content);                              break; -                        case "open_at_mouse": -                            open_at_mouse = bool.parse(attr_content); -                            break;                          default:                              warning("Invalid setting \"" + attr_name + "\" in gnome-pie.conf!");                              break; diff --git a/src/utilities/focusGrabber.vala b/src/utilities/focusGrabber.vala index 0e07b39..293e103 100644 --- a/src/utilities/focusGrabber.vala +++ b/src/utilities/focusGrabber.vala @@ -25,48 +25,95 @@ public class FocusGrabber : GLib.Object {      /////////////////////////////////////////////////////////////////////      /// Utilities for grabbing focus. -    /// Code from Gnome-Do/Synapse. +    /// Code roughly from Gnome-Do/Synapse.      ///////////////////////////////////////////////////////////////////// -    public static void grab(Gtk.Window window) { -        window.present_with_time(Gdk.CURRENT_TIME); -        window.get_window().raise(); -        window.get_window().focus(Gdk.CURRENT_TIME); +    public static void grab(Gdk.Window window, bool keyboard = true, bool pointer = true, bool owner_events = true) { +        if (keyboard || pointer) { +            window.raise(); +            window.focus(Gdk.CURRENT_TIME); -        int i = 0; -        Timeout.add(100, () => { -            if (++i >= 100) return false; -            return !try_grab_window(window); -        }); +            if (!try_grab_window(window, keyboard, pointer, owner_events)) { +                int i = 0; +                Timeout.add(100, () => { +                    if (++i >= 100) return false; +                    return !try_grab_window(window, keyboard, pointer, owner_events); +                }); +            } +        }      }      ///////////////////////////////////////////////////////////////////// -    /// Code from Gnome-Do/Synapse. +    /// Code roughly from Gnome-Do/Synapse.      ///////////////////////////////////////////////////////////////////// -    public static void ungrab(Gtk.Window window) { -        Gdk.pointer_ungrab(Gdk.CURRENT_TIME); -        Gdk.keyboard_ungrab(Gdk.CURRENT_TIME); -        Gtk.grab_remove(window); +    public static void ungrab(bool keyboard = true, bool pointer = true) { +        #if HAVE_GTK_3 +         +            var display = Gdk.Display.get_default(); +            var manager = display.get_device_manager(); +             +            unowned GLib.List<weak Gdk.Device?> list = manager.list_devices(Gdk.DeviceType.MASTER); +            foreach(var device in list) { +                if ((device.input_source == Gdk.InputSource.KEYBOARD && keyboard) +                 || (device.input_source != Gdk.InputSource.KEYBOARD && pointer))  +                  +                    device.ungrab(Gdk.CURRENT_TIME); +            } +             +        #else +         +            if (pointer)  Gdk.pointer_ungrab(Gdk.CURRENT_TIME); +            if (keyboard) Gdk.keyboard_ungrab(Gdk.CURRENT_TIME); +             +        #endif      }      ///////////////////////////////////////////////////////////////////// -    /// Code from Gnome-Do/Synapse. +    /// Code roughly from Gnome-Do/Synapse.      ///////////////////////////////////////////////////////////////////// -    private static bool try_grab_window(Gtk.Window window) { -        if (Gdk.pointer_grab(window.get_window(), true, Gdk.EventMask.BUTTON_PRESS_MASK |  -                             Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK, -                             null, null, Gdk.CURRENT_TIME) == Gdk.GrabStatus.SUCCESS) { +    private static bool try_grab_window(Gdk.Window window, bool keyboard, bool pointer, bool owner_events) { +        #if HAVE_GTK_3 +         +            var display = Gdk.Display.get_default(); +            var manager = display.get_device_manager(); -            if (Gdk.keyboard_grab(window.get_window(), true, Gdk.CURRENT_TIME) == Gdk.GrabStatus.SUCCESS) { -                Gtk.grab_add(window); +            bool grabbed_all = true; +             +            unowned GLib.List<weak Gdk.Device?> list = manager.list_devices(Gdk.DeviceType.MASTER); +            foreach(var device in list) { +                if ((device.input_source == Gdk.InputSource.KEYBOARD && keyboard)  +                 || (device.input_source != Gdk.InputSource.KEYBOARD && pointer)) { +                  +                    var status = device.grab(window, Gdk.GrabOwnership.APPLICATION, owner_events,  +                                             Gdk.EventMask.ALL_EVENTS_MASK, null, Gdk.CURRENT_TIME); +                     +                    if (status != Gdk.GrabStatus.SUCCESS) +                        grabbed_all = false; +                } +            } +             +            if (grabbed_all)                  return true; -            } else { -                Gdk.pointer_ungrab(Gdk.CURRENT_TIME); -                return false; +             +            ungrab(keyboard, pointer); +             +        #else +         +            if (!pointer || Gdk.pointer_grab(window, owner_events, Gdk.EventMask.BUTTON_PRESS_MASK | +                                             Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK, +                                 null, null, Gdk.CURRENT_TIME) == Gdk.GrabStatus.SUCCESS) { +                 +                if (!keyboard || Gdk.keyboard_grab(window, owner_events, Gdk.CURRENT_TIME) == Gdk.GrabStatus.SUCCESS) { +                    return true; +                } else if (pointer) { +                    ungrab(false, true); +                    return false; +                }              } -        } +        #endif +                  return false;      }    } diff --git a/src/utilities/key.vala b/src/utilities/key.vala index 6700b16..5f27a1e 100644 --- a/src/utilities/key.vala +++ b/src/utilities/key.vala @@ -55,10 +55,21 @@ public class Key : GLib.Object {      private Gdk.ModifierType modifiers;      ///////////////////////////////////////////////////////////////////// +    /// C'tor, initializes all members to defaults. +    ///////////////////////////////////////////////////////////////////// +     +    public Key() { +        this.accelerator = ""; +        this.modifiers = 0; +        this.key_code = 0; +        this.label = _("Not bound"); +    } +     +    /////////////////////////////////////////////////////////////////////      /// C'tor, initializes all members.      ///////////////////////////////////////////////////////////////////// -    public Key(string stroke) { +    public Key.from_string(string stroke) {          this.accelerator = stroke;          uint keysym; @@ -68,6 +79,17 @@ public class Key : GLib.Object {      }      ///////////////////////////////////////////////////////////////////// +    /// C'tor, initializes all members. +    ///////////////////////////////////////////////////////////////////// +     +    public Key.from_values(uint keysym, Gdk.ModifierType modifiers) { +        this.accelerator = Gtk.accelerator_name(keysym, modifiers); +        this.label = Gtk.accelerator_get_label(keysym, modifiers); +        this.key_code = display.keysym_to_keycode(keysym); +        this.modifiers = modifiers; +    } +     +    /////////////////////////////////////////////////////////////////////      /// Initializes static members.      ///////////////////////////////////////////////////////////////////// @@ -113,7 +135,7 @@ public class Key : GLib.Object {      private Gdk.ModifierType get_modifiers() {          Gdk.ModifierType modifiers; -        Gdk.Display.get_default().get_pointer(null, null, null, out modifiers); +        Gtk.get_current_event_state(out modifiers);          return modifiers;      } diff --git a/src/utilities/paths.vala b/src/utilities/paths.vala index 1c42176..589cc36 100644 --- a/src/utilities/paths.vala +++ b/src/utilities/paths.vala @@ -60,6 +60,13 @@ public class Paths : GLib.Object {      public static string locales { get; private set; default=""; }      ///////////////////////////////////////////////////////////////////// +    /// The directory containing UI declaration files +    /// usually /usr/share/gnome-pie/ui/. +    ///////////////////////////////////////////////////////////////////// +     +    public static string ui_files { get; private set; default=""; } +     +    /////////////////////////////////////////////////////////////////////      /// The autostart file of gnome-pie_config      /// usually ~/.config/autostart/gnome-pie.desktop.      ///////////////////////////////////////////////////////////////////// @@ -74,43 +81,47 @@ public class Paths : GLib.Object {      public static string launchers { get; private set; default=""; }      ///////////////////////////////////////////////////////////////////// +    /// The path to the executable. +    ///////////////////////////////////////////////////////////////////// +     +    public static string executable { get; private set; default=""; } +     +    /////////////////////////////////////////////////////////////////////      /// Initializes all values above.      /////////////////////////////////////////////////////////////////////      public static void init() { -        // append resources to icon search path to icon theme, if neccasary +        // get path of executable          try { -            var icon_dir = GLib.File.new_for_path(GLib.Path.get_dirname( -                        GLib.FileUtils.read_link("/proc/self/exe"))).get_child("resources"); -                         -            if (icon_dir.query_exists()) { -                string path = icon_dir.get_path(); -                Gtk.IconTheme.get_default().append_search_path(path); -            } -             -            Gtk.IconTheme.get_default().append_search_path("/usr/share/pixmaps/"); -             +            executable = GLib.File.new_for_path(GLib.FileUtils.read_link("/proc/self/exe")).get_path();          } catch (GLib.FileError e) {              warning("Failed to get path of executable!");          } +        // append resources to icon search path to icon theme, if neccasary +        var icon_dir = GLib.File.new_for_path(GLib.Path.get_dirname(executable)).get_child("resources"); +                     +        if (icon_dir.query_exists()) { +            string path = icon_dir.get_path(); +            Gtk.IconTheme.get_default().append_search_path(path); +        } +         +        Gtk.IconTheme.get_default().append_search_path("/usr/share/pixmaps/"); +              // get global paths          var default_dir = GLib.File.new_for_path("/usr/share/gnome-pie/");          if(!default_dir.query_exists()) {              default_dir = GLib.File.new_for_path("/usr/local/share/gnome-pie/");              if(!default_dir.query_exists()) { -                try { -                    default_dir = GLib.File.new_for_path(GLib.Path.get_dirname( -                        GLib.FileUtils.read_link("/proc/self/exe"))).get_child("resources"); -                } catch (GLib.FileError e) { -                    warning("Failed to get path of executable!"); -                } +                default_dir = GLib.File.new_for_path(GLib.Path.get_dirname( +                    executable)).get_child("resources");              }          }          global_themes = default_dir.get_path() + "/themes"; +        ui_files = default_dir.get_path() + "/ui";          // get locales path          var locale_dir = GLib.File.new_for_path("/usr/share/locale/de/LC_MESSAGES/gnomepie.mo"); @@ -121,22 +132,12 @@ public class Paths : GLib.Object {              if(locale_dir.query_exists()) {                  locale_dir = GLib.File.new_for_path("/usr/local/share/locale");              } else { -             -                try { -                    locale_dir = GLib.File.new_for_path(GLib.Path.get_dirname( -                        GLib.FileUtils.read_link("/proc/self/exe"))).get_child( -                        "resources/locale/de/LC_MESSAGES/gnomepie.mo"); -                } catch (GLib.FileError e) { -                    warning("Failed to get path of executable!"); -                } +                locale_dir = GLib.File.new_for_path(GLib.Path.get_dirname( +                    executable)).get_child("resources/locale/de/LC_MESSAGES/gnomepie.mo");                  if(locale_dir.query_exists()) { -                    try { -                        locale_dir = GLib.File.new_for_path(GLib.Path.get_dirname( -                            GLib.FileUtils.read_link("/proc/self/exe"))).get_child("resources/locale"); -                    } catch (GLib.FileError e) { -                        warning("Failed to get path of executable!"); -                    } +                    locale_dir = GLib.File.new_for_path(GLib.Path.get_dirname( +                        executable)).get_child("resources/locale");                  }              }          } @@ -204,7 +205,10 @@ public class Paths : GLib.Object {              warning("Failed to find launchers directory!");          if (!GLib.File.new_for_path(global_themes).query_exists())  -            warning("Failed to find global themes directory!");      +            warning("Failed to find global themes directory!");    +             +        if (!GLib.File.new_for_path(ui_files).query_exists())  +            warning("Failed to find UI files directory!");           }      } diff --git a/src/utilities/trigger.vala b/src/utilities/trigger.vala index 1f6fcfe..4b6167b 100644 --- a/src/utilities/trigger.vala +++ b/src/utilities/trigger.vala @@ -81,6 +81,12 @@ public class Trigger : GLib.Object {      public bool delayed { get; private set; default=false; }      ///////////////////////////////////////////////////////////////////// +    /// True if the pie opens in the middle of the screen. +    ///////////////////////////////////////////////////////////////////// +     +    public bool centered { get; private set; default=false; } +     +    /////////////////////////////////////////////////////////////////////      /// C'tor, creates a new, "unbound" Trigger.      ///////////////////////////////////////////////////////////////////// @@ -93,7 +99,7 @@ public class Trigger : GLib.Object {      /// in this format: "[option(s)]<modifier(s)>button" where      /// "<modifier>" is something like "<Alt>" or "<Control>", "button"      /// something like "s", "F4" or "button0" and "[option]" is either -    /// "[turbo]" or "["delayed"]". +    /// "[turbo]", "[centered]" or "["delayed"]".      /////////////////////////////////////////////////////////////////////      public Trigger.from_string(string trigger) { @@ -105,9 +111,12 @@ public class Trigger : GLib.Object {      /////////////////////////////////////////////////////////////////////      public Trigger.from_values(uint key_sym, Gdk.ModifierType modifiers,  -                               bool with_mouse, bool turbo, bool delayed ) { +                               bool with_mouse, bool turbo, bool delayed, +                               bool centered ) { -        string trigger = (turbo ? "[turbo]" : "") + (delayed ? "[delayed]" : ""); +        string trigger = (turbo ? "[turbo]" : "") +                       + (delayed ? "[delayed]" : "") +                       + (centered ? "[centered]" : "");          if (with_mouse) {              trigger += Gtk.accelerator_name(0, modifiers) + "button%u".printf(key_sym); @@ -123,7 +132,7 @@ public class Trigger : GLib.Object {      /// in this format: "[option(s)]<modifier(s)>button" where      /// "<modifier>" is something like "<Alt>" or "<Control>", "button"      /// something like "s", "F4" or "button0" and "[option]" is either -    /// "[turbo]" or "["delayed"]". +    /// "[turbo]", "[centered]" or "["delayed"]".      /////////////////////////////////////////////////////////////////////      public void parse_string(string trigger) { @@ -135,10 +144,12 @@ public class Trigger : GLib.Object {              this.turbo = check_string.contains("[turbo]");              this.delayed = check_string.contains("[delayed]"); +            this.centered = check_string.contains("[centered]");              // remove optional arguments              check_string = check_string.replace("[turbo]", "");              check_string = check_string.replace("[delayed]", ""); +            check_string = check_string.replace("[centered]", "");              int button = this.get_mouse_button(check_string);              if (button > 0) { @@ -177,12 +188,20 @@ public class Trigger : GLib.Object {              this.label_with_specials = this.label; -            if (this.turbo && this.delayed) -                this.label_with_specials += ("\n<small><span weight='light'>" + _("Turbo") + " | " + _("Delayed") + "</span></small>"); +            if (this.turbo && this.delayed && this.centered) +                this.label_with_specials += ("  <small><span weight='light'>[ " + _("Turbo") + " | " + _("Delayed") + " | " + _("Centered") + " ]</span></small>"); +            else if (this.turbo && this.centered) +                this.label_with_specials += ("  <small><span weight='light'>[ " + _("Turbo") + " | " + _("Centered") + " ]</span></small>"); +            else if (this.turbo && this.delayed) +                this.label_with_specials += ("  <small><span weight='light'>[ " + _("Turbo") + " | " + _("Delayed") + " ]</span></small>"); +            else if (this.centered && this.delayed) +                this.label_with_specials += ("  <small><span weight='light'>[ " + _("Delayed") + " | " + _("Centered") + " ]</span></small>");              else if (this.turbo) -                this.label_with_specials += ("\n<small><span weight='light'>" + _("Turbo") + "</span></small>"); +                this.label_with_specials += ("  <small><span weight='light'>[ " + _("Turbo") + " ]</span></small>");              else if (this.delayed) -                this.label_with_specials += ("\n<small><span weight='light'>" + _("Delayed") + "</span></small>"); +                this.label_with_specials += ("  <small><span weight='light'>[ " + _("Delayed") + " ]</span></small>"); +            else if (this.centered) +                this.label_with_specials += ("  <small><span weight='light'>[ " + _("Centered") + " ]</span></small>");          } else {              this.set_unbound(); @@ -216,6 +235,7 @@ public class Trigger : GLib.Object {          // remove optional arguments          check_string = check_string.replace("[turbo]", "");          check_string = check_string.replace("[delayed]", ""); +        check_string = check_string.replace("[centered]", "");          if (this.get_mouse_button(check_string) > 0) {              // it seems to be a valid mouse-trigger so replace button part, | 
