Trash and Spam folders may now be emptied by Geary. A user warning is presented, as the operation cannot be undone and deletes mail on the server, not just locally.
This commit is contained in:
parent
080becfc98
commit
260436e92c
11 changed files with 276 additions and 18 deletions
|
|
@ -29,6 +29,7 @@ engine/api/geary-folder-properties.vala
|
|||
engine/api/geary-folder-supports-archive.vala
|
||||
engine/api/geary-folder-supports-copy.vala
|
||||
engine/api/geary-folder-supports-create.vala
|
||||
engine/api/geary-folder-supports-empty.vala
|
||||
engine/api/geary-folder-supports-mark.vala
|
||||
engine/api/geary-folder-supports-move.vala
|
||||
engine/api/geary-folder-supports-remove.vala
|
||||
|
|
@ -205,6 +206,7 @@ engine/imap-engine/outlook/imap-engine-outlook-drafts-folder.vala
|
|||
engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
|
||||
engine/imap-engine/replay-ops/imap-engine-copy-email.vala
|
||||
engine/imap-engine/replay-ops/imap-engine-create-email.vala
|
||||
engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
|
||||
engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
|
||||
engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
|
||||
engine/imap-engine/replay-ops/imap-engine-list-email-by-sparse-id.vala
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ public class GearyController : Geary.BaseObject {
|
|||
public const string ACTION_ARCHIVE_MESSAGE = "GearyArchiveMessage";
|
||||
public const string ACTION_TRASH_MESSAGE = "GearyTrashMessage";
|
||||
public const string ACTION_DELETE_MESSAGE = "GearyDeleteMessage";
|
||||
public const string ACTION_EMPTY_MENU = "GearyEmptyMenu";
|
||||
public const string ACTION_EMPTY_SPAM = "GearyEmptySpam";
|
||||
public const string ACTION_EMPTY_TRASH = "GearyEmptyTrash";
|
||||
public const string ACTION_FIND_IN_CONVERSATION = "GearyFindInConversation";
|
||||
public const string ACTION_FIND_NEXT_IN_CONVERSATION = "GearyFindNextInConversation";
|
||||
public const string ACTION_FIND_PREVIOUS_IN_CONVERSATION = "GearyFindPreviousInConversation";
|
||||
|
|
@ -358,7 +361,7 @@ public class GearyController : Geary.BaseObject {
|
|||
add_accelerator("F", ACTION_FORWARD_MESSAGE);
|
||||
|
||||
Gtk.ActionEntry find_in_conversation = { ACTION_FIND_IN_CONVERSATION, null, null, "<Ctrl>F",
|
||||
null, on_find_in_conversation_action };
|
||||
null, on_find_in_conversation_action };
|
||||
entries += find_in_conversation;
|
||||
add_accelerator("slash", ACTION_FIND_IN_CONVERSATION);
|
||||
|
||||
|
|
@ -388,7 +391,21 @@ public class GearyController : Geary.BaseObject {
|
|||
delete_message.tooltip = DELETE_MESSAGE_TOOLTIP_SINGLE;
|
||||
entries += delete_message;
|
||||
add_accelerator("<Shift>BackSpace", ACTION_DELETE_MESSAGE);
|
||||
|
||||
|
||||
Gtk.ActionEntry empty_menu = { ACTION_EMPTY_MENU, "edit-clear-all-symbolic", null, null,
|
||||
null, null };
|
||||
empty_menu.label = _("Empty");
|
||||
empty_menu.tooltip = _("Empty Spam or Trash folders");
|
||||
entries += empty_menu;
|
||||
|
||||
Gtk.ActionEntry empty_spam = { ACTION_EMPTY_SPAM, null, null, null, null, on_empty_spam };
|
||||
empty_spam.label = _("Empty _Spam…");
|
||||
entries += empty_spam;
|
||||
|
||||
Gtk.ActionEntry empty_trash = { ACTION_EMPTY_TRASH, null, null, null, null, on_empty_trash };
|
||||
empty_trash.label = _("Empty _Trash…");
|
||||
entries += empty_trash;
|
||||
|
||||
Gtk.ActionEntry zoom_in = { ACTION_ZOOM_IN, null, null, "<Ctrl>equal",
|
||||
null, on_zoom_in };
|
||||
entries += zoom_in;
|
||||
|
|
@ -2257,6 +2274,83 @@ public class GearyController : Geary.BaseObject {
|
|||
on_archive_or_delete_selection_finished);
|
||||
}
|
||||
|
||||
private void on_empty_spam() {
|
||||
on_empty_trash_or_spam(Geary.SpecialFolderType.SPAM);
|
||||
}
|
||||
|
||||
private void on_empty_trash() {
|
||||
on_empty_trash_or_spam(Geary.SpecialFolderType.TRASH);
|
||||
}
|
||||
|
||||
private void on_empty_trash_or_spam(Geary.SpecialFolderType special_folder_type) {
|
||||
// Account must be in place, must have the specified special folder type, and that folder
|
||||
// must support Empty in order for this command to proceed
|
||||
if (current_account == null)
|
||||
return;
|
||||
|
||||
Geary.Folder? folder = null;
|
||||
try {
|
||||
folder = current_account.get_special_folder(special_folder_type);
|
||||
} catch (Error err) {
|
||||
debug("%s: Unable to get special folder %s: %s", current_account.to_string(),
|
||||
special_folder_type.to_string(), err.message);
|
||||
|
||||
// fall through
|
||||
}
|
||||
|
||||
if (folder == null)
|
||||
return;
|
||||
|
||||
Geary.FolderSupport.Empty? emptyable = folder as Geary.FolderSupport.Empty;
|
||||
if (emptyable == null) {
|
||||
debug("%s: Special folder %s (%s) does not support emptying", current_account.to_string(),
|
||||
folder.path.to_string(), special_folder_type.to_string());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ConfirmationDialog dialog = new ConfirmationDialog(main_window,
|
||||
_("Empty all email from your %s folder?").printf(special_folder_type.get_display_name()),
|
||||
_("This removes the email from Geary and your email server.")
|
||||
+ " <b>" + _("This cannot be undone.") + "</b>",
|
||||
_("Empty %s").printf(special_folder_type.get_display_name()));
|
||||
dialog.use_secondary_markup(true);
|
||||
dialog.set_focus_response(Gtk.ResponseType.CANCEL);
|
||||
|
||||
if (dialog.run() == Gtk.ResponseType.OK)
|
||||
empty_folder_async.begin(emptyable, cancellable_folder);
|
||||
}
|
||||
|
||||
private async void empty_folder_async(Geary.FolderSupport.Empty emptyable, Cancellable? cancellable) {
|
||||
try {
|
||||
yield do_empty_folder_async(emptyable, cancellable);
|
||||
} catch (Error err) {
|
||||
// don't report to user if cancelled
|
||||
if (cancellable is IOError.CANCELLED)
|
||||
return;
|
||||
|
||||
ErrorDialog dialog = new ErrorDialog(main_window,
|
||||
_("Error emptying %s").printf(emptyable.get_display_name()), err.message);
|
||||
dialog.run();
|
||||
}
|
||||
}
|
||||
|
||||
private async void do_empty_folder_async(Geary.FolderSupport.Empty emptyable, Cancellable? cancellable)
|
||||
throws Error {
|
||||
yield emptyable.open_async(Geary.Folder.OpenFlags.NONE, cancellable);
|
||||
|
||||
// be sure to close in all code paths
|
||||
try {
|
||||
yield emptyable.empty_folder_async(cancellable);
|
||||
} finally {
|
||||
try {
|
||||
yield emptyable.close_async(null);
|
||||
} catch (Error err) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool current_folder_supports_trash() {
|
||||
return (current_folder != null && current_folder.special_folder_type != Geary.SpecialFolderType.TRASH
|
||||
&& !current_folder.properties.is_local_only && current_account != null
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public class MainToolbar : PillHeaderbar {
|
|||
|
||||
private Gtk.Button archive_button;
|
||||
private Gtk.Button trash_buttons[2];
|
||||
private Gtk.MenuButton empty_buttons[2];
|
||||
private Gtk.SearchEntry search_entry = new Gtk.SearchEntry();
|
||||
private Geary.ProgressMonitor? search_upgrade_progress_monitor = null;
|
||||
private MonitoredProgressBar search_upgrade_progress_bar = new MonitoredProgressBar();
|
||||
|
|
@ -61,6 +62,11 @@ public class MainToolbar : PillHeaderbar {
|
|||
insert.add(create_menu_button("folder-symbolic", move_folder_menu, GearyController.ACTION_MOVE_MENU));
|
||||
add_start(create_pill_buttons(insert));
|
||||
|
||||
// Assemble the empty menu
|
||||
GearyApplication.instance.load_ui_file("toolbar_empty_menu.ui");
|
||||
Gtk.Menu empty_menu = (Gtk.Menu) GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarEmptyMenu");
|
||||
empty_menu.foreach(GtkUtil.show_menuitem_accel_labels);
|
||||
|
||||
// The toolbar looks bad when you hide one of a pair of pill buttons.
|
||||
// Unfortunately, this means we have to have one pair for archive/trash
|
||||
// and one single button for just trash, for when the archive button is
|
||||
|
|
@ -68,10 +74,12 @@ public class MainToolbar : PillHeaderbar {
|
|||
insert.clear();
|
||||
insert.add(archive_button = create_toolbar_button(null, GearyController.ACTION_ARCHIVE_MESSAGE, true));
|
||||
insert.add(trash_buttons[0] = create_toolbar_button(null, GearyController.ACTION_TRASH_MESSAGE, true));
|
||||
Gtk.Box trash_archive = create_pill_buttons(insert);
|
||||
insert.add(empty_buttons[0] = create_menu_button(null, empty_menu, GearyController.ACTION_EMPTY_MENU));
|
||||
Gtk.Box trash_archive_empty = create_pill_buttons(insert);
|
||||
insert.clear();
|
||||
insert.add(trash_buttons[1] = create_toolbar_button(null, GearyController.ACTION_TRASH_MESSAGE, true));
|
||||
Gtk.Box trash = create_pill_buttons(insert, false);
|
||||
insert.add(empty_buttons[1] = create_menu_button(null, empty_menu, GearyController.ACTION_EMPTY_MENU));
|
||||
Gtk.Box trash_empty = create_pill_buttons(insert, false);
|
||||
|
||||
// Search bar.
|
||||
search_entry.width_chars = 28;
|
||||
|
|
@ -88,8 +96,8 @@ public class MainToolbar : PillHeaderbar {
|
|||
|
||||
// pack_end() ordering is reversed in GtkHeaderBar in 3.12 and above
|
||||
#if !GTK_3_12
|
||||
add_end(trash_archive);
|
||||
add_end(trash);
|
||||
add_end(trash_archive_empty);
|
||||
add_end(trash_empty);
|
||||
add_end(search_upgrade_progress_bar);
|
||||
add_end(search_entry);
|
||||
#endif
|
||||
|
|
@ -105,8 +113,8 @@ public class MainToolbar : PillHeaderbar {
|
|||
#if GTK_3_12
|
||||
add_end(search_entry);
|
||||
add_end(search_upgrade_progress_bar);
|
||||
add_end(trash);
|
||||
add_end(trash_archive);
|
||||
add_end(trash_empty);
|
||||
add_end(trash_archive_empty);
|
||||
#endif
|
||||
|
||||
set_search_placeholder_text(DEFAULT_SEARCH_TEXT);
|
||||
|
|
@ -115,12 +123,12 @@ public class MainToolbar : PillHeaderbar {
|
|||
private void show_archive_button(bool show) {
|
||||
if (show) {
|
||||
archive_button.show();
|
||||
trash_buttons[0].show();
|
||||
trash_buttons[1].hide();
|
||||
trash_buttons[0].visible = empty_buttons[0].visible = true;
|
||||
trash_buttons[1].visible = empty_buttons[1].visible = false;
|
||||
} else {
|
||||
archive_button.hide();
|
||||
trash_buttons[0].hide();
|
||||
trash_buttons[1].show();
|
||||
trash_buttons[0].visible = empty_buttons[0].visible = false;
|
||||
trash_buttons[1].visible = empty_buttons[1].visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
25
src/engine/api/geary-folder-supports-empty.vala
Normal file
25
src/engine/api/geary-folder-supports-empty.vala
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* Copyright 2015 Yorba Foundation
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The addition of the Geary.FolderSupport.Empty interface to a {@link Geary.Folder}
|
||||
* indicates that it supports removing (deleting) all email quickly.
|
||||
*
|
||||
* This generally means that the message is deleted from the server and is not recoverable.
|
||||
* It does ''not'' mean the messages are moved to a Trash folder where they may or may not be
|
||||
* automatically deleted some time later. Users invoking empty are expecting all contents on the
|
||||
* remote to be removed entirely, whether or not any or all of them have been synchronized locally.
|
||||
*
|
||||
* @see FolderSupport.Remove
|
||||
*/
|
||||
|
||||
public interface Geary.FolderSupport.Empty : Geary.Folder {
|
||||
/**
|
||||
* Removes all email from the folder.
|
||||
*/
|
||||
public abstract async void empty_folder_async(Cancellable? cancellable = null) throws Error;
|
||||
}
|
||||
|
||||
|
|
@ -935,16 +935,22 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
// on most operations unless ListFlags.INCLUDE_MARKED_REMOVED is true. Use detach_email_async()
|
||||
// to formally remove the messages from the folder.
|
||||
//
|
||||
// If ids is null, all messages are marked for removal.
|
||||
//
|
||||
// Returns a collection of ImapDB.EmailIdentifiers *with the UIDs set* for this folder.
|
||||
// Supplied EmailIdentifiers not in this Folder will not be included.
|
||||
public async Gee.Set<ImapDB.EmailIdentifier>? mark_removed_async(
|
||||
Gee.Collection<ImapDB.EmailIdentifier> ids, bool mark_removed, Cancellable? cancellable)
|
||||
Gee.Collection<ImapDB.EmailIdentifier>? ids, bool mark_removed, Cancellable? cancellable)
|
||||
throws Error {
|
||||
int unread_count = 0;
|
||||
Gee.Set<ImapDB.EmailIdentifier> removed_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
|
||||
yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
|
||||
Gee.List<LocationIdentifier?> locs = do_get_locations_for_ids(cx, ids,
|
||||
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
Gee.List<LocationIdentifier?> locs;
|
||||
if (ids != null)
|
||||
locs = do_get_locations_for_ids(cx, ids, ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
else
|
||||
locs = do_get_all_locations(cx, ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
|
||||
if (locs == null || locs.size == 0)
|
||||
return Db.TransactionOutcome.DONE;
|
||||
|
||||
|
|
@ -2256,8 +2262,26 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
return (locs.size > 0) ? locs : null;
|
||||
}
|
||||
|
||||
private Gee.List<LocationIdentifier>? do_get_all_locations(Db.Connection cx, ListFlags flags,
|
||||
Cancellable? cancellable) throws Error {
|
||||
Db.Statement stmt = cx.prepare("""
|
||||
SELECT message_id, ordering, remove_marker
|
||||
FROM MessageLocationTable
|
||||
WHERE folder_id = ?
|
||||
""");
|
||||
stmt.bind_rowid(0, folder_id);
|
||||
|
||||
Gee.List<LocationIdentifier> locs = do_results_to_locations(stmt.exec(cancellable), flags,
|
||||
cancellable);
|
||||
|
||||
return (locs.size > 0) ? locs : null;
|
||||
}
|
||||
|
||||
private int do_get_unread_count_for_ids(Db.Connection cx,
|
||||
Gee.Collection<ImapDB.EmailIdentifier> ids, Cancellable? cancellable) throws Error {
|
||||
Gee.Collection<ImapDB.EmailIdentifier>? ids, Cancellable? cancellable) throws Error {
|
||||
if (ids == null || ids.size == 0)
|
||||
return 0;
|
||||
|
||||
// Fetch flags for each email and update this folder's unread count.
|
||||
// (Note that this only flags for emails which have NOT been marked for removal
|
||||
// are included.)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
* IMAP STORE/EXPUNGE operation.
|
||||
*/
|
||||
|
||||
private class Geary.ImapEngine.GmailSpamTrashFolder : MinimalFolder, FolderSupport.Remove {
|
||||
private class Geary.ImapEngine.GmailSpamTrashFolder : MinimalFolder, FolderSupport.Remove,
|
||||
FolderSupport.Empty {
|
||||
public GmailSpamTrashFolder(GmailAccount account, Imap.Account remote, ImapDB.Account local,
|
||||
ImapDB.Folder local_folder, SpecialFolderType special_folder_type) {
|
||||
base (account, remote, local, local_folder, special_folder_type);
|
||||
|
|
@ -19,5 +20,9 @@ private class Geary.ImapEngine.GmailSpamTrashFolder : MinimalFolder, FolderSuppo
|
|||
Cancellable? cancellable = null) throws Error {
|
||||
yield expunge_email_async(email_ids, cancellable);
|
||||
}
|
||||
|
||||
public async void empty_folder_async(Cancellable? cancellable = null) throws Error {
|
||||
yield expunge_all_async(cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
private class Geary.ImapEngine.GenericFolder : MinimalFolder, Geary.FolderSupport.Remove,
|
||||
Geary.FolderSupport.Create {
|
||||
Geary.FolderSupport.Create, Geary.FolderSupport.Empty {
|
||||
public GenericFolder(GenericAccount account, Imap.Account remote, ImapDB.Account local,
|
||||
ImapDB.Folder local_folder, SpecialFolderType special_folder_type) {
|
||||
base (account, remote, local, local_folder, special_folder_type);
|
||||
|
|
@ -16,6 +16,10 @@ private class Geary.ImapEngine.GenericFolder : MinimalFolder, Geary.FolderSuppor
|
|||
yield expunge_email_async(email_ids, cancellable);
|
||||
}
|
||||
|
||||
public async void empty_folder_async(Cancellable? cancellable = null) throws Error {
|
||||
yield expunge_all_async(cancellable);
|
||||
}
|
||||
|
||||
public new async Geary.EmailIdentifier? create_email_async(
|
||||
RFC822.Message rfc822, Geary.EmailFlags? flags, DateTime? date_received,
|
||||
Geary.EmailIdentifier? id, Cancellable? cancellable = null) throws Error {
|
||||
|
|
|
|||
|
|
@ -1231,6 +1231,15 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
|
|||
yield remove.wait_for_ready_async(cancellable);
|
||||
}
|
||||
|
||||
protected async void expunge_all_async(Cancellable? cancellable = null) throws Error {
|
||||
check_open("expunge_all_async");
|
||||
|
||||
EmptyFolder empty_folder = new EmptyFolder(this, cancellable);
|
||||
replay_queue.schedule(empty_folder);
|
||||
|
||||
yield empty_folder.wait_for_ready_async(cancellable);
|
||||
}
|
||||
|
||||
private void check_open(string method) throws EngineError {
|
||||
if (open_count == 0)
|
||||
throw new EngineError.OPEN_REQUIRED("%s failed: folder %s is not open", method, to_string());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
/* Copyright 2015 Yorba Foundation
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Similar to RemoveEmail, except this command ''always'' issues the command to remove all mail,
|
||||
* ensuring the entire folder is emptied even if only a portion of it is synchronized locally.
|
||||
*/
|
||||
|
||||
private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperation {
|
||||
private MinimalFolder engine;
|
||||
private Cancellable? cancellable;
|
||||
private Gee.Set<ImapDB.EmailIdentifier>? removed_ids = null;
|
||||
private int original_count = 0;
|
||||
|
||||
public EmptyFolder(MinimalFolder engine, Cancellable? cancellable) {
|
||||
base("EmptyFolder");
|
||||
|
||||
this.engine = engine;
|
||||
this.cancellable = cancellable;
|
||||
}
|
||||
|
||||
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
|
||||
}
|
||||
|
||||
public override async ReplayOperation.Status replay_local_async() throws Error {
|
||||
original_count = engine.get_remote_counts(null, null);
|
||||
|
||||
// because this value is only used for reporting count changes, offer best-possible service
|
||||
if (original_count < 0)
|
||||
original_count = 0;
|
||||
|
||||
// mark everything in the folder as removed
|
||||
removed_ids = yield engine.local_folder.mark_removed_async(null, true, cancellable);
|
||||
|
||||
// if local folder is not empty, report all as being removed
|
||||
if (removed_ids != null) {
|
||||
if (removed_ids.size > 0)
|
||||
engine.replay_notify_email_removed(removed_ids);
|
||||
|
||||
int new_count = Numeric.int_floor(original_count - removed_ids.size, 0);
|
||||
if (new_count != original_count)
|
||||
engine.replay_notify_email_count_changed(new_count, Geary.Folder.CountChangeReason.REMOVED);
|
||||
}
|
||||
|
||||
return ReplayOperation.Status.CONTINUE;
|
||||
}
|
||||
|
||||
public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
|
||||
if (removed_ids != null)
|
||||
ids.add_all(removed_ids);
|
||||
}
|
||||
|
||||
public override async ReplayOperation.Status replay_remote_async() throws Error {
|
||||
// STORE and EXPUNGE using positional addressing: "1:*"
|
||||
Imap.MessageSet msg_set = new Imap.MessageSet.range_to_highest(
|
||||
new Imap.SequenceNumber(Imap.SequenceNumber.MIN));
|
||||
|
||||
yield engine.remote_folder.remove_email_async(msg_set.to_list(), cancellable);
|
||||
|
||||
return ReplayOperation.Status.COMPLETED;
|
||||
}
|
||||
|
||||
public override async void backout_local_async() throws Error {
|
||||
if (removed_ids != null && removed_ids.size > 0) {
|
||||
yield engine.local_folder.mark_removed_async(removed_ids, false, cancellable);
|
||||
engine.replay_notify_email_inserted(removed_ids);
|
||||
}
|
||||
|
||||
engine.replay_notify_email_count_changed(original_count, Geary.Folder.CountChangeReason.INSERTED);
|
||||
}
|
||||
|
||||
public override string describe_state() {
|
||||
return "removed_ids.size=%d".printf((removed_ids != null) ? removed_ids.size : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -14,6 +14,7 @@ install(FILES message.glade DESTINATION ${UI_DEST})
|
|||
install(FILES password-dialog.glade DESTINATION ${UI_DEST})
|
||||
install(FILES preferences.glade DESTINATION ${UI_DEST})
|
||||
install(FILES remove_confirm.glade DESTINATION ${UI_DEST})
|
||||
install(FILES toolbar_empty_menu.ui DESTINATION ${UI_DEST})
|
||||
install(FILES toolbar_mark_menu.ui DESTINATION ${UI_DEST})
|
||||
install(FILES toolbar_menu.ui DESTINATION ${UI_DEST})
|
||||
install(FILES upgrade_dialog.glade DESTINATION ${UI_DEST})
|
||||
|
|
|
|||
7
ui/toolbar_empty_menu.ui
Normal file
7
ui/toolbar_empty_menu.ui
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<ui>
|
||||
<popup name="ToolbarEmptyMenu">
|
||||
<menuitem name="EmptySpam" action="GearyEmptySpam" />
|
||||
<menuitem name="EmptyTrash" action="GearyEmptyTrash" />
|
||||
</popup>
|
||||
</ui>
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue