/* * Copyright (C) 2005 Kouji TAKAO * * 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 2 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 Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include "gpass/configuration.h" #include "helper.h" #include "application.h" #include "command.h" /*********************************************************** * * GPassCommand * ***********************************************************/ static GObjectClass *parent_command_class = NULL; static void gpass_command_instance_init(GTypeInstance *instance, gpointer g_class) { GPassCommand *self = GPASS_COMMAND(instance); self->application = NULL; self->description = NULL; self->prev = NULL; self->next = NULL; } enum { COMMAND_PROP_0, COMMAND_PROP_APPLICATION, COMMAND_PROP_DESCRIPTION, }; static void gpass_command_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GPassCommand *self = GPASS_COMMAND(object); switch (prop_id) { case COMMAND_PROP_APPLICATION: self->application = g_value_get_pointer(value); break; case COMMAND_PROP_DESCRIPTION: self->description = g_value_dup_string(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gpass_command_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GPassCommand *self = GPASS_COMMAND(object); switch (prop_id) { case COMMAND_PROP_APPLICATION: g_value_set_pointer(value, self->application); break; case COMMAND_PROP_DESCRIPTION: g_value_set_string(value, self->description); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static GError * gpass_command_instance_redo(GPassCommand *self) { return NULL; } static GError * gpass_command_instance_undo(GPassCommand *self) { return NULL; } static void gpass_command_instance_finalize(GObject *object) { GPassCommand *self = GPASS_COMMAND(object); g_free(self->description); G_OBJECT_CLASS(parent_command_class)->finalize(object); } static void gpass_command_class_init(gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); GPassCommandClass *klass = GPASS_COMMAND_CLASS(g_class); parent_command_class = g_type_class_peek_parent(g_class); gobject_class->set_property = gpass_command_set_property; gobject_class->get_property = gpass_command_get_property; gobject_class->finalize = gpass_command_instance_finalize; klass->redo = gpass_command_instance_redo; klass->undo = gpass_command_instance_undo; g_object_class_install_property (gobject_class, COMMAND_PROP_APPLICATION, g_param_spec_pointer("application", _("GPassApplication"), _("The pointer to GPassApplication"), G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, COMMAND_PROP_DESCRIPTION, g_param_spec_string("description", _("Description"), _("The description of command"), NULL, G_PARAM_READWRITE)); } GType gpass_command_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassCommandClass), NULL, NULL, gpass_command_class_init, NULL, NULL, sizeof(GPassCommand), 0, gpass_command_instance_init }; type = g_type_register_static(G_TYPE_OBJECT, "GPassCommand", &info, G_TYPE_FLAG_ABSTRACT); } return type; } GError * gpass_command_redo(GPassCommand *self) { return GPASS_COMMAND_GET_CLASS(self)->redo(self); } GError * gpass_command_undo(GPassCommand *self) { return GPASS_COMMAND_GET_CLASS(self)->undo(self); } /*********************************************************** * * GPassNullCommand * ***********************************************************/ GType gpass_null_command_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassNullCommandClass), NULL, NULL, NULL, NULL, NULL, sizeof(GPassNullCommand), 0, NULL, }; type = g_type_register_static(GPASS_TYPE_COMMAND, "GPassNullCommand", &info, 0); } return type; } /*********************************************************** * * GPassTransactionCommand * ***********************************************************/ static GPassCommandClass *parent_transaction_command_class = NULL; static void gpass_transaction_command_instance_init(GTypeInstance *instance, gpointer g_class) { GPassTransactionCommand *self = GPASS_TRANSACTION_COMMAND(instance); self->first = NULL; self->last = NULL; } static GError * gpass_transaction_command_instance_redo(GPassCommand *command) { GPassTransactionCommand *self = GPASS_TRANSACTION_COMMAND(command); GPassCommand *p; GError *error = NULL; for (p = self->first; p != NULL; p = p->next) { error = GPASS_COMMAND_GET_CLASS(p)->redo(p); if (error != NULL) { break; } } return error; } static GError * gpass_transaction_command_instance_undo(GPassCommand *command) { GPassTransactionCommand *self = GPASS_TRANSACTION_COMMAND(command); GPassCommand *p; GError *error = NULL; for (p = self->last; p != NULL; p = p->prev) { error = GPASS_COMMAND_GET_CLASS(p)->undo(p); if (error != NULL) { break; } } return error; } static void gpass_transaction_command_instance_finalize(GObject *object) { GPassTransactionCommand *self = GPASS_TRANSACTION_COMMAND(object); GPassCommand *p; for (p = self->first; p != NULL; p = p->next) { g_object_unref(p); } G_OBJECT_CLASS(parent_transaction_command_class)->finalize(object); } static void gpass_transaction_command_class_init(gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); GPassCommandClass *command_class = GPASS_COMMAND_CLASS(g_class); parent_transaction_command_class = g_type_class_peek_parent(g_class); gobject_class->finalize = gpass_transaction_command_instance_finalize; command_class->redo = gpass_transaction_command_instance_redo; command_class->undo = gpass_transaction_command_instance_undo; } GType gpass_transaction_command_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassTransactionCommandClass), NULL, NULL, gpass_transaction_command_class_init, NULL, NULL, sizeof(GPassTransactionCommand), 0, gpass_transaction_command_instance_init }; type = g_type_register_static(GPASS_TYPE_COMMAND, "GPassTransactionCommand", &info, 0); } return type; } void gpass_transaction_command_push(GPassTransactionCommand *self, GPassCommand *command) { if (self->first == NULL) { self->first = command; } command->prev = self->last; command->next = NULL; self->last = command; } /*********************************************************** * * GPassCommandStack * ***********************************************************/ static GObjectClass *parent_command_statck_class = NULL; static void gpass_command_stack_instance_init(GTypeInstance *instance, gpointer g_class) { GPassCommandStack *self = GPASS_COMMAND_STACK(instance); GPassConfiguration *config; gint max_depth; self->top = g_object_new(GPASS_TYPE_NULL_COMMAND, NULL); self->current = self->top; self->base = NULL; self->depth = 0; config = gpass_configuration_instance(); g_object_get(config, "undo_levels", &max_depth, NULL); if (max_depth == 0) { self->max_depth = GPASS_COMMAND_STACK_MAX_DEPTH; } else { self->max_depth = max_depth; } self->removed_base = FALSE; } enum { STACK_PROP_0, STACK_PROP_MAX_DEPTH, }; static void gpass_command_stack_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GPassCommandStack *self = GPASS_COMMAND_STACK(object); gint max_depth; switch (prop_id) { case STACK_PROP_MAX_DEPTH: max_depth = g_value_get_int(value); if (max_depth != self->max_depth) { GPassConfiguration *config; self->max_depth = max_depth; config = gpass_configuration_instance(); g_object_set(config, "undo_levels", self->max_depth, NULL); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gpass_command_stack_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GPassCommandStack *self = GPASS_COMMAND_STACK(object); switch (prop_id) { case STACK_PROP_MAX_DEPTH: g_value_set_int(value, self->max_depth); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gpass_command_stack_instance_finalize(GObject *object) { GPassCommandStack *self = GPASS_COMMAND_STACK(object); GPassCommand *p; p = self->top; while (p->prev != NULL) { p = p->prev; g_object_unref(p->next); } g_object_unref(p); G_OBJECT_CLASS(parent_command_statck_class)->finalize(object); } static void gpass_command_stack_class_init(gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); parent_command_statck_class = g_type_class_peek_parent(g_class); gobject_class->set_property = gpass_command_stack_set_property; gobject_class->get_property = gpass_command_stack_get_property; gobject_class->finalize = gpass_command_stack_instance_finalize; g_object_class_install_property (gobject_class, STACK_PROP_MAX_DEPTH, g_param_spec_int("max_depth", _("Max depth"), _("The max depth of command stack"), 1, G_MAXINT, GPASS_COMMAND_STACK_MAX_DEPTH, G_PARAM_READWRITE)); } GType gpass_command_stack_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassCommandStackClass), NULL, NULL, gpass_command_stack_class_init, NULL, NULL, sizeof(GPassCommandStack), 0, gpass_command_stack_instance_init }; type = g_type_register_static(G_TYPE_OBJECT, "GPassCommandStack", &info, 0); } return type; } static void remove_between_current_and_top(GPassCommandStack *self) { GPassCommand *save, *p; if (self->current == self->top) { return; } save = self->current->prev; for (p = self->current; p != self->top; p = p->next) { if (p == self->base) { self->base = NULL; self->removed_base = TRUE; } g_object_unref(p); self->depth--; } self->top->prev = save; if (save != NULL) { save->next = self->top; } self->current = self->top; } static void remove_bottom(GPassCommandStack *self) { GPassCommand *bottom, *p; gint i; if (self->depth <= self->max_depth) { return; } p = self->current; i = 0; while (p != NULL && i < self->max_depth) { p = p->prev; i++; } bottom = p->next; while (p != NULL) { GPassCommand *prev; prev = p->prev; if (p == self->base) { self->base = NULL; } g_object_unref(p); p = prev; } bottom->prev = NULL; self->depth = self->max_depth; if (self->base == NULL) { self->removed_base = TRUE; } } void gpass_command_stack_push(GPassCommandStack *self, GPassCommand *command) { if (self->max_depth <= 0) { g_object_unref(command); return; } remove_between_current_and_top(self); if (self->top->prev == NULL) { command->prev = NULL; self->top->prev = command; } else { command->prev = self->top->prev; command->prev->next = command; } command->next = self->top; command->next->prev = command; self->current = command; self->depth++; remove_bottom(self); } GError * gpass_command_stack_redo(GPassCommandStack *self) { GError *error; if (self->current == self->top) { return NULL; } error = GPASS_COMMAND_GET_CLASS(self->current)->redo(self->current); if (error != NULL) { return error; } self->current = self->current->next; return NULL; } GError * gpass_command_stack_undo(GPassCommandStack *self) { GPassCommand *prev; GError *error; if (self->current->prev == NULL) { return NULL; } prev = self->current->prev; error = GPASS_COMMAND_GET_CLASS(prev)->undo(prev); if (error != NULL) { return error; } self->current = prev; return NULL; } void gpass_command_stack_get_redo_description(GPassCommandStack *self, const gchar **description) { if (self->current == self->top) { *description = NULL; } else { *description = GPASS_COMMAND(self->current)->description; } } void gpass_command_stack_get_undo_description(GPassCommandStack *self, const gchar **description) { if (self->current->prev == NULL) { *description = NULL; } else { *description = GPASS_COMMAND(self->current->prev)->description; } } void gpass_command_stack_set_base(GPassCommandStack *self) { self->base = self->current->prev; if (self->removed_base) { self->removed_base = FALSE; } } gboolean gpass_command_stack_at_base(GPassCommandStack *self) { if (self->removed_base) { return FALSE; } if (self->base == self->current->prev) { return TRUE; } return FALSE; } /*********************************************************** * * GPassEditCommand * ***********************************************************/ static GPassCommandClass *parent_edit_command_class = NULL; static void gpass_edit_command_instance_init(GTypeInstance *instance, gpointer g_class) { GPassEditCommand *self = GPASS_EDIT_COMMAND(instance); self->target = NULL; self->edited = NULL; } static GError * gpass_edit_command_instance_do(GPassCommand *command) { GPassEditCommand *self = GPASS_EDIT_COMMAND(command); GPassEntry *old_original, *old_edited; GError *error; old_original = self->target; old_edited = self->edited; error = gpass_application_passwords_edit (command->application, self->target, self->edited); if (error != NULL) { return error; } self->target = old_edited; self->edited = old_original; return NULL; } static void gpass_edit_command_instance_finalize(GObject *object) { GPassEditCommand *self = GPASS_EDIT_COMMAND(object); g_object_unref(self->edited); G_OBJECT_CLASS(parent_edit_command_class)->finalize(object); } static void gpass_edit_command_class_init(gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); GPassCommandClass *command_class = GPASS_COMMAND_CLASS(g_class); parent_edit_command_class = g_type_class_peek_parent(g_class); gobject_class->finalize = gpass_edit_command_instance_finalize; command_class->redo = gpass_edit_command_instance_do; command_class->undo = gpass_edit_command_instance_do; } GType gpass_edit_command_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassEditCommandClass), NULL, NULL, gpass_edit_command_class_init, NULL, NULL, sizeof(GPassEditCommand), 0, gpass_edit_command_instance_init }; type = g_type_register_static(GPASS_TYPE_COMMAND, "GPassEditCommand", &info, 0); } return type; } GPassCommand * gpass_edit_command_new(GPassApplication *application, GPassEntry *target, GPassEntry *edited) { GPassEditCommand *self; const gchar *name; gchar *buf; g_object_get(target, "name", &name, NULL); buf = g_strdup_printf(_("Edit %s"), name); self = GPASS_EDIT_COMMAND(g_object_new(GPASS_TYPE_EDIT_COMMAND, "application", application, "description", buf, NULL)); g_free(buf); self->target = target; self->edited = edited; return GPASS_COMMAND(self); } /*********************************************************** * * GPassLinkCommand * ***********************************************************/ static GPassCommandClass *parent_link_command_class = NULL; static void gpass_link_command_instance_init(GTypeInstance *instance, gpointer g_class) { GPassLinkCommand *self = GPASS_LINK_COMMAND(instance); self->target = NULL; self->parent = NULL; self->sibling = NULL; self->referenced = FALSE; } static void gpass_link_command_instance_finalize(GObject *object) { GPassLinkCommand *self = GPASS_LINK_COMMAND(object); if (!self->referenced) { g_object_unref(self->target); } G_OBJECT_CLASS(parent_link_command_class)->finalize(object); } static void gpass_link_command_class_init(gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); parent_link_command_class = g_type_class_peek_parent(g_class); gobject_class->finalize = gpass_link_command_instance_finalize; } GType gpass_link_command_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassLinkCommandClass), NULL, NULL, gpass_link_command_class_init, NULL, NULL, sizeof(GPassLinkCommand), 0, gpass_link_command_instance_init }; type = g_type_register_static(GPASS_TYPE_COMMAND, "GPassLinkCommand", &info, G_TYPE_FLAG_ABSTRACT); } return type; } static GPassCommand * gpass_link_command_initialize(GPassLinkCommand *self, GPassEntry *target, GPassEntry *parent, GPassEntry *sibling) { self->target = target; self->parent = parent; self->sibling = sibling; return GPASS_COMMAND(self); } static GError * gpass_link_command_instance_insert(GPassCommand *command) { GPassLinkCommand *self = GPASS_LINK_COMMAND(command); GError *error; error = gpass_application_passwords_insert (command->application, self->target, self->parent, self->sibling); if (error != NULL) { return error; } self->referenced = TRUE; return NULL; } static GError * gpass_link_command_instance_unlink(GPassCommand *command) { GPassLinkCommand *self = GPASS_LINK_COMMAND(command); GError *error; error = gpass_application_passwords_unlink (command->application, self->target, self->parent, self->sibling); if (error != NULL) { return error; } self->referenced = FALSE; return NULL; } /*********************************************************** * * GPassInsertCommand * ***********************************************************/ static void gpass_insert_command_class_init(gpointer g_class, gpointer g_class_data) { GPassCommandClass *command_class = GPASS_COMMAND_CLASS(g_class); command_class->redo = gpass_link_command_instance_insert; command_class->undo = gpass_link_command_instance_unlink; } GType gpass_insert_command_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassInsertCommandClass), NULL, NULL, gpass_insert_command_class_init, NULL, NULL, sizeof(GPassInsertCommand), 0, NULL, }; type = g_type_register_static(GPASS_TYPE_LINK_COMMAND, "GPassInsertCommand", &info, 0); } return type; } GPassCommand * gpass_insert_command_new(GPassApplication *application, GPassEntry *target, GPassEntry *parent, GPassEntry *sibling) { GPassInsertCommand *self; self = GPASS_INSERT_COMMAND(g_object_new(GPASS_TYPE_INSERT_COMMAND, "application", application, NULL)); gpass_link_command_initialize(GPASS_LINK_COMMAND(self), target, parent, sibling); return GPASS_COMMAND(self); } /*********************************************************** * * GPassUnlinkCommand * ***********************************************************/ static void gpass_unlink_command_class_init(gpointer g_class, gpointer g_class_data) { GPassCommandClass *command_class = GPASS_COMMAND_CLASS(g_class); command_class->redo = gpass_link_command_instance_unlink; command_class->undo = gpass_link_command_instance_insert; } GType gpass_unlink_command_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassUnlinkCommandClass), NULL, NULL, gpass_unlink_command_class_init, NULL, NULL, sizeof(GPassUnlinkCommand), 0, NULL, }; type = g_type_register_static(GPASS_TYPE_LINK_COMMAND, "GPassUnlinkCommand", &info, 0); } return type; } GPassCommand * gpass_unlink_command_new(GPassApplication *application, GPassEntry *target, GPassEntry *parent, GPassEntry *sibling) { GPassUnlinkCommand *self; self = GPASS_UNLINK_COMMAND(g_object_new(GPASS_TYPE_UNLINK_COMMAND, "application", application, NULL)); gpass_link_command_initialize(GPASS_LINK_COMMAND(self), target, parent, sibling); return GPASS_COMMAND(self); } /*********************************************************** * * GPassMoveCommand * ***********************************************************/ static void gpass_move_command_instance_init(GTypeInstance *instance, gpointer g_class) { GPassMoveCommand *self = GPASS_MOVE_COMMAND(instance); self->target = NULL; self->parent = NULL; self->previous = NULL; } static GError * gpass_move_command_instance_do(GPassCommand *command) { GPassMoveCommand *self = GPASS_MOVE_COMMAND(command); GPassEntry *old_parent, *old_previous; GError *error; old_parent = gpass_entry_parent(self->target); old_previous = gpass_entry_prev_sibling(self->target); error = gpass_application_passwords_move_after (command->application, self->target, self->parent, self->previous); if (error != NULL) { return error; } self->parent = old_parent; self->previous = old_previous; return NULL; } static void gpass_move_command_class_init(gpointer g_class, gpointer g_class_data) { GPassCommandClass *command_class = GPASS_COMMAND_CLASS(g_class); command_class->redo = gpass_move_command_instance_do; command_class->undo = gpass_move_command_instance_do; } GType gpass_move_command_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassMoveCommandClass), NULL, NULL, gpass_move_command_class_init, NULL, NULL, sizeof(GPassMoveCommand), 0, gpass_move_command_instance_init }; type = g_type_register_static(GPASS_TYPE_COMMAND, "GPassMoveCommand", &info, 0); } return type; } GPassCommand * gpass_move_command_new(struct GPassApplication *application, GPassEntry *target, GPassEntry *parent, GPassEntry *previous) { GPassMoveCommand *self; const gchar *name; gchar *buf; g_object_get(target, "name", &name, NULL); buf = g_strdup_printf(_("Move %s"), name); self = GPASS_MOVE_COMMAND(g_object_new(GPASS_TYPE_MOVE_COMMAND, "application", application, "description", buf, NULL)); g_free(buf); self->target = target; self->parent = parent; self->previous = previous; return GPASS_COMMAND(self); }