#!/usr/bin/env python2.5
# -*- coding: utf-8 -*-
import gtk
import hildon
import gobject
import pickle
import gzip
import locale
import gettext
import dbus
import dbus.glib
import osso
from Crypto.Cipher import Blowfish
from Crypto.Hash import MD5


"""
Classe adaptada da original utilizada no gPodder.
"""
class FremantleRotation(object):
    """thp's screen rotation for Maemo 5

    Simply instantiate an object of this class and let it auto-rotate
    your StackableWindows depending on the device orientation.

    If you need to relayout a window, connect to its "configure-event"
    signal and measure the ratio of width/height and relayout for that.

    You can set the mode for rotation to AUTOMATIC (default), NEVER or
    ALWAYS with the set_mode() method.
    """
    AUTOMATIC, NEVER, ALWAYS = range(3)

    # Privately-used constants
    _PORTRAIT, _LANDSCAPE = ('portrait', 'landscape')
    _ENABLE_ACCEL = 'req_accelerometer_enable'
    _DISABLE_ACCEL = 'req_accelerometer_disable'

    # Defined in mce/dbus-names.h
    _MCE_SERVICE = 'com.nokia.mce'
    _MCE_REQUEST_PATH = '/com/nokia/mce/request'
    _MCE_REQUEST_IF = 'com.nokia.mce.request'

    def __init__(self, app_name, main_window=None, version='1.0', mode=0, cb = None):
        """Create a new rotation manager

        app_name    ... The name of your application (for osso.Context)
        main_window ... The root window (optional, hildon.StackableWindow)
        version     ... The version of your application (optional, string)
        mode        ... Initial mode for this manager (default: AUTOMATIC)
        """
        self._callback = cb
        self._orientation = None
        self._main_window = main_window
        self._stack = hildon.WindowStack.get_default()
        self._mode = -1
        self._last_dbus_orientation = None
        app_id = '-'.join((app_name, self.__class__.__name__))
        self._osso_context = osso.Context(app_id, version, False)
        program = hildon.Program.get_instance()
        program.connect('notify::is-topmost', self._on_topmost_changed)
        system_bus = dbus.Bus.get_system()
        system_bus.add_signal_receiver(self._on_orientation_signal, \
                signal_name='sig_device_orientation_ind', \
                dbus_interface='com.nokia.mce.signal', \
                path='/com/nokia/mce/signal')
        self.set_mode(mode)

    def get_orientation(self):
        """Get the rotation
        """
        return self._orientation

    def get_mode(self):
        """Get the currently-set rotation mode

        This will return one of three values: AUTOMATIC, ALWAYS or NEVER.
        """
        return self._mode

    def set_mode(self, new_mode):
        """Set the rotation mode

        You can set the rotation mode to AUTOMATIC (use hardware rotation
        info), ALWAYS (force portrait) and NEVER (force landscape).
        """
        if new_mode not in (self.AUTOMATIC, self.ALWAYS, self.NEVER):
            raise ValueError('Unknown rotation mode')

        if self._mode != new_mode:
            if self._mode == self.AUTOMATIC:
                # Remember the current "automatic" orientation for later
                self._last_dbus_orientation = self._orientation
                # Tell MCE that we don't need the accelerometer anymore
                self._send_mce_request(self._DISABLE_ACCEL)

            if new_mode == self.NEVER:
                self._orientation_changed(self._LANDSCAPE)
            elif new_mode == self.ALWAYS:
                self._orientation_changed(self._PORTRAIT)
            elif new_mode == self.AUTOMATIC:
                # Restore the last-known "automatic" orientation
                self._orientation_changed(self._last_dbus_orientation)
                # Tell MCE that we need the accelerometer again
                self._send_mce_request(self._ENABLE_ACCEL)

            self._mode = new_mode

    def _send_mce_request(self, request):
        rpc = osso.Rpc(self._osso_context)
        rpc.rpc_run(self._MCE_SERVICE, \
                    self._MCE_REQUEST_PATH, \
                    self._MCE_REQUEST_IF, \
                    request, \
                    use_system_bus=True)

    def _on_topmost_changed(self, program, property_spec):
        # XXX: This seems to never get called on Fremantle(?)
        if self._mode == self.AUTOMATIC:
            if program.get_is_topmost():
                self._send_mce_request(self._ENABLE_ACCEL)
            else:
                self._send_mce_request(self._DISABLE_ACCEL)

    def _get_main_window(self):
        if self._main_window:
            # If we have gotten the main window as parameter, return it and
            # don't try "harder" to find another window using the stack
            return self._main_window
        else:
            # The main window is at the "bottom" of the window stack, and as
            # the list we get with get_windows() is sorted "topmost first", we
            # simply take the last item of the list to get our main window
            windows = self._stack.get_windows()
            if windows:
                return windows[-1]
            else:
                return None

    def _orientation_changed(self, orientation):
        if self._orientation == orientation:
            # Ignore repeated requests
            return

        flags = hildon.PORTRAIT_MODE_SUPPORT
        if orientation == self._PORTRAIT:
            flags |= hildon.PORTRAIT_MODE_REQUEST

        window = self._get_main_window()
        if window is not None:
            hildon.hildon_gtk_window_set_portrait_flags(window, flags)

        self._orientation = orientation
        self._callback(self._orientation)

    def _on_orientation_signal(self, orientation, stand, face, x, y, z):
        if orientation in (self._PORTRAIT, self._LANDSCAPE):
            if self._mode == self.AUTOMATIC:
                # Automatically set the rotation based on hardware orientation
                self._orientation_changed(orientation)
            else:
                # Ignore orientation changes for non-automatic modes, but save
                # the current orientation for "automatic" mode later on
                self._last_dbus_orientation = orientation


