summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/deamon.vala8
-rw-r--r--src/gui/preferencesWindow.vala134
-rw-r--r--src/gui/themeList.vala20
-rw-r--r--src/gui/tipViewer.vala163
-rwxr-xr-xsrc/renderers/pieWindow.vala12
-rw-r--r--src/themes/theme.vala36
-rw-r--r--src/themes/themeImporter.vala62
-rw-r--r--src/utilities/archiveReader.vala123
-rw-r--r--src/utilities/archiveWriter.vala139
-rw-r--r--src/utilities/config.vala35
10 files changed, 708 insertions, 24 deletions
diff --git a/src/deamon.vala b/src/deamon.vala
index 8c84f3a..f4e1aeb 100644
--- a/src/deamon.vala
+++ b/src/deamon.vala
@@ -43,7 +43,7 @@ public class Deamon : GLib.Application {
/////////////////////////////////////////////////////////////////////
public static int main(string[] args) {
- version = "0.6.5";
+ version = "0.6.6";
// disable overlay scrollbar --- hacky workaround for black /
// transparent background
@@ -192,10 +192,12 @@ public class Deamon : GLib.Application {
}
if (reset) {
- if (GLib.FileUtils.remove(Paths.pie_config) == 0)
+ if (GLib.FileUtils.remove(Paths.pie_config) == 0) {
message("Removed file \"%s\"", Paths.pie_config);
- if (GLib.FileUtils.remove(Paths.settings) == 0)
+ }
+ if (GLib.FileUtils.remove(Paths.settings) == 0) {
message("Removed file \"%s\"", Paths.settings);
+ }
return true;
}
diff --git a/src/gui/preferencesWindow.vala b/src/gui/preferencesWindow.vala
index 5d22d6b..d671501 100644
--- a/src/gui/preferencesWindow.vala
+++ b/src/gui/preferencesWindow.vala
@@ -154,6 +154,17 @@ public class PreferencesWindow : GLib.Object {
scroll_area = builder.get_object("theme-scrolledwindow") as Gtk.ScrolledWindow;
scroll_area.add(this.theme_list);
+ (builder.get_object("theme-help-button") as Gtk.Button).clicked.connect(() => {
+ try{
+ GLib.AppInfo.launch_default_for_uri("http://simmesimme.github.io/lessons/2015/04/26/themes-for-gnome-pie/", null);
+ } catch (Error e) {
+ warning(e.message);
+ }
+ });
+
+ (builder.get_object("theme-export-button") as Gtk.Button).clicked.connect(on_export_theme_button_clicked);
+ (builder.get_object("theme-import-button") as Gtk.Button).clicked.connect(on_import_theme_button_clicked);
+
this.autostart = (builder.get_object("autostart-checkbox") as Gtk.ToggleButton);
this.autostart.toggled.connect(on_autostart_toggled);
@@ -209,6 +220,33 @@ public class PreferencesWindow : GLib.Object {
Config.global.max_visible_slices = (int)range_slices.get_value();
});
+ var info_box = (builder.get_object("info-box") as Gtk.Box);
+
+ // info label
+ var info_label = new TipViewer({
+ _("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://simmesimme.github.io/gnome-pie.html'>gnome-pie.simonschneegans.de</a>"),
+ _("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 can support the development of Gnome-Pie by donating via %s.").printf("<a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=X65SUVC4ZTQSC'>Paypal</a>"),
+ _("Translating Gnome-Pie to your language is easy. Translations are managed at %s.").printf("<a href='https://translate.zanata.org/zanata/iteration/view/gnome-pie/develop'>Zanata</a>"),
+ _("It's easy to create new themes for Gnome-Pie. Read the <a href='%s'>Tutorial</a> online.").printf("http://simmesimme.github.io/lessons/2015/04/26/themes-for-gnome-pie/"),
+ _("It's usually a good practive to have at most twelve slices per pie."),
+ _("You can export themes you created and share them with the community!"),
+ _("The source code of Gnome-Pie is available on %s.").printf("<a href='https://github.com/Simmesimme/Gnome-Pie'>Github</a>"),
+ _("Bugs can be reported at %s!").printf("<a href='https://github.com/Simmesimme/Gnome-Pie/issues'>Github</a>"),
+ _("Suggestions can be posted on %s!").printf("<a href='https://github.com/Simmesimme/Gnome-Pie/issues'>Github</a>"),
+ _("An awesome companion of Gnome-Pie is %s. It will make using your computer feel like magic!").printf("<a href='https://github.com/thjaeger/easystroke/wiki'>Easystroke</a>"),
+ _("You can drag'n'drop applications from your main menu to the pie above."),
+ _("You may drag'n'drop URLs and bookmarks from your internet browser to the pie above."),
+ _("You can drag'n'drop files and folders from your file browser to the pie above."),
+ _("You can drag'n'drop pies from the list on the left into other pies in order to create sub-pies."),
+ _("You can drag'n'drop pies from the list on the left to your desktop or dock to create a launcher for this pie.")
+ });
+ this.window.show.connect(info_label.start_slide_show);
+ this.window.hide.connect(info_label.stop_slide_show);
+
+ info_box.pack_end(info_label);
+
this.window.hide.connect(() => {
// save settings on close
Config.global.save();
@@ -290,15 +328,105 @@ public class PreferencesWindow : GLib.Object {
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,
+ var d = new Gtk.MessageDialog(this.window, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
"%s", e.message);
- d.run ();
- d.destroy ();
+ d.run();
+ d.destroy();
}
}
}
/////////////////////////////////////////////////////////////////////
+ /// Saves the current theme to an archive.
+ /////////////////////////////////////////////////////////////////////
+
+ private void on_export_theme_button_clicked(Gtk.Button button) {
+ var dialog = new Gtk.FileChooserDialog("Pick a file", this.window,
+ Gtk.FileChooserAction.SAVE,
+ "_Cancel",
+ Gtk.ResponseType.CANCEL,
+ "_Save",
+ Gtk.ResponseType.ACCEPT);
+
+ dialog.set_do_overwrite_confirmation(true);
+ dialog.set_modal(true);
+ dialog.filter = new Gtk.FileFilter();
+ dialog.filter.add_pattern ("*.tar.gz");
+ dialog.set_current_name(Config.global.theme.name + ".tar.gz");
+
+ dialog.response.connect((d, result) => {
+ if (result == Gtk.ResponseType.ACCEPT) {
+ var file = dialog.get_filename();
+ if (!file.has_suffix(".tar.gz")) {
+ file = file + ".tar.gz";
+ }
+ Config.global.theme.export(file);
+ }
+ dialog.destroy();
+ });
+ dialog.show();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Imports a new theme from an archive.
+ /////////////////////////////////////////////////////////////////////
+
+ private void on_import_theme_button_clicked(Gtk.Button button) {
+ var dialog = new Gtk.FileChooserDialog("Pick a file", this.window,
+ Gtk.FileChooserAction.OPEN,
+ "_Cancel",
+ Gtk.ResponseType.CANCEL,
+ "_Open",
+ Gtk.ResponseType.ACCEPT);
+
+ dialog.set_modal(true);
+ dialog.filter = new Gtk.FileFilter();
+ dialog.filter.add_pattern ("*.tar.gz");
+
+ var result = Gtk.MessageType.INFO;
+ var message = _("Sucessfully imported new theme!");
+
+ dialog.response.connect((d, r) => {
+ if (r == Gtk.ResponseType.ACCEPT) {
+ var file = dialog.get_filename();
+
+ var a = new ThemeImporter();
+ if (a.open(file)) {
+ if (a.is_valid_theme) {
+ if (!Config.global.has_theme(a.theme_name)) {
+ if (a.extract_to(Paths.local_themes + "/" + a.theme_name)) {
+ Config.global.load_themes(a.theme_name);
+ this.theme_list.reload();
+ } else {
+ message = _("An error occured while importing the theme: Failed to extract theme!");
+ result = Gtk.MessageType.ERROR;
+ }
+ } else {
+ message = _("An error occured while importing the theme: A theme with this name does already exist!");
+ result = Gtk.MessageType.ERROR;
+ }
+ } else {
+ message = _("An error occured while importing the theme: Theme archive does not contain a valid theme!");
+ result = Gtk.MessageType.ERROR;
+ }
+ } else {
+ message = _("An error occured while importing the theme: Failed to open theme archive!");
+ result = Gtk.MessageType.ERROR;
+ }
+ a.close();
+
+ var result_dialog = new Gtk.MessageDialog(null, Gtk.DialogFlags.MODAL,
+ result, Gtk.ButtonsType.CLOSE, message);
+ result_dialog.run();
+ result_dialog.destroy();
+ }
+ dialog.destroy();
+
+ });
+ dialog.show();
+ }
+
+ /////////////////////////////////////////////////////////////////////
/// Shows or hides the indicator.
/////////////////////////////////////////////////////////////////////
diff --git a/src/gui/themeList.vala b/src/gui/themeList.vala
index 517c6d5..46ae876 100644
--- a/src/gui/themeList.vala
+++ b/src/gui/themeList.vala
@@ -51,9 +51,6 @@ class ThemeList : Gtk.TreeView {
public ThemeList() {
GLib.Object();
- 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);
@@ -79,7 +76,7 @@ class ThemeList : Gtk.TreeView {
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());
+ int index = int.parse(this.model.get_path(active).to_string());
Config.global.theme = Config.global.themes[index];
this.on_select_new();
@@ -91,6 +88,15 @@ class ThemeList : Gtk.TreeView {
}
});
+ reload();
+ }
+
+ public void reload() {
+
+ var data = new Gtk.ListStore(2, typeof(Gdk.Pixbuf),
+ typeof(string));
+ this.set_model(data);
+
// load all themes into the list
var themes = Config.global.themes;
foreach(var theme in themes) {
@@ -99,10 +105,10 @@ class ThemeList : Gtk.TreeView {
data.set(current, DataPos.ICON, theme.preview_icon.to_pixbuf());
data.set(current, DataPos.NAME, GLib.Markup.escape_text(theme.name)+"\n"
+ "<span font-size='x-small'>" + GLib.Markup.escape_text(theme.description)
- + " - <i>"+GLib.Markup.escape_text(_("By")+" "+theme.author)
- + "</i></span>");
- if(theme == Config.global.theme)
+ + "</span>");
+ if(theme == Config.global.theme) {
get_selection().select_iter(current);
+ }
}
}
}
diff --git a/src/gui/tipViewer.vala b/src/gui/tipViewer.vala
new file mode 100644
index 0000000..e2158bd
--- /dev/null
+++ b/src/gui/tipViewer.vala
@@ -0,0 +1,163 @@
+/////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2015 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 base_delay = 3.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;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The fading value.
+ /////////////////////////////////////////////////////////////////////
+
+ private AnimatedValue alpha;
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, initializes all members and sets the basic layout.
+ /////////////////////////////////////////////////////////////////////
+
+ public TipViewer(string[] tips) {
+ this.tips = tips;
+
+ this.alpha = new AnimatedValue.linear(0.0, 1.0, fade_time);
+
+ this.set_alignment (0.0f, 0.5f);
+ this.opacity = 0;
+ this.wrap = true;
+ this.valign = Gtk.Align.END;
+ this.set_use_markup(true);
+
+ this.override_font(Pango.FontDescription.from_string("8"));
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Starts the playback of tips.
+ /////////////////////////////////////////////////////////////////////
+
+ public void start_slide_show() {
+ if (!this.playing && tips.length > 1) {
+ this.playing = true;
+ show_tip();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// 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, fade_time);
+
+ GLib.Timeout.add((uint)(1000.0/frame_rate), () => {
+ this.alpha.update(1.0/frame_rate);
+ this.opacity = this.alpha.val;
+
+ return (this.alpha.val != 1.0);
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Starts the fading out.
+ /////////////////////////////////////////////////////////////////////
+
+ private void fade_out() {
+ this.alpha = new AnimatedValue.linear(this.alpha.val, 0.0, fade_time);
+
+ GLib.Timeout.add((uint)(1000.0/frame_rate), () => {
+ this.alpha.update(1.0/frame_rate);
+ this.opacity = this.alpha.val;
+
+ return (this.alpha.val != 0.0);
+ });
+ }
+
+ private void show_tip() {
+
+ this.set_random_tip();
+
+ this.fade_in();
+
+ uint delay = (uint)(base_delay*1000.0) + tips[this.index].length*30;
+
+ GLib.Timeout.add(delay, () => {
+ this.fade_out();
+
+ if (this.playing) {
+ GLib.Timeout.add((uint)(1000.0*fade_time), () => {
+ this.show_tip();
+ return false;
+ });
+ }
+
+ return false;
+ });
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// 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/renderers/pieWindow.vala b/src/renderers/pieWindow.vala
index a49813a..c1d70b7 100755
--- a/src/renderers/pieWindow.vala
+++ b/src/renderers/pieWindow.vala
@@ -149,10 +149,12 @@ public class PieWindow : Gtk.Window {
// remember last pressed key in order to disable key repeat
uint last_key = 0;
+ uint32 last_time_stamp = 0;
this.key_press_event.connect((e) => {
if (e.keyval != last_key) {
- last_key = e.keyval;
- this.handle_key_press(e.keyval, e.time, e.str);
+ this.handle_key_press(e.keyval, e.time, last_time_stamp, e.str);
+ last_key = e.keyval;
+ last_time_stamp = e.time;
}
return true;
});
@@ -436,7 +438,11 @@ public class PieWindow : Gtk.Window {
/// Do some useful stuff when keys are pressed.
/////////////////////////////////////////////////////////////////////
- private void handle_key_press(uint key, uint32 time_stamp, string text) {
+ private void handle_key_press(uint key, uint32 time_stamp, uint32 last_time_stamp, string text) {
+ if (last_time_stamp + 1000 < time_stamp) {
+ this.search_string = "";
+ }
+
if (Gdk.keyval_name(key) == "Escape") this.cancel();
else if (Gdk.keyval_name(key) == "Return") this.activate_slice(time_stamp);
else if (!PieManager.get_is_turbo(this.renderer.id)) {
diff --git a/src/themes/theme.vala b/src/themes/theme.vala
index a7dd4f8..98e8994 100644
--- a/src/themes/theme.vala
+++ b/src/themes/theme.vala
@@ -115,6 +115,42 @@ public class Theme : GLib.Object {
return true;
}
+
+ /////////////////////////////////////////////////////////////////////
+ /// Exports the theme directory to an importable archive.
+ /////////////////////////////////////////////////////////////////////
+
+ public void export(string file) {
+
+ var archive = new ArchiveWriter();
+ bool success = true;
+
+ if (!archive.open(file)) {
+ warning("Cannot open file " + file + " for writing!");
+ success = false;
+ } else if (!archive.add(this.directory)) {
+ warning("Cannot append directory " + this.directory + " to archive!");
+ success = false;
+ }
+
+ archive.close();
+
+ if (success) {
+ var message = _("Successfully exported the theme \"%s\"!").printf(this.name);
+ var dialog = new Gtk.MessageDialog(null, Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, message);
+ dialog.run();
+ dialog.destroy();
+
+ } else {
+ var message = _("An error occured while exporting the theme \"%s\"! Please check the console output.").printf(this.name);
+ var dialog = new Gtk.MessageDialog(null, Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, message);
+ dialog.run();
+ dialog.destroy();
+ }
+ }
+
/////////////////////////////////////////////////////////////////////
/// Loads all images of the theme.
/////////////////////////////////////////////////////////////////////
diff --git a/src/themes/themeImporter.vala b/src/themes/themeImporter.vala
new file mode 100644
index 0000000..f110696
--- /dev/null
+++ b/src/themes/themeImporter.vala
@@ -0,0 +1,62 @@
+/////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2015 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 class provides functions to check whether an archive contains a
+/// valid Gnome-Pie theme.
+/////////////////////////////////////////////////////////////////////////
+
+public class ThemeImporter : ArchiveReader {
+
+ public bool is_valid_theme;
+ public string theme_name;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns
+ /////////////////////////////////////////////////////////////////////
+
+ public new bool open(string path) {
+
+ this.is_valid_theme = false;
+ this.theme_name = "";
+
+ var tmp_reader = new ArchiveReader();
+
+ if (tmp_reader.open(path)) {
+ try {
+ var tmp_dir = GLib.DirUtils.make_tmp("gnomepieXXXXXX");
+ if (tmp_reader.extract_to(tmp_dir)) {
+ var tmp_theme = new Theme(tmp_dir);
+ if (tmp_theme.load()) {
+ is_valid_theme = true;
+ theme_name = tmp_theme.name;
+ }
+ }
+ } catch (Error e) {
+ warning(e.message);
+ }
+ }
+
+ tmp_reader.close();
+
+ return base.open(path);
+ }
+}
+
+}
diff --git a/src/utilities/archiveReader.vala b/src/utilities/archiveReader.vala
new file mode 100644
index 0000000..16e4541
--- /dev/null
+++ b/src/utilities/archiveReader.vala
@@ -0,0 +1,123 @@
+/////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2015 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 class can be used to unpack an archive to a directory.
+/////////////////////////////////////////////////////////////////////////
+
+public class ArchiveReader : GLib.Object {
+
+ private Archive.Read archive;
+ private Archive.WriteDisk writer;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Constructs a new ArchiveReader
+ /////////////////////////////////////////////////////////////////////
+
+ public ArchiveReader() {
+ this.archive = new Archive.Read();
+ this.archive.support_format_all();
+ this.archive.support_filter_all();
+
+ this.writer = new Archive.WriteDisk();
+ this.writer.set_options(
+ Archive.ExtractFlags.TIME |
+ Archive.ExtractFlags.PERM |
+ Archive.ExtractFlags.ACL |
+ Archive.ExtractFlags.FFLAGS
+ );
+ this.writer.set_standard_lookup();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Call this once after you created the ArchiveReader. Pass the
+ /// path to the target archive location.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool open(string path) {
+ return this.archive.open_filename(path, 10240) == Archive.Result.OK;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Extracts all files from the previously opened archive.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool extract_to(string directory) {
+ while (true) {
+ unowned Archive.Entry entry;
+ var r = this.archive.next_header(out entry);
+
+ if (r == Archive.Result.EOF) {
+ break;
+ }
+
+ if (r != Archive.Result.OK) {
+ warning(this.archive.error_string());
+ return false;
+ }
+
+ entry.set_pathname(directory + "/" + entry.pathname());
+
+ r = this.writer.write_header(entry);
+
+ if (r != Archive.Result.OK) {
+ warning(this.writer.error_string());
+ return false;
+ }
+
+ if (entry.size() > 0) {
+ while (true) {
+ size_t offset, size;
+ void *buff;
+
+ r = this.archive.read_data_block(out buff, out size, out offset);
+ if (r == Archive.Result.EOF) {
+ break;
+ }
+
+ if (r != Archive.Result.OK) {
+ warning(this.archive.error_string());
+ return false;
+ }
+
+ this.writer.write_data_block(buff, size, offset);
+ }
+ }
+
+ r = this.writer.finish_entry();
+
+ if (r != Archive.Result.OK) {
+ warning(this.writer.error_string());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// When all files have been added, close the directory again.
+ /////////////////////////////////////////////////////////////////////
+
+ public void close() {
+ this.archive.close();
+ this.writer.close();
+ }
+}
+
+}
diff --git a/src/utilities/archiveWriter.vala b/src/utilities/archiveWriter.vala
new file mode 100644
index 0000000..92bd31b
--- /dev/null
+++ b/src/utilities/archiveWriter.vala
@@ -0,0 +1,139 @@
+/////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2015 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 class can be used to pack a directory of files recursively into
+/// a *.tar.gz archive.
+/////////////////////////////////////////////////////////////////////////
+
+public class ArchiveWriter : GLib.Object {
+
+ private Archive.Write archive;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Constructs a new ArchiveWriter
+ /////////////////////////////////////////////////////////////////////
+
+ public ArchiveWriter() {
+ this.archive = new Archive.Write();
+ this.archive.add_filter_gzip();
+ this.archive.set_format_pax_restricted();
+
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Call this once after you created the ArchiveWriter. Pass the
+ /// path to the target archive location.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool open(string path) {
+ return this.archive.open_filename(path) == Archive.Result.OK;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Adds all files of a given directory to the previously opened
+ /// archive.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool add(string directory) {
+ return add_directory(directory, directory);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// When all files have been added, close the directory again.
+ /////////////////////////////////////////////////////////////////////
+
+ public void close() {
+ this.archive.close();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Private helper function which traveres a directory recursively.
+ /////////////////////////////////////////////////////////////////////
+
+ private bool add_directory(string directory, string relative_to) {
+ try {
+ var d = Dir.open(directory);
+ string name;
+ while ((name = d.read_name()) != null) {
+ string path = Path.build_filename(directory, name);
+ if (FileUtils.test(path, FileTest.IS_DIR)) {
+ if (!add_directory(path, relative_to)) {
+ return false;
+ }
+
+ } else if (FileUtils.test(path, FileTest.IS_REGULAR)) {
+ if (!add_file(path, relative_to)) {
+ return false;
+ }
+
+ } else {
+ warning("Packaging theme: Ignoring irregular file " + name);
+ }
+ }
+ } catch (Error e) {
+ warning (e.message);
+ return false;
+ }
+
+ return true;
+
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Private halper which adds a file to the archive.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool add_file(string path, string relative_to) {
+ var entry = new Archive.Entry();
+ entry.set_pathname(path.replace(relative_to, ""));
+
+ Posix.Stat st;
+ Posix.stat(path, out st);
+ entry.copy_stat(st);
+ entry.set_size(st.st_size);
+
+ if (this.archive.write_header(entry) == Archive.Result.OK) {
+ try {
+ var reader = File.new_for_path(path).read();
+ uint8 buffer[4096];
+
+ var len = reader.read(buffer);
+
+ while(len > 0) {
+ this.archive.write_data(buffer, len);
+ len = reader.read(buffer);
+ }
+
+ this.archive.finish_entry();
+ } catch (Error e) {
+ warning (e.message);
+ return false;
+ }
+
+ } else {
+ warning("Failed to include file " + path + " into archive");
+ return false;
+ }
+
+ return true;
+ }
+}
+
+}
diff --git a/src/utilities/config.vala b/src/utilities/config.vala
index 538602d..5dedddb 100644
--- a/src/utilities/config.vala
+++ b/src/utilities/config.vala
@@ -161,7 +161,9 @@ public class Config : GLib.Object {
load_themes(theme_name);
- if (error_occrured) save();
+ if (error_occrured) {
+ save();
+ }
}
/////////////////////////////////////////////////////////////////////
@@ -177,18 +179,19 @@ public class Config : GLib.Object {
// load global themes
var d = Dir.open(Paths.global_themes);
while ((name = d.read_name()) != null) {
- var theme = new Theme(Paths.global_themes + "/" + name);
+ var new_theme = new Theme(Paths.global_themes + "/" + name);
- if (theme.load())
- themes.add(theme);
+ if (new_theme.load()) {
+ themes.add(new_theme);
+ }
}
// load local themes
d = Dir.open(Paths.local_themes);
while ((name = d.read_name()) != null) {
- var theme = new Theme(Paths.local_themes + "/" + name);
- if (theme.load())
- themes.add(theme);
+ var new_theme = new Theme(Paths.local_themes + "/" + name);
+ if (new_theme.load())
+ themes.add(new_theme);
}
} catch (Error e) {
@@ -211,10 +214,26 @@ public class Config : GLib.Object {
warning("Theme \"" + current + "\" not found! Using fallback...");
}
theme.load_images();
+ } else {
+ error("No theme found!");
}
- else error("No theme found!");
}
+ /////////////////////////////////////////////////////////////////////
+ /// Returns true if a loaded theme has the given name or is in a
+ /// directory with the given name.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool has_theme(string name) {
+
+ foreach (var theme in themes) {
+ if (theme.name == name || theme.directory.has_suffix(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
}