"""
Esta classe mantém as informações sobre os itens (grupos e seus itens internos)
Também salva e carrega do arquivo
"""
class Dados:

  DATABASE_OK = 0
  ARQUIVO_NAO_LOCALIZADO = 1
  SENHA_INVALIDA = 2
  DADOS_CORROMPIDOS = 3

  def __init__(self, p, db = "/home/user/MyDocs/pysafe.db"):
    self.password = p
    self.database_file = db
    self.changed = False
    self.dados = {}
    return

  def load(self):
    crypt = Blowfish.new(self.password)
    md5 = MD5.new()

    # le o arquivo compactado
    try:
      arch = gzip.GzipFile(self.database_file, 'rb')
    except IOError:
      return self.ARQUIVO_NAO_LOCALIZADO
    buffer = ""
    while True:
      data = arch.read()
      if data == "":
        break
      buffer += data
    arch.close()

    # teoricamente, descriptografa ele
    dados_tmp = crypt.decrypt(buffer)
    # valida a senha...segurança para saber se a senha é realmente válida
    # e as informações podem ser descriptografadas corretamente
    pos = dados_tmp.find("\n")
    if pos == -1:
      return self.SENHA_INVALIDA
    senha = dados_tmp[:pos]
    if senha != self.password:
      return self.SENHA_INVALIDA

    # chegou aqui, a senha confere...pode apagar!
    dados_tmp = dados_tmp[pos + 1:]

    # pega o MD5...
    pos = dados_tmp.find("\n")
    if pos == -1:
      return self.DADOS_CORROMPIDOS
    checksum = dados_tmp[:pos]

    # remove o checksum para criar efetivamente os dados
    dados_tmp = dados_tmp[pos + 1:].strip()
    md5.update(dados_tmp)

    if checksum != md5.hexdigest():
      return self.DADOS_CORROMPIDOS

    self.dados = pickle.loads(dados_tmp)

    return self.DATABASE_OK

  def save(self, force = False, new_pass = None):
    if force == False and self.changed == False:
      return True

    if new_pass != None:
      self.password = new_pass

    crypt = Blowfish.new(self.password)
    md5 = MD5.new()

    # serializa os dados
    dados_tmp = pickle.dumps(self.dados, 1)
    # cria o MD5 deles
    md5.update(dados_tmp)
    # acrescenta a senha e o checksum
    dados_tmp = "%s\n%s\n%s" % (self.password, md5.hexdigest(), dados_tmp)
    # criptografa!
    dados_tmp = crypt.encrypt(self.fillWithSpace(dados_tmp))

    # salva o arquivo compactado
    try:
      arch = gzip.GzipFile(self.database_file, "wb")
    except IOError:
      return False
    arch.write(dados_tmp)
    arch.close()
    return True

  def fillWithSpace(self, s):
    while len(s) % 8 != 0:
      s = "%s " % (s)
    return s

  def getGroups(self):
    return self.dados.keys()

  def addGroup(self, name):
    if name in self.dados:
      return False
    self.changed = True
    self.dados[name] = {}
    return True

  def delGroup(self, name):
    self.changed = True
    del self.dados[name]

  def addItem(self, group, name):
    if name in self.dados[group]:
      return False
    self.changed = True
    self.dados[group][name] = {}
    return True

  def delItem(self, group, item):
    self.changed = True
    g = self.dados[group]
    del g[item]
    self.dados[group] = g

  def getItens(self, group):
    return self.dados[group]

  def getItem(self, group, item):
    return self.dados[group][item]

  def addDetail(self, group, item, detail):
    if detail in self.dados[group][item]:
      return False
    self.changed = True
    self.dados[group][item][detail] = ""
    return True

  def delDetail(self, group, item, detail):
    self.changed = True
    g = self.dados[group][item]
    del g[detail]
    self.dados[group][item] = g

  def setDetail(self, group, item, detail, value):
    self.changed = True
    self.dados[group][item][detail] = value


"""
Classe responsável pela exibição da tela principal do aplicativo
"""
class MainWindow():

  def __init__(self):
    self.ignore_list_clicked = False
    self.atual_group = None
    self.database = None

    self.window = hildon.StackableWindow()
    self.window.set_title("pySafe")

    # adiciona o menu
    self.menu = hildon.AppMenu()
    self.add_group_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                          hildon.BUTTON_ARRANGEMENT_VERTICAL)
    self.add_group_button.set_text(_("Add group"), "")
    self.add_group_button.connect("clicked", self.add_group_clicked)

    self.del_group_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                          hildon.BUTTON_ARRANGEMENT_VERTICAL)
    self.del_group_button.set_text(_("Remove group"), "")
    self.del_group_button.connect("clicked", self.del_group_clicked)

    self.add_item_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                          hildon.BUTTON_ARRANGEMENT_VERTICAL)
    self.add_item_button.set_text(_("Add item"), "")
    self.add_item_button.connect("clicked", self.add_item_clicked)

    self.del_item_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                          hildon.BUTTON_ARRANGEMENT_VERTICAL)
    self.del_item_button.set_text(_("Remove item"), "")
    self.del_item_button.connect("clicked", self.del_item_clicked)

    self.change_pass_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                          hildon.BUTTON_ARRANGEMENT_VERTICAL)
    self.change_pass_button.set_text(_("Change password"), "")
    self.change_pass_button.connect("clicked", self.change_password_clicked)

    self.about_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                          hildon.BUTTON_ARRANGEMENT_VERTICAL)
    self.about_button.set_text(_("About"), "")
    self.about_button.connect("clicked", self.about_clicked)

    self.add_detail_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                          hildon.BUTTON_ARRANGEMENT_VERTICAL)
    self.add_detail_button.set_text(_("Add detail"), "")
    self.add_detail_button.connect("clicked", self.add_detail_clicked)

    self.del_detail_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                          hildon.BUTTON_ARRANGEMENT_VERTICAL)
    self.del_detail_button.set_text(_("Remove detail"), "")
    self.del_detail_button.connect("clicked", self.del_detail_clicked)

    self.del_group_button.set_sensitive(False)
    self.add_item_button.set_sensitive(False)
    self.del_item_button.set_sensitive(False)
    self.add_detail_button.set_sensitive(False)
    self.del_detail_button.set_sensitive(False)

    self.menu.append(self.add_group_button)
    self.menu.append(self.del_group_button)
    self.menu.append(self.add_item_button)
    self.menu.append(self.del_item_button)
    self.menu.append(self.change_pass_button)
    self.menu.append(self.about_button)

    self.menu.show_all()
    self.window.set_app_menu(self.menu)

    self.window.connect("destroy", self.quit, None)

    self.panel_landscape = gtk.HPaned()
    self.panel_landscape.set_position(300)
    self.panel_portrait = gtk.VPaned()
    self.panel_portrait.set_position(300)

    self.window.add(self.panel_portrait)

    #list_store = gtk.ListStore(gtk.gdk.Pixbuf, gobject.TYPE_STRING)

    self.groupList = hildon.TouchSelector(text=True)
    self.groupList.connect("changed", self.list_clicked)
    #self.groupList.append_column(list_store, gtk.CellRenderer())
    self.panel_landscape.add1(self.groupList)
    """ este bloco seria para acrescentar um menu de contexto (popup) ao
        pressionar o botão do grupo, para excluir mais facilmente...mas usando
        o GtkTreeView não consigo eventos de seleção, e usando o TouchSelector
        o método tap_and_hold_setup dá um segmentation fault
    area = hildon.PannableArea()
    self.groupList = hildon.GtkTreeView(gtk.HILDON_UI_MODE_EDIT)
    renderer = gtk.CellRendererText()
    col = gtk.TreeViewColumn("Title", renderer, text=0)
    self.groupList.append_column(col)
    # Set multiple selection mode
    selection = self.groupList.get_selection()
    #selection.set_mode(gtk.SELECTION_MULTIPLE)
    store = gtk.ListStore(gobject.TYPE_STRING)
    self.groupList.set_model(store)
    area.add(self.groupList)
    self.panel_landscape.add1(area)

    urlmenu = gtk.Menu()
    urlmenu.set_title("hildon-context-sensitive-menu")
    urlmenu.append(gtk.MenuItem("URL actions"))
    urlmenu.show_all()
    self.groupList.tap_and_hold_setup(urlmenu, callback=gtk.tap_and_hold_menu_position_top)
    """

    self.panel_detail = gtk.VBox()
    self.panel_landscape.add2(self.panel_detail)

    self.item_detail = hildon.PannableArea()
    self.item_detail_area = gtk.VBox()
    self.item_detail.add_with_viewport(self.item_detail_area)
    self.panel_detail.pack_start(self.item_detail, True, True, 0)

    box = gtk.HBox()
    box.add(self.add_detail_button)
    box.add(self.del_detail_button)
    self.panel_detail.pack_end(box, False, False, 0)

    # cria o objeto responsável pela rotação da tela
    self.rotation_object = FremantleRotation("pySafe", self.window, cb=self.screen_rotation)
    # o deixa desativado por enquanto! Só ativa quando exibir a janela em si
    self.rotation_object.set_mode(FremantleRotation.NEVER)

    # This call show the window and also add the window to the stack
    self.window.show_all()


  def set_database(self, d):
    self.database = d


  def quit(self, v1, v2):
    if self.database.save() != True:
      gtk.Dialog.run(hildon.Note("information", self.window, _("The database could not be updated. Check file permissions or disk space.")))

    gtk.main_quit()


  def screen_rotation(self, mode):
    if mode == "landscape":
      if self.panel_portrait.get_child1() != None:
        self.panel_portrait.remove(self.groupList)
      self.panel_landscape.add1(self.groupList)
      if self.panel_portrait.get_child2() != None:
        self.panel_portrait.remove(self.panel_detail)
      self.panel_landscape.add2(self.panel_detail)
      self.window.remove(self.panel_portrait)
      self.window.add(self.panel_landscape)
      self.menu.show_all()
    elif mode == "portrait":
      if self.panel_landscape.get_child1() != None:
        self.panel_landscape.remove(self.groupList)
      self.panel_portrait.add1(self.groupList)
      if self.panel_landscape.get_child2() != None:
        self.panel_landscape.remove(self.panel_detail)
      self.panel_portrait.add2(self.panel_detail)
      self.window.remove(self.panel_landscape)
      self.window.add(self.panel_portrait)
      self.menu.hide_all()

    self.check_editable_details(mode == "landscape")

    self.window.show_all()

    if mode == "portrait":
      self.add_detail_button.get_parent().hide_all()


  def show(self):
    # habilita a rotação
    self.rotation_object.set_mode(FremantleRotation.AUTOMATIC)
    # mostra os grupos
    self.showGroups()


  def add_group_clicked(self, src):
    entry = hildon.Entry(0)
    dialog = gtk.Dialog(title=_("New Group"), parent=self.window, buttons=(_("Done"), gtk.RESPONSE_OK))
    dialog.vbox.add(hildon.Caption(None, _("Name"), entry, None, True))
    dialog.show_all()
    response = dialog.run()
    dialog.destroy()
    if response == gtk.RESPONSE_OK:
      if self.database.addGroup(entry.get_text()):
        self.showGroups()
        # força a janela a se atualizar...se não fizer isso, o scroll abaixo pode não funcionar
        self.groupList.get_parent_window().process_updates(True)
        # seta o elemento atual
        treemodel = self.groupList.get_model(0)
        it = treemodel.get_iter_first()
        while (it != None):
          if treemodel.get_value(it, 0) == entry.get_text():
            self.groupList.select_iter(0, it, True)
            break
          it = treemodel.iter_next(it)
      else:
        gtk.Dialog.run(hildon.Note("information", self.window, _("The group \"%s\" already exists!") % (entry.get_text())))


  def del_group_clicked(self, src):
    note = hildon.Note("confirmation", self.window, _("Are you sure you want to remove the group \"%s\" and all their itens?") % (self.atual_group))
    retcode = gtk.Dialog.run(note)
    if retcode == gtk.RESPONSE_OK:
      self.database.delGroup(self.atual_group)
      self.showGroups()
    gtk.Dialog.destroy(note)


  def add_item_clicked(self, src):
    entry = hildon.Entry(0)
    dialog = gtk.Dialog(title=_("New Item for Group \"%s\"") % (self.atual_group), parent=self.window, buttons=("Done", gtk.RESPONSE_OK))
    dialog.vbox.add(hildon.Caption(None, _("Name"), entry, None, True))
    dialog.show_all()
    response = dialog.run()
    dialog.destroy()
    if response == gtk.RESPONSE_OK:
      if self.database.addItem(self.atual_group, entry.get_text()):
        self.showItens()
        # força a janela a se atualizar...se não fizer isso, o scroll abaixo pode não funcionar
        self.groupList.get_parent_window().process_updates(True)
        # seta o elemento atual
        treemodel = self.groupList.get_model(0)
        it = treemodel.get_iter_first()
        while (it != None):
          if treemodel.get_value(it, 0) == entry.get_text():
            self.groupList.select_iter(0, it, True)
            break
          it = treemodel.iter_next(it)
      else:
        gtk.Dialog.run(hildon.Note("information", self.window, _("The item \"%s\" for the group \"%s\" already exists!") % (entry.get_text(), self.atual_group)))


  def del_item_clicked(self, src):
    group = self.atual_group
    item = self.groupList.get_current_text()
    note = hildon.Note("confirmation", self.window, _("Are you sure you want to remove the item \"%s\" from the group \"%s\"?") % (item, group))
    retcode = gtk.Dialog.run(note)
    if retcode == gtk.RESPONSE_OK:
      self.database.delItem(group, item)
      self.showItens()
      self.remove_itens_from_list()
    gtk.Dialog.destroy(note)


  def add_detail_clicked(self, src):
    entry = hildon.Entry(0)
    dialog = gtk.Dialog(title=_("New Detail for Item \"%s\" in Group \"%s\"") % (self.groupList.get_current_text(), self.atual_group), parent=self.window, buttons=(_("Done"), gtk.RESPONSE_OK))
    dialog.vbox.add(hildon.Caption(None, _("Name"), entry, None, True))
    dialog.show_all()
    response = dialog.run()
    dialog.destroy()
    if response == gtk.RESPONSE_OK:
      if self.database.addDetail(self.atual_group, self.groupList.get_current_text(), entry.get_text()):
        self.show_details_for_item()
      else:
        gtk.Dialog.run(hildon.Note("information", self.window, _("The detail \"%s\" for item \"%s\" in group \"%s\" already exists!") % (entry.get_text(), self.groupList.get_current_text(), self.atual_group)))


  def del_detail_clicked(self, src):
    RemoveDetailWindow(self.database, self.atual_group, self.groupList.get_current_text(), self)


  def about_clicked(self, src):
    dialog = gtk.AboutDialog()
    dialog.set_name("pySafe")
    dialog.set_version("0.2.0")
    dialog.set_authors(["Jorge Aguilar"])
    dialog.set_translator_credits(_("translator-credits"))
    #dialog.set_logo(gtk.gdk.pixbuf_new_from_file_at_size("pysafe_128x128.png", 64, 64))
    dialog.run()


  def list_clicked(self, widget, column, user_data = None):
    if self.ignore_list_clicked:
      return

    self.ignore_list_clicked = True

    # estes botões só devem ser ativados caso seja exibido um item
    self.del_item_button.set_sensitive(False)
    self.add_detail_button.set_sensitive(False)
    self.del_detail_button.set_sensitive(False)

    self.remove_itens_from_list()

    if self.atual_group == None:
      self.atual_group = self.groupList.get_current_text()
      self.ignore_list_clicked = False
      self.showItens()
    elif self.groupList.get_current_text() == _("<< back to groups"):
      self.ignore_list_clicked = False
      self.showGroups()
    else:
      self.show_details_for_item()

      # habilita os botões
      self.del_item_button.set_sensitive(True)
      self.add_detail_button.set_sensitive(True)

    # habilita/desabilita botões dependendo do contexto
    self.del_group_button.set_sensitive(self.atual_group != None)
    self.add_item_button.set_sensitive(self.atual_group != None)

    self.ignore_list_clicked = False


  def remove_itens_from_list(self):
    # remove os itens atuais
    # o "get_children" devolve um array...por isso o índice 0
    # dentro do item_detail tem um viewport, que tem dentro um vbox, que tem dentro os elementos
    for i in self.item_detail.get_children()[0].get_children()[0].get_children():
      self.item_detail_area.remove(i)

    self.item_detail.jump_to(0, 0)


  def check_editable_details(self, editable):
    for i in self.item_detail.get_children()[0].get_children()[0].get_children():
      if type(i).__name__ == "Entry":
        i.set_editable(editable)


  def show_details_for_item(self):
    # pega os itens que deve inserir
    itens = self.database.getItem(self.atual_group, self.groupList.get_current_text())

    self.remove_itens_from_list()

    # insere os itens na tela
    keys = itens.keys()
    keys.sort(key=str.lower)
    for i in keys:
      view = hildon.Entry(0)
      view.set_text(itens[i])
      view.connect("changed", self.textview_changed, i)
      label = gtk.Label(i)
      label.set_alignment(0,0)
      self.item_detail_area.pack_start(label, False, True, 0)
      self.item_detail_area.pack_start(view, False, True, 0)

    self.check_editable_details(self.rotation_object.get_orientation() != "portrait")

    # exibe os itens
    self.item_detail_area.show_all()

    self.del_detail_button.set_sensitive(len(keys) != 0)


  def showGroups(self):
    if self.ignore_list_clicked:
      return
  
    self.ignore_list_clicked = True

    self.atual_group = None

    # pega os valores e ordena
    keys = self.database.getGroups()
    #keys.sort(key=str.lower, reverse=True)
    keys.sort(key=str.lower)

    treemodel = self.groupList.get_model(0)
    treemodel.clear()
    for i in keys:
      self.groupList.append_text(i)
    """ se usar GtkTreeView este bloco deve ser ativo...usando
        TouchSelector o de cima
    store = self.groupList.get_model()
    for i in keys:
      store.insert(0, [i])
    self.groupList.set_model(store)
    """

    self.ignore_list_clicked = False


  def showItens(self):
    if self.ignore_list_clicked:
      return
  
    self.ignore_list_clicked = True

    treemodel = self.groupList.get_model(0)
    treemodel.clear()

    """
    button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
                           hildon.BUTTON_ARRANGEMENT_VERTICAL)
    button.set_text("Some title", "some value")
    image = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_BUTTON)
    button.set_image(image)
    button.set_image_position(gtk.POS_RIGHT)
    #a = gtk.icon_factory_lookup_default(gtk.STOCK_ADD)
    #self.groupList.add(a.render_icon(gtk.Style(), gtk.TEXT_DIR_LTR, gtk.STATE_NORMAL, gtk.ICON_SIZE_BUTTON, None, None))
    self.groupList.add(button)
    """

    self.groupList.append_text(_("<< back to groups"))
    keys = self.database.getItens(self.atual_group).keys()
    keys.sort(key=str.lower)
    for i in keys:
      self.groupList.append_text(i)

    #self.ignore_list_clicked = False


  def textview_changed(self, src, detail):
    self.database.setDetail(self.atual_group, self.groupList.get_current_text(), detail, src.get_text())


  def change_password_clicked(self, src):
    while True:
      # cria a janela para pedir a senha
      passwordDialog = hildon.GetPasswordDialog(self.window, True)
      passwordDialog.set_title("")
      passwordDialog.set_caption(_("Type your new password"))
      passwordDialog.set_property("password", "")
      response = passwordDialog.run()
      pass1 = passwordDialog.get_password()
      passwordDialog.destroy() 
      if response != gtk.RESPONSE_OK:
        return

      if len(pass1) == 0:
        note = hildon.Note("information", self.window, _("The password can not be empty!"))
        gtk.Dialog.run(note)
        note.destroy()
        continue

      # solicita para redigitar
      passwordDialog = hildon.GetPasswordDialog(self.window, True)
      passwordDialog.set_title("")
      passwordDialog.set_caption(_("Re-type your new password"))
      passwordDialog.set_property("password", "")
      response = passwordDialog.run()
      passwordDialog.hide() 
      if response != gtk.RESPONSE_OK:
        return

      if passwordDialog.get_password() != pass1:
        note = hildon.Note("information", self.window, _("The passwords are not equals!"))
        gtk.Dialog.run(note)
        note.destroy()
      else:
        self.database.save(force = True, new_pass = pass1)
        note = hildon.Note("information", self.window, _("Password changed!"))
        gtk.Dialog.run(note)
        note.destroy()
        break


"""
Esta classe exibe a janela de exclusão de detalhes do item
"""
class RemoveDetailWindow:

  def __init__(self, d, g, i, c):
    self.database = d
    self.grupo = g
    self.item = i
    self.callback_function_on_close = c

    window = hildon.StackableWindow()
    window.set_border_width(6)

    # Create a new edit toolbar
    toolbar = hildon.EditToolbar(_("Choose details to delete"), _("Delete"))

    area = hildon.PannableArea()
    tree_view = self.create_treeview(gtk.HILDON_UI_MODE_EDIT)

    # Add toolbar to the window
    window.set_edit_toolbar(toolbar)

    area.add(tree_view)
    window.add(area)

    toolbar.connect("button-clicked", self.delete_button_clicked, tree_view)
    toolbar.connect_object("arrow-clicked", self.close_window, window)

    window.show_all()
    # Set window to fullscreen
    window.fullscreen()


  def close_window(self, window):
    MainWindow.show_details_for_item(self.callback_function_on_close)
    window.destroy()


  def delete_button_clicked(self, button, treeview):
      selection = treeview.get_selection()

      (model, selected_rows) = selection.get_selected_rows()

      row_references = []
      for path in selected_rows:
          ref = gtk.TreeRowReference(model, path)
          row_references.append(ref)

      for ref in row_references:
          path = ref.get_path()
          iter = model.get_iter(path)
          self.database.delDetail(self.grupo, self.item, model.get_value(iter, 0))
          model.remove(iter)


  def create_treeview(self, tvmode):
      tv = hildon.GtkTreeView(tvmode)
      renderer = gtk.CellRendererText()
      col = gtk.TreeViewColumn("Title", renderer, text=0)

      tv.append_column(col)

      # Set multiple selection mode
      selection = tv.get_selection()
      selection.set_mode(gtk.SELECTION_MULTIPLE)

      store = gtk.ListStore(gobject.TYPE_STRING)
      itens = self.database.getItem(self.grupo, self.item)
      keys = itens.keys()
      keys.sort(key=str.lower, reverse=True)
      # insere os itens na tela
      for i in keys:
        store.insert(0, [i])

      tv.set_model(store)

      return tv


def main():

  # cria a janela principal
  win = MainWindow()

  while True:
    # cria a janela para pedir a senha
    passwordDialog = hildon.GetPasswordDialog(win.window, True)
    passwordDialog.set_title("")
    passwordDialog.set_caption(_("Type your password"))
    passwordDialog.set_property("password", "")
    response = passwordDialog.run()
    pass1 = passwordDialog.get_password()
    passwordDialog.destroy() 
    if response != gtk.RESPONSE_OK:
      return

    if len(pass1) == 0:
      note = hildon.Note("information", win.window, _("The password can not be empty!"))
      gtk.Dialog.run(note)
      note.destroy()
      continue

    database = Dados(pass1)
    ret = database.load()
    if ret == database.DATABASE_OK:
      # tenta salvar o banco (para garantir que será atualizavel)
      if database.save(force = True) == False:
        gtk.Dialog.run(hildon.Note("information", win.window, _("The database are not updataple. Check file permissions or disk space.")))
      break
    elif ret == database.ARQUIVO_NAO_LOCALIZADO:
      note = hildon.Note("confirmation", win.window, _("Database file not found. Confirm the creation of a new one?"))
      if gtk.Dialog.run(note) != gtk.RESPONSE_OK:
        return
      note.destroy()

      # solicita para redigitar
      passwordDialog = hildon.GetPasswordDialog(win.window, True)
      passwordDialog.set_title("")
      passwordDialog.set_caption(_("Re-type your password"))
      passwordDialog.set_property("password", "")
      response = passwordDialog.run()
      passwordDialog.hide() 
      if response != gtk.RESPONSE_OK:
        return

      if passwordDialog.get_password() != pass1:
        note = hildon.Note("information", win.window, _("The passwords are not equals!"))
        gtk.Dialog.run(note)
        note.destroy()
      else:
        database.save(force = True)
        break
    else:
      texto = _("An undefined error has ocurred!")
      if ret == database.SENHA_INVALIDA:
        texto = _("Invalid password!")
      elif ret == database.DADOS_CORROMPIDOS:
        texto = _("Database corrupted or invalid password!")
      note = hildon.Note("information", win.window, texto)
      gtk.Dialog.run(note)
      note.destroy()

  passwordDialog.destroy() 

  win.set_database(database)
  win.show()
  gtk.main()


gettext.install('pysafe', '.')
if __name__ == "__main__":
  main()
