Add build script.

This commit is contained in:
luxick
2018-02-19 22:56:31 +01:00
parent f90930c617
commit 2e8411c6b4
181 changed files with 288 additions and 284 deletions

471
legacy/application.py Normal file
View File

@@ -0,0 +1,471 @@
import gi
import os
import copy
import re
import mtgsdk
import time
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject, Pango
from typing import Type, Dict, List
from cardvault import handlers
from cardvault import util
from cardvault import database
class Application:
# ---------------------------------Initialize the Application----------------------------------------------
def __init__(self):
# Load configuration file
self.config = self.load_config()
util.LOG_LEVEL = self.config["log_level"]
util.log("Start using config file: '{}'".format(util.get_root_filename("config.json")), util.LogLevel.Info)
self.ui = Gtk.Builder()
self.ui.add_from_file(util.get_ui_filename("mainwindow.glade"))
self.ui.add_from_file(util.get_ui_filename("overlays.glade"))
self.ui.add_from_file(util.get_ui_filename("search.glade"))
self.ui.add_from_file(util.get_ui_filename("library.glade"))
self.ui.add_from_file(util.get_ui_filename("wants.glade"))
self.ui.add_from_file(util.get_ui_filename("dialogs.glade"))
self.current_page = None
self.current_lib_tag = "Untagged"
self.db = database.CardVaultDB(util.get_root_filename(util.DB_NAME))
# Create database tables if they do not exist
self.db.db_create()
not_found = self.ui.get_object("pageNotFound")
self.pages = {
"search": self.ui.get_object("searchView"),
"library": self.ui.get_object("libraryView"),
"wants": self.ui.get_object("wantsView")
}
# Load data from cache path
util.log("Loading image cache...", util.LogLevel.Info)
self.image_cache = util.reload_image_cache(util.CACHE_PATH + "images/")
self.precon_icons = util.reload_preconstructed_icons(util.CACHE_PATH + "icons/")
self.mana_icons = util.load_mana_icons(os.path.dirname(__file__) + "/resources/mana/")
self.library = Dict[str, Type[mtgsdk.Card]]
self.tags = Dict[str, str]
self.wants = Dict[str, List[Type[mtgsdk.Card]]]
self.load_user_data()
self.ui.get_object('statusbar_icon').set_from_icon_name(util.online_icons[self.is_online()],
Gtk.IconSize.BUTTON)
self.ui.get_object('statusbar_icon').set_tooltip_text(util.online_tooltips[self.is_online()])
self.handlers = handlers.Handlers(self)
self.ui.connect_signals(self.handlers)
self.ui.get_object("mainWindow").connect('delete-event', Gtk.main_quit)
self.ui.get_object("mainWindow").show_all()
self.push_status("Card Vault ready.")
view_menu = self.ui.get_object("viewMenu")
view = self.config["start_page"] if not self.config["start_page"] == "dynamic" else self.config["last_viewed"]
start_page = [page for page in view_menu.get_children() if page.get_name() == view]
start_page[0].activate()
util.log("Launching Card Vault version {}".format(util.VERSION), util.LogLevel.Info)
if self.config.get('first_run'):
ref = '<a href="' + util.MANUAL_LOCATION + '">' + util.MANUAL_LOCATION + '</a>'
s = "Welcome to Card Vault.\n\nIf you need help using the application please refer to the manual at\n{}\n\n" \
"To increase search performance and to be able to search while offline it is advised to use the " \
"offline mode.\nDo you want to start the download?".format(ref)
response = self.show_dialog_yn("Welcome", s)
if response == Gtk.ResponseType.YES:
self.handlers.do_download_card_data(Gtk.MenuItem())
self.config['first_run'] = False
self.save_config()
def push_status(self, msg):
status_bar = self.ui.get_object("statusBar")
status_bar.pop(0)
status_bar.push(0, msg)
def show_card_details(self, card):
builder = Gtk.Builder()
builder.add_from_file(util.get_ui_filename("detailswindow.glade"))
builder.add_from_file(util.get_ui_filename("overlays.glade"))
window = builder.get_object("cardDetails")
window.set_title(card.get('name'))
# Card Image
container = builder.get_object("imageContainer")
pixbuf = util.load_card_image(card, 63 * 5, 88 * 5, self.image_cache)
image = Gtk.Image().new_from_pixbuf(pixbuf)
container.add(image)
# Name
builder.get_object("cardName").set_text(card.get('name'))
# Types
supertypes = ""
if card.get('subtypes'):
supertypes = " - " + " ".join(card.get('subtypes'))
types = " ".join(card.get('types')) + supertypes
builder.get_object("cardTypes").set_text(types)
# Rarity
builder.get_object("cardRarity").set_text(card.get('rarity') or "")
# Release
builder.get_object("cardReleaseDate").set_text(card.get('release_date') or "")
# Set
builder.get_object("cardSet").set_text(card.get('set_name'))
# Printings
all_sets = self.get_all_sets()
prints = [all_sets[set_name].get('name') for set_name in card.get('printings')]
builder.get_object("cardPrintings").set_text(", ".join(prints))
# Legalities
grid = builder.get_object("legalitiesGrid")
rows = 1
for legality in card.get('legalities') or {}:
date_label = Gtk.Label()
date_label.set_halign(Gtk.Align.END)
text_label = Gtk.Label()
text_label.set_line_wrap_mode(Pango.WrapMode.WORD)
text_label.set_line_wrap(True)
text_label.set_halign(Gtk.Align.END)
color = util.LEGALITY_COLORS[legality["legality"]]
date_label.set_markup("<span fgcolor=\"" + color + "\">" + legality["format"] + ":" + "</span>")
text_label.set_markup("<span fgcolor=\"" + color + "\">" + legality["legality"] + "</span>")
grid.attach(date_label, 0, rows + 2, 1, 1)
grid.attach(text_label, 1, rows + 2, 1, 1)
rows += 1
grid.show_all()
# Rulings
if card.get('rulings'):
store = builder.get_object("rulesStore")
for rule in card.get('rulings'):
store.append([rule["date"], rule["text"]])
else:
builder.get_object("ruleBox").set_visible(False)
window.show_all()
def eval_key_pressed(widget, event):
key, modifier = Gtk.accelerator_parse('Escape')
keyval = event.keyval
if keyval == key:
window.destroy()
window.connect("key-press-event", eval_key_pressed)
def show_dialog_ync(self, title: str, message: str) -> Gtk.ResponseType:
"""Display a simple Yes/No Question dialog and return the result"""
dialog = self.ui.get_object("ync_dialog")
dialog.set_transient_for(self.ui.get_object("mainWindow"))
dialog.set_title(title)
self.ui.get_object("ync_label").set_markup(message)
response = dialog.run()
dialog.hide()
return response
def show_dialog_yn(self, title: str, message: str) -> Gtk.ResponseType:
"""Display a simple Yes/No Question dialog and return the result"""
dialog = self.ui.get_object("yn_dialog")
dialog.set_transient_for(self.ui.get_object("mainWindow"))
dialog.set_title(title)
self.ui.get_object("yn_label").set_markup(message)
response = dialog.run()
dialog.hide()
return response
def show_message(self, title, message):
dialog = Gtk.MessageDialog(self.ui.get_object("mainWindow"), 0, Gtk.MessageType.INFO,
Gtk.ButtonsType.OK, title)
dialog.format_secondary_text(message)
dialog.run()
dialog.destroy()
def show_name_enter_dialog(self, title: str, value: str) -> str:
dialog = self.ui.get_object("nameEnterDialog") # type: Gtk.Dialog
dialog.set_transient_for(self.ui.get_object("mainWindow"))
dialog.set_title(title)
entry = self.ui.get_object("nameEnterEntry")
entry.set_text(value)
entry.grab_focus()
result = dialog.run()
dialog.hide()
if result == Gtk.ResponseType.OK:
return entry.get_text()
else:
return value
def show_preferences_dialog(self):
"""Show a dialog to adjust user preferences"""
dialog = self.ui.get_object("pref_dialog") # type: Gtk.Dialog
dialog.set_transient_for(self.ui.get_object("mainWindow"))
store = Gtk.ListStore(str, str)
for page in self.pages.keys():
store.append([page.title(), page])
store.append(["Continue where you left", "dynamic"])
page_map = {"search": 0,
"library": 1,
"decks": 2,
"wants": 3,
"dynamic": 4}
self.ui.get_object("pref_start_view_combo").set_model(store)
self.ui.get_object("pref_start_view_combo").set_active(page_map[self.config["start_page"]])
self.ui.get_object("pref_show_all_check").set_active(self.config["show_all_in_search"])
result = dialog.run()
dialog.hide()
if not result == Gtk.ResponseType.OK:
return
tree_iter = self.ui.get_object("pref_start_view_combo").get_active_iter()
value = self.ui.get_object("pref_start_view_combo").get_model().get_value(tree_iter, 1)
self.config["start_page"] = value
self.config["show_all_in_search"] = self.ui.get_object("pref_show_all_check").get_active()
self.save_config()
self.config = self.load_config()
def unsaved_changes(self) -> bool:
"""Check if database is in transaction"""
return self.db.db_unsaved_changes()
def save_config(self):
cf = util.get_root_filename("config.json")
util.save_config(self.config, cf)
@staticmethod
def load_config() -> dict:
configfile = util.get_root_filename("config.json")
return util.parse_config(configfile, util.DEFAULT_CONFIG)
def save_data(self):
util.log("Saving Data to database", util.LogLevel.Info)
start = time.time()
self.db.db_save_changes()
end = time.time()
util.log("Finished in {}s".format(str(round(end - start, 3))), util.LogLevel.Info)
self.push_status("All data saved.")
pass
def load_user_data(self):
util.log("Loading User Data from form '{}'".format(util.get_root_filename(util.DB_NAME)), util.LogLevel.Info)
start = time.time()
self.library = self.db.lib_get_all()
self.tags = self.db.tag_get_all()
self.wants = self.db.wants_get_all()
end = time.time()
util.log("Finished in {}s".format(str(round(end - start, 3))), util.LogLevel.Info)
self.push_status("All data loaded.")
def set_online(self, status: bool):
"""Online status of the application. True if no local card data is present."""
self.ui.get_object('statusbar_icon').set_from_icon_name(util.online_icons[status], Gtk.IconSize.BUTTON)
self.ui.get_object('statusbar_icon').set_tooltip_text(util.online_tooltips[status])
self.config['local_db'] = not status
self.save_config()
def is_online(self) -> bool:
"""Return the online status of the application. True if no local data present."""
return not self.config['local_db']
def get_untagged_cards(self):
lib = copy.copy(self.library)
for ids in self.tags.values():
for card_id in ids:
try:
del lib[card_id]
except KeyError:
pass
return lib
def get_tagged_cards(self, tag):
if not tag:
return self.library
else:
lib = {}
for card_id in self.tags[tag]:
lib[card_id] = self.library[card_id]
return lib
def tag_card(self, card, tag: str):
"""Add a card to tag"""
list = self.tags[tag]
list.append(card.multiverse_id)
self.db.tag_card_add(tag, card.multiverse_id)
def untag_card(self, card, tag: str):
list = self.tags[tag]
list.remove(card.multiverse_id)
self.db.tag_card_remove(tag, card.multiverse_id)
def tag_new(self, tag: str):
self.tags[tag] = []
self.db.tag_new(tag)
util.log("Tag '" + tag + "' added", util.LogLevel.Info)
self.push_status("Added Tag \"" + tag + "\"")
def tag_delete(self, tag: str):
del self.tags[tag]
self.db.tag_delete(tag)
util.log("Tag '" + tag + "' removed", util.LogLevel.Info)
self.push_status("Removed Tag \"" + tag + "\"")
def tag_rename(self, old, new):
if old == new:
return
self.tags[new] = self.tags[old]
del self.tags[old]
self.db.tag_rename(old, new)
util.log("Tag '" + old + "' renamed to '" + new + "'", util.LogLevel.Info)
def get_wanted_card_ids(self) -> List[str]:
all_ids = []
for cards in self.wants.values():
next_ids = [card['multiverse_id'] for card in cards]
all_ids = list(set(all_ids) | set(next_ids))
return all_ids
def get_wanted_cards(self, list_name: str = None) -> Dict[str, Type[mtgsdk.Card]]:
if list_name:
out = {card.multiverse_id: card for card in self.wants[list_name]}
return out
def wants_new(self, name):
"""Add a empty wants list"""
self.wants[name] = []
self.db.wants_new(name)
util.log("Want list '" + name + "' created", util.LogLevel.Info)
self.push_status("Created want list '" + name + "'")
def wants_delete(self, name: str):
"""Delete an wants list an all cards in it"""
del self.wants[name]
self.db.wants_delete(name)
util.log("Deleted Wants List '{}'".format(name), util.LogLevel.Info)
self.push_status("Deleted Wants List '{}'".format(name))
def wants_rename(self, old: str, new: str):
if old == new:
return
self.wants[new] = self.wants[old]
del self.wants[old]
self.db.wants_rename(old, new)
util.log("Want List '" + old + "' renamed to '" + new + "'", util.LogLevel.Info)
def wants_card_add(self, list_name: str, card: 'mtgsdk.Card'):
self.wants[list_name].append(card)
self.db.wants_card_add(list_name, card.multiverse_id)
util.log(card.name + " added to want list " + list_name, util.LogLevel.Info)
def wants_card_remove(self, card: mtgsdk.Card, list: str):
"""Remove a card from a wants list"""
l = self.wants[list]
l.remove(card)
self.db.wants_card_remove(list, card.multiverse_id)
util.log("Removed '{}' from wants list '{}'".format(card.name, list), util.LogLevel.Info)
def lib_card_add(self, card, tag=None):
if tag is not None:
self.tag_card(card, tag)
self.db.tag_card_add(tag, card.multiverse_id)
self.library[card.multiverse_id] = card
self.db.lib_card_add(card)
self.push_status(card.name + " added to library")
def lib_card_add_bulk(self, cards: list, tag: str = None):
for card in cards:
if tag is not None:
self.tag_card(card, tag)
self.db.tag_card_add(tag, card.multiverse_id)
self.lib_card_add(card, tag)
self.db.lib_card_add(card)
util.log("Added {} cards to library.".format(str(len(cards))), util.LogLevel.Info)
self.push_status("Added {} cards to library.".format(str(len(cards))))
def lib_card_remove(self, card):
# Check if card is tagged
is_tagged, tags = self.db.tag_card_check_tagged(card)
if is_tagged:
for tag in tags:
self.tags[tag].remove(card.multiverse_id)
self.db.tag_card_remove(tag, card.multiverse_id)
del self.library[card.multiverse_id]
self.db.lib_card_remove(card)
util.log("Removed {} from library".format(card.name), util.LogLevel.Info)
self.push_status(card.name + " removed from library")
def db_override_user_data(self):
"""Called after import of user data. Overrides existing user data in database"""
util.log("Clearing old user data", util.LogLevel.Info)
self.db.db_clear_data_user()
util.log("Attempt loading user data to database", util.LogLevel.Info)
start = time.time()
# Library
for card in self.library.values():
self.db.lib_card_add(card)
# Tags
for tag, card_ids in self.tags.items():
self.db.tag_new(tag)
for card_id in card_ids:
self.db.tag_card_add(tag, card_id)
# Wants
for list_name, cards in self.wants.items():
self.db.wants_new(list_name)
for card in cards:
self.db.wants_card_add(list_name, card.multiverse_id)
end = time.time()
util.log("Finished in {}s".format(str(round(end - start, 3))), util.LogLevel.Info)
self.push_status("User data imported")
def db_delete_card_data(self):
"""Called before before rebuilding local data storage"""
util.log("Clearing local card data", util.LogLevel.Info)
self.db.db_clear_data_card()
self.set_online(True)
util.log("Done", util.LogLevel.Info)
def db_delete_user_data(self):
"""Delete all user data"""
util.log("Clearing all user data", util.LogLevel.Info)
self.db.db_clear_data_user()
util.log("Done", util.LogLevel.Info)
def get_all_sets(self) -> dict:
if not self.is_online():
out = {s['code']: s for s in self.db.set_get_all()}
else:
out = util.load_sets(util.get_root_filename('sets'))
return out
def get_mana_icons(self, mana_string):
if not mana_string:
util.log("No mana string provided", util.LogLevel.Info)
return
icon_list = re.findall("{(.*?)}", mana_string.replace("/", "-"))
icon_name = "_".join(icon_list)
try:
icon = self.precon_icons[icon_name]
except KeyError:
icon = util.create_mana_icons(self.mana_icons, mana_string)
self.precon_icons[icon_name] = icon
return icon
def filter_lib_func(self, model, iter, data):
filter_text = self.ui.get_object("searchLibEntry").get_text()
if filter_text == "":
return True
else:
return filter_text.lower() in model[iter][1].lower()
def main():
GObject.threads_init()
Application()
Gtk.main()

114
legacy/cardlist.py Normal file
View File

@@ -0,0 +1,114 @@
import copy
import gi
from cardvault import util
from cardvault import application
from gi.repository import Gtk, GdkPixbuf, Gdk
from typing import Dict, Type
import time
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
class CardList(Gtk.ScrolledWindow):
def __init__(self, filtered, app, row_colors: Dict[str, str]):
Gtk.ScrolledWindow.__init__(self)
self.set_hexpand(True)
self.set_vexpand(True)
self.filtered = filtered
self.lib = {}
self.app = app
self.row_colors = row_colors
builder = Gtk.Builder()
builder.add_from_file(util.get_ui_filename("cardtree.glade"))
self.filter = builder.get_object("cardStoreFiltered")
self.store = builder.get_object("cardStore")
self.tree = builder.get_object("cardTree")
self.store.set_sort_func(4, self.compare_rarity, None)
self.add(self.tree)
self.selection = self.tree.get_selection()
def get_selected_cards(self) -> dict:
(model, pathlist) = self.selection.get_selected_rows()
output = {}
for path in pathlist:
tree_iter = model.get_iter(path)
card_id = model.get_value(tree_iter, 0)
card = self.lib[card_id]
output[card_id] = card
return output
def update(self, library: Dict[str, dict]):
self.store.clear()
if library is None:
return
self.lib = library
# Disable update if tree is filtered (performance)
if self.filtered:
self.tree.freeze_child_notify()
util.log("Updating tree view", util.LogLevel.Info)
start = time.time()
all_wants = self.app.get_wanted_card_ids()
for card in library.values():
if card['multiverse_id'] is not None:
color = self.get_row_color(card, self.app.library, all_wants, self.row_colors)
mana_cost = None
if not card.get('types').__contains__("Land"):
mana_cost = self.app.get_mana_icons(card.get('mana_cost'))
item = [card['multiverse_id'],
card['name'],
" ".join(card.get('supertypes') or ""),
" ".join(card.get('types') or ""),
card.get('rarity'),
card.get('power'),
card.get('toughness'),
", ".join(card.get('printings') or ""),
mana_cost,
card.get('cmc'),
card.get('set_name'),
color,
card.get('original_text')]
self.store.append(item)
end = time.time()
util.log("Time to build Table: " + str(round(end - start, 3)) + "s", util.LogLevel.Info)
util.log("Total entries: " + str(len(self.lib)), util.LogLevel.Info)
# Reactivate update for filtered trees
if self.filtered:
self.tree.thaw_child_notify()
@staticmethod
def compare_rarity(model, row1, row2, user_data):
# Column for rarity
sort_column = 4
value1 = model.get_value(row1, sort_column)
value2 = model.get_value(row2, sort_column)
if util.rarity_dict[value1.lower()] < util.rarity_dict[value2.lower()]:
return -1
elif value1 == value2:
return 0
else:
return 1
@staticmethod
def get_row_color(card, lib: dict, wants: dict, colors: dict) -> str:
if lib.__contains__(card.get('multiverse_id')):
return colors["owned"]
elif wants.__contains__(card.get('multiverse_id')):
return colors["wanted"]
else:
return colors["unowned"]

449
legacy/database.py Normal file
View File

@@ -0,0 +1,449 @@
import sqlite3
import ast
from mtgsdk import Card, Set
from cardvault import util
class CardVaultDB:
"""Data access class for sqlite3"""
def __init__(self, db_file: str):
self.db_file = db_file
self.connection = sqlite3.connect(self.db_file)
# Database operations ##############################################################################################
def db_create(self):
"""Create initial database"""
con = sqlite3.connect(self.db_file)
with con:
# Create library table
con.execute("CREATE TABLE IF NOT EXISTS `cards` "
"( `name` TEXT, `layout` TEXT, `manaCost` TEXT, `cmc` INTEGER, "
"`colors` TEXT, `names` TEXT, `type` TEXT, `supertypes` TEXT, "
"`subtypes` TEXT, `types` TEXT, `rarity` TEXT, `text` TEXT, "
"`flavor` TEXT, `artist` TEXT, `number` INTEGER, `power` TEXT, "
"`toughness` TEXT, `loyalty` INTEGER, `multiverseid` INTEGER , "
"`variations` TEXT, `watermark` TEXT, `border` TEXT, `timeshifted` "
"TEXT, `hand` TEXT, `life` TEXT, `releaseDate` TEXT, `starter` TEXT, "
"`printings` TEXT, `originalText` TEXT, `originalType` TEXT, "
"`source` TEXT, `imageUrl` TEXT, `set` TEXT, `setName` TEXT, `id` TEXT, "
"`legalities` TEXT, `rulings` TEXT, `foreignNames` TEXT, `fcolor` TEXT) ")
con.execute("CREATE TABLE IF NOT EXISTS library ( multiverseid INT PRIMARY KEY, copies INT )")
con.execute("CREATE TABLE IF NOT EXISTS tags ( tag TEXT, multiverseid INT )")
con.execute("CREATE TABLE IF NOT EXISTS wants ( listName TEXT, multiverseid INT )")
con.execute("CREATE TABLE IF NOT EXISTS sets ( code TEXT PRIMARY KEY , name TEXT, type TEXT, border TEXT, "
"mkmid INT, mkmname TEXT, releasedate TEXT, gatherercode TEXT, magiccardsinfocode TEXT, "
"booster TEXT, oldcode TEXT)")
def db_card_insert(self, card: Card):
"""Insert single card data into database"""
# Use own connection so that inserts are commited directly
con = sqlite3.connect(self.db_file)
try:
with con:
# Map card object to database tables
db_values = self.card_to_table_mapping(card)
sql_string = "INSERT INTO `cards` VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?," \
"?,?,?,?,?,?,?,?,?,?,?)"
# Insert into database
con.execute(sql_string, db_values)
except sqlite3.OperationalError as err:
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
except sqlite3.IntegrityError:
pass
def db_get_all(self):
"""Return data of all cards in database"""
sql = 'SELECT * FROM cards'
cur = self.connection.cursor()
cur.row_factory = sqlite3.Row
cur.execute(sql)
rows = cur.fetchall()
output = []
for row in rows:
card = self.table_to_card_mapping(row)
output.append(card)
return output
def db_insert_data_card(self, cards_json):
"""Insert download from mtgjson"""
c_rows = []
s_rows = []
for data in cards_json.values():
cards = []
for raw in data["cards"]:
c = Card(raw)
c.image_url = util.CARD_IMAGE_URL.format(c.multiverse_id)
c.set = data["code"]
c.set_name = data["name"]
cards.append(c)
for c in cards:
c_rows.append(self.card_to_table_mapping(c))
set = Set(data)
s_rows.append(self.set_to_table_mapping(set))
# Use separate connection to commit changes immediately
con = sqlite3.connect(self.db_file)
try:
with con:
sql_string = "INSERT INTO `cards` VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?," \
"?,?,?,?,?,?,?,?,?,?,?)"
con.executemany(sql_string, c_rows)
sql_string = "INSERT INTO `sets` VALUES (?,?,?,?,?,?,?,?,?,?,?)"
con.executemany(sql_string, s_rows)
except sqlite3.OperationalError as err:
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
except sqlite3.IntegrityError as err:
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
def db_clear_data_card(self):
"""Delete all resource data from database"""
con = sqlite3.connect(self.db_file)
try:
with con:
con.execute("DELETE FROM cards")
con.execute("DELETE FROM sets")
except sqlite3.OperationalError as err:
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
except sqlite3.IntegrityError as err:
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
def db_clear_data_user(self):
"""Delete all user data from database"""
self.db_operation('DELETE FROM library')
self.db_operation('DELETE FROM wants')
self.db_operation('DELETE FROM tags')
# Library operations ###############################################################################################
def lib_get_all(self) -> dict:
"""Load library from database"""
cur = self.connection.cursor()
cur.row_factory = sqlite3.Row
cur.execute('SELECT * FROM `library` INNER JOIN `cards` ON library.multiverseid = cards.multiverseid')
rows = cur.fetchall()
return self.rows_to_card_dict(rows)
def lib_card_add(self, card: Card):
"""Insert card into library"""
self.db_operation("INSERT INTO `library` (`copies`, `multiverseid`) VALUES (?, ?)", (1, card.multiverse_id))
def lib_card_remove(self, card: Card):
"""Remove a from the library"""
self.db_operation("DELETE FROM `library` WHERE `multiverseid` = ?", (card.multiverse_id,))
# Tag operations ###################################################################################################
def tag_get_all(self) -> dict:
"""Loads a dict from database with all tags and the card ids tagged"""
cur = self.connection.cursor()
cur.row_factory = sqlite3.Row
# First load all tags
cur.execute("SELECT `tag` FROM tags GROUP BY `tag`")
rows = cur.fetchall()
tags = {}
for row in rows:
tags[row["tag"]] = []
# Go trough all tags an load the card ids
for tag in tags.keys():
cur.execute('SELECT `multiverseid` FROM `tags` WHERE tags.tag = ? AND multiverseid NOT NULL', (tag,))
rows = cur.fetchall()
for row in rows:
tags[tag].append(row["multiverseid"])
return tags
def tag_new(self, tag: str):
"""Add a new tag to the database"""
self.db_operation("INSERT INTO `tags` VALUES (?, NULL)", (tag,))
def tag_delete(self, tag: str):
"""Remove a tag with all entries"""
self.db_operation("DELETE FROM `tags` WHERE `tag` = ?", (tag,))
def tag_rename(self, name_old: str, name_new: str):
"""Rename a tag"""
self.db_operation('UPDATE `tags` SET `tag`=? WHERE `tag` = ?;', (name_new, name_old))
def tag_card_add(self, tag: str, card_id: int):
"""Add an entry for a tagged card"""
self.db_operation("INSERT INTO `tags` VALUES (?, ?)", (tag, card_id))
def tag_card_remove(self, tag: str, card_id: int):
"""Remove a card from a tag"""
self.db_operation("DELETE FROM `tags` WHERE `tag` = ? AND `multiverseid` = ?", (tag, card_id))
def tag_card_check_tagged(self, card) -> tuple:
"""Check if a card is tagged. Return True/False and a list of tags."""
cur = self.connection.cursor()
cur.row_factory = sqlite3.Row
cur.execute('SELECT `tag` FROM `tags` WHERE tags.multiverseid = ? ', (card.multiverse_id,))
rows = cur.fetchall()
if len(rows) == 0:
return False, []
else:
tags = []
for row in rows:
tags.append(row["tag"])
return True, tags
# Wants operations #################################################################################################
def wants_get_all(self) -> dict:
"""Load all wants lists from database"""
cur = self.connection.cursor()
cur.row_factory = sqlite3.Row
# First load all lists
cur.execute("SELECT `listName` FROM wants GROUP BY `listName`")
rows = cur.fetchall()
wants = {}
for row in rows:
wants[row["listName"]] = []
# Go trough all tags an load the card ids
for list_name in wants.keys():
cur.execute('SELECT * FROM '
'(SELECT `multiverseid` FROM `wants` WHERE `listName` = ? AND multiverseid NOT NULL) tagged '
'INNER JOIN `cards` ON tagged.multiverseid = cards.multiverseid', (list_name,))
rows = cur.fetchall()
for row in rows:
wants[list_name].append(self.table_to_card_mapping(row))
return wants
def wants_new(self, name: str):
"""Add a new wants list to the database"""
self.db_operation("INSERT INTO `wants` VALUES (?, NULL)", (name,))
def wants_delete(self, name: str):
"""Remove a tag with all entries"""
self.db_operation("DELETE FROM `wants` WHERE `listName` = ?", (name,))
def wants_rename(self, name_old: str, name_new: str):
"""Rename a tag"""
self.db_operation('UPDATE `wants` SET `listName`=? WHERE `listName` = ?;', (name_new, name_old))
def wants_card_add(self, list_name: str, card_id: int):
"""Add a card entry to a wants list"""
self.db_operation("INSERT INTO `wants` VALUES (?, ?)", (list_name, card_id))
def wants_card_remove(self, list_name: str, card_id: int):
"""Remove a card from a want list """
self.db_operation("DELETE FROM `wants` WHERE `listName` = ? AND `multiverseid` = ?", (list_name, card_id))
# Query operations #################################################################################################
def search_by_name_filtered(self, term: str, filters: dict, list_size: int) -> list:
"""Search for cards based on the cards name with filter constrains"""
filter_rarity = filters["rarity"]
filer_type = filters["type"]
filter_set = filters["set"]
filter_mana = filters["mana"]
filter_mana.sort(key=lambda val: util.color_sort_order[val[0]])
sql = 'SELECT * FROM cards WHERE `name` LIKE ?'
parameters = ['%' + term + '%']
if filter_rarity != "":
sql += ' AND `rarity` = ?'
parameters.append(filter_rarity)
if filer_type != "":
sql += ' AND `types` LIKE ?'
parameters.append('%' + filer_type + '%')
if filter_set != "":
sql += ' AND `set` = ?'
parameters.append(filter_set)
if len(filter_mana) != 0:
sql += ' AND `fcolor` = ?'
parameters.append(self.filter_colors_list(filter_mana))
sql += ' LIMIT ?'
parameters.append(list_size)
con = sqlite3.connect(self.db_file)
cur = con.cursor()
cur.row_factory = sqlite3.Row
cur.execute(sql, parameters)
rows = cur.fetchall()
con.close()
output = []
for row in rows:
card = self.table_to_card_mapping(row)
output.append(card)
return output
def search_by_name(self, term: str) -> dict:
"""Search for cards based on the cards name"""
con = sqlite3.connect(self.db_file)
cur = con.cursor()
cur.row_factory = sqlite3.Row
cur.execute("SELECT * FROM cards WHERE `name` LIKE ? LIMIT 50", ('%' + term + '%',))
rows = cur.fetchall()
con.close()
return self.rows_to_card_dict(rows)
def set_get_all(self):
con = sqlite3.connect(self.db_file)
cur = con.cursor()
cur.row_factory = sqlite3.Row
cur.execute("SELECT * FROM sets")
rows = cur.fetchall()
sets = []
for row in rows:
sets.append(self.table_to_set_mapping(row))
return sets
# DB internal functions ############################################################################################
def rows_to_card_dict(self, rows):
"""Convert database rows to a card dict"""
output = {}
for row in rows:
card = self.table_to_card_mapping(row)
output[card.multiverse_id] = card
return output
def db_operation(self, sql: str, args: tuple=()):
"""Perform an arbitrary sql operation on the database"""
cur = self.connection.cursor()
try:
cur.execute(sql, args)
except sqlite3.OperationalError as err:
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
def db_save_changes(self):
try:
self.connection.commit()
except sqlite3.Error as err:
self.connection.rollback()
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
def db_unsaved_changes(self) -> bool:
"""Checks if database is currently in transaction"""
return self.connection.in_transaction
@staticmethod
def filter_colors_list(mana: list) -> str:
symbols = util.unique_list(mana)
output = [s for s in symbols if (s in util.card_colors.values())]
return "-".join(output)
@staticmethod
def filter_colors(card) -> str:
"""Extracts the colors of a card for filtering."""
output = []
if card.colors is not None:
for color in card.colors:
output.append(util.card_colors[color])
else:
output.append("C")
# TODO extract symbols from card text
return "-".join(output)
def card_to_table_mapping(self, card: Card):
"""Return the database representation of a card object"""
return (str(card.name), str(card.layout), str(card.mana_cost), card.cmc, str(card.colors), str(card.names),
str(card.type), str(card.supertypes), str(card.subtypes), str(card.types), str(card.rarity),
str(card.text),
str(card.flavor), str(card.artist), str(card.number), str(card.power), str(card.toughness),
str(card.loyalty),
card.multiverse_id, str(card.variations), str(card.watermark), str(card.border),
str(card.timeshifted),
str(card.hand), str(card.life), str(card.release_date), str(card.starter), str(card.printings),
str(card.original_text),
str(card.original_type), str(card.source), str(card.image_url), str(card.set), str(card.set_name),
str(card.id),
str(card.legalities), str(card.rulings), str(card.foreign_names), self.filter_colors(card))
@staticmethod
def table_to_card_mapping(row: sqlite3.Row):
"""Return card object representation of a table row"""
card = Card()
card.multiverse_id = row["multiverseid"]
tmp = row["name"]
card.name = tmp
card.layout = row["layout"]
card.mana_cost = row["manacost"]
card.cmc = row["cmc"]
card.colors = row["colors"]
card.type = row["type"]
card.rarity = row["rarity"]
card.text = row["text"]
card.flavor = row["flavor"]
card.artist = row["artist"]
card.number = row["number"]
card.power = row["power"]
card.toughness = row["toughness"]
card.loyalty = row["loyalty"]
card.watermark = row["watermark"]
card.border = row["border"]
card.hand = row["hand"]
card.life = row["life"]
card.release_date = row["releaseDate"]
card.starter = row["starter"]
card.original_text = row["originalText"]
card.original_type = row["originalType"]
card.source = row["source"]
card.image_url = row["imageUrl"]
card.set = row["set"]
card.set_name = row["setName"]
card.id = row["id"]
# Bool attributes
card.timeshifted = ast.literal_eval(row["timeshifted"])
# List attributes
card.names = ast.literal_eval(row["names"])
card.supertypes = ast.literal_eval(row["supertypes"])
card.subtypes = ast.literal_eval(row["subtypes"])
card.types = ast.literal_eval(row["types"])
card.printings = ast.literal_eval(row["printings"])
card.variations = ast.literal_eval(row["variations"])
# Dict attributes
card.legalities = ast.literal_eval(row["legalities"])
card.rulings = ast.literal_eval(row["rulings"])
card.foreign_names = ast.literal_eval(row["foreignNames"])
return card
@staticmethod
def set_to_table_mapping(set: Set):
"""Convert Set object to a table row"""
return (set.code, set.name, set.type, set.border, set.mkm_id, set.mkm_name, set.release_date, set.gatherer_code,
set.magic_cards_info_code, str(set.booster), set.old_code)
@staticmethod
def table_to_set_mapping(row):
"""Return Set object representation of a table row"""
set = Set()
set.code = row['code']
set.name = row['name']
set.type = row['type']
set.border = row['border']
set.mkm_id = row['mkmid']
set.mkm_name = row['mkmname']
set.release_date = row['releasedate']
set.gatherer_code = row['gatherercode']
set.magic_cards_info_code = row['magiccardsinfocode']
set.booster = ast.literal_eval(row['booster'])
set.old_code = row['oldcode']
return set

157
legacy/gui/cardtree.glade Normal file
View File

@@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="cardStore">
<columns>
<!-- column-name multiverse_id -->
<column type="gint"/>
<!-- column-name name -->
<column type="gchararray"/>
<!-- column-name supertypes -->
<column type="gchararray"/>
<!-- column-name types -->
<column type="gchararray"/>
<!-- column-name rarity -->
<column type="gchararray"/>
<!-- column-name power -->
<column type="gchararray"/>
<!-- column-name toughness -->
<column type="gchararray"/>
<!-- column-name printings -->
<column type="gchararray"/>
<!-- column-name mana_cost -->
<column type="GdkPixbuf"/>
<!-- column-name cmc -->
<column type="gint"/>
<!-- column-name set_name -->
<column type="gchararray"/>
<!-- column-name color -->
<column type="gchararray"/>
<!-- column-name original_text -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkTreeModelFilter" id="cardStoreFiltered">
<property name="child_model">cardStore</property>
</object>
<object class="GtkTreeModelSort" id="cardStoreFilteredSorted">
<property name="model">cardStoreFiltered</property>
</object>
<object class="GtkTreeView" id="cardTree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">cardStoreFilteredSorted</property>
<property name="search_column">2</property>
<property name="enable_grid_lines">horizontal</property>
<child internal-child="selection">
<object class="GtkTreeSelection">
<property name="mode">multiple</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_name">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Card Name</property>
<property name="sort_indicator">True</property>
<property name="sort_column_id">1</property>
<child>
<object class="GtkCellRendererText" id="cell_bold">
<property name="xpad">2</property>
<property name="weight">800</property>
</object>
<attributes>
<attribute name="foreground">11</attribute>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_text">
<property name="visible">False</property>
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Card Text</property>
<child>
<object class="GtkCellRendererText">
<property name="wrap_mode">word-char</property>
<property name="wrap_width">400</property>
</object>
<attributes>
<attribute name="foreground">11</attribute>
<attribute name="text">12</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_types">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Types</property>
<property name="sort_column_id">3</property>
<child>
<object class="GtkCellRendererText">
<property name="xpad">2</property>
</object>
<attributes>
<attribute name="foreground">11</attribute>
<attribute name="text">3</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_rarity">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Rarity</property>
<property name="sort_column_id">4</property>
<child>
<object class="GtkCellRendererText">
<property name="xpad">2</property>
</object>
<attributes>
<attribute name="foreground">11</attribute>
<attribute name="text">4</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_set">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Edition</property>
<property name="sort_column_id">10</property>
<child>
<object class="GtkCellRendererText">
<property name="xpad">2</property>
</object>
<attributes>
<attribute name="foreground">11</attribute>
<attribute name="text">10</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_mana">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Mana Cost</property>
<property name="sort_column_id">9</property>
<child>
<object class="GtkCellRendererPixbuf">
<property name="xalign">0.019999999552965164</property>
</object>
<attributes>
<attribute name="pixbuf">8</attribute>
</attributes>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -0,0 +1,387 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="rulesStore">
<columns>
<!-- column-name date -->
<column type="gchararray"/>
<!-- column-name rule -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkWindow" id="cardDetails">
<property name="can_focus">False</property>
<property name="default_width">600</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="row_spacing">5</property>
<property name="column_spacing">5</property>
<child>
<object class="GtkOverlay" id="imageContainer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="ruleBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Rulings</property>
<attributes>
<attribute name="scale" value="1.2"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="rulesTree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">rulesStore</property>
<property name="headers_clickable">False</property>
<property name="enable_search">False</property>
<property name="show_expanders">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection">
<property name="mode">none</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Date</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Rule</property>
<child>
<object class="GtkCellRendererText">
<property name="wrap_mode">word-char</property>
<property name="wrap_width">300</property>
</object>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">3</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="row_spacing">10</property>
<property name="column_spacing">5</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="margin_right">2</property>
<property name="label" translatable="yes">Card Name:</property>
<property name="justify">right</property>
<property name="xalign">0.89999997615814209</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="cardName">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Loading...</property>
<property name="xalign">0.10000000149011612</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.3"/>
</attributes>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="margin_right">2</property>
<property name="label" translatable="yes">Type:</property>
<property name="justify">right</property>
<property name="xalign">0.89999997615814209</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="cardTypes">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Loading...</property>
<property name="xalign">0.10000000149011612</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="margin_right">2</property>
<property name="label" translatable="yes">Rarity:</property>
<property name="justify">right</property>
<property name="xalign">0.89999997615814209</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="cardRarity">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Loading...</property>
<property name="xalign">0.10000000149011612</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="margin_right">2</property>
<property name="label" translatable="yes">Release:</property>
<property name="justify">right</property>
<property name="xalign">0.89999997615814209</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="cardReleaseDate">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Loading...</property>
<property name="xalign">0.10000000149011612</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="margin_right">2</property>
<property name="label" translatable="yes">Edition:</property>
<property name="justify">right</property>
<property name="xalign">0.89999997615814209</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="cardSet">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Loading...</property>
<property name="xalign">0.10000000149011612</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="margin_right">2</property>
<property name="label" translatable="yes">Other Printings:</property>
<property name="justify">right</property>
<property name="xalign">0.89999997615814209</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="cardPrintings">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Loading...</property>
<property name="wrap">True</property>
<property name="max_width_chars">50</property>
<property name="xalign">0.10000000149011612</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="legalitiesGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="row_spacing">2</property>
<property name="column_spacing">2</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Legality:</property>
<property name="justify">right</property>
<property name="xalign">0.89999997615814209</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
</interface>

671
legacy/gui/dialogs.glade Normal file
View File

@@ -0,0 +1,671 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkTreeStore" id="export_treestore">
<columns>
<!-- column-name check -->
<column type="gboolean"/>
<!-- column-name visible -->
<column type="gboolean"/>
<!-- column-name element -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkDialog" id="export_dialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Export Data</property>
<property name="modal">True</property>
<property name="default_width">320</property>
<property name="default_height">260</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="export_ok">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="export_cancel">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Choose what to export</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkTreeView" id="export_sel_tree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">export_treestore</property>
<property name="headers_clickable">False</property>
<property name="enable_search">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="export_col_check">
<property name="min_width">30</property>
<child>
<object class="GtkCellRendererToggle">
<signal name="toggled" handler="export_cell_toggled" swapped="no"/>
</object>
<attributes>
<attribute name="visible">1</attribute>
<attribute name="active">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="export_col_element">
<property name="title" translatable="yes">Element</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-5">export_ok</action-widget>
<action-widget response="-6">export_cancel</action-widget>
</action-widgets>
<child>
<placeholder/>
</child>
</object>
<object class="GtkDialog" id="loadDataDialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Download</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="window_position">center</property>
<property name="default_width">280</property>
<property name="type_hint">dialog</property>
<property name="deletable">False</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">15</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkButton">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="do_cancel_download" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="dl_info_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Connecting to the Internet</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSpinner" id="dl_spinner">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkProgressBar" id="dl_progress_bar">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="dl_progress_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child>
<placeholder/>
</child>
</object>
<object class="GtkDialog" id="nameEnterDialog">
<property name="can_focus">False</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="type_hint">dialog</property>
<property name="deletable">False</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="okButtonRename">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancelButtonRename">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="nameEnterEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-5">okButtonRename</action-widget>
<action-widget response="-6">cancelButtonRename</action-widget>
</action-widgets>
<child>
<placeholder/>
</child>
</object>
<object class="GtkListStore" id="views_store">
<columns>
<!-- column-name view -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Last used view</col>
</row>
</data>
</object>
<object class="GtkDialog" id="pref_dialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Preferences</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button1">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button2">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">5</property>
<property name="column_spacing">5</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">General</property>
<attributes>
<attribute name="weight" value="semibold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Start View</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="pref_start_view_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">views_store</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Search</property>
<attributes>
<attribute name="weight" value="semibold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="pref_show_all_check">
<property name="label" translatable="yes">Show results from all sets</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Display mutliple cards with the same name from diffrents sets in search results.</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-5">button1</action-widget>
<action-widget response="-6">button2</action-widget>
</action-widgets>
<child>
<placeholder/>
</child>
</object>
<object class="GtkDialog" id="yn_dialog">
<property name="can_focus">False</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
<property name="deletable">False</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">15</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="yn_yes">
<property name="label">gtk-yes</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="yn_no">
<property name="label">gtk-no</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="yn_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">2</property>
<property name="margin_right">2</property>
<property name="margin_top">2</property>
<property name="margin_bottom">2</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-8">yn_yes</action-widget>
<action-widget response="-9">yn_no</action-widget>
</action-widgets>
<child>
<placeholder/>
</child>
</object>
<object class="GtkDialog" id="ync_dialog">
<property name="can_focus">False</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
<property name="deletable">False</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">15</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="ync_yes">
<property name="label">gtk-yes</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ync_no">
<property name="label">gtk-no</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ync_cancel">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ync_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">2</property>
<property name="margin_right">2</property>
<property name="margin_top">2</property>
<property name="margin_bottom">2</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-8">ync_yes</action-widget>
<action-widget response="-9">ync_no</action-widget>
<action-widget response="-6">ync_cancel</action-widget>
</action-widgets>
<child>
<placeholder/>
</child>
</object>
</interface>

346
legacy/gui/library.glade Normal file
View File

@@ -0,0 +1,346 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkMenu" id="libListPopup">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="show" handler="lib_tree_popup_showed" swapped="no"/>
<child>
<object class="GtkMenuItem" id="tagItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Tag Card</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="untagItem">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Untag Card</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_popup_untag_cards" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="removeItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Remove from Library</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_popup_remove_card" swapped="no"/>
</object>
</child>
</object>
<object class="GtkMenu" id="tagListPopup">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="tagRename">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Rename</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_tag_list_rename" object="tagTree" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="tagDelete">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Delete</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_tag_list_delete" object="tagTree" swapped="no"/>
</object>
</child>
</object>
<object class="GtkListStore" id="tagStore">
<columns>
<!-- column-name tag -->
<column type="gchararray"/>
<!-- column-name display -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkEntryCompletion" id="tagCompleter">
<property name="model">tagStore</property>
<property name="text_column">0</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<object class="GtkPaned" id="libraryView">
<property name="name">Library</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="show" handler="do_reload_library" swapped="no"/>
<child>
<object class="GtkBox" id="lib_left_pane">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="tagTree">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="vexpand">True</property>
<property name="model">tagStore</property>
<property name="search_column">1</property>
<signal name="button-press-event" handler="do_tag_tree_press_event" swapped="no"/>
<signal name="row-activated" handler="on_tag_selected" object="tagTreeSelection" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="tagTreeSelection"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_tag">
<property name="visible">False</property>
<property name="title" translatable="yes">tag</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_tag_display">
<property name="title" translatable="yes">Tags</property>
<property name="clickable">True</property>
<property name="alignment">0.5</property>
<property name="sort_indicator">True</property>
<property name="sort_column_id">0</property>
<child>
<object class="GtkCellRendererText" id="cell_tag"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="showUntagged">
<property name="label" translatable="yes">Untagged</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="do_show_untagged_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="showAllButton">
<property name="label" translatable="yes">All</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="do_show_all_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="addTagBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="vexpand">False</property>
<child>
<object class="GtkEntry" id="newTagEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="placeholder_text" translatable="yes">New Tag</property>
<signal name="changed" handler="do_tag_entry_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="newTagButton">
<property name="label" translatable="yes">Add</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="do_new_tag_clicked" object="newTagEntry" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<object class="GtkBox" id="lib_right_pane">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkBox" id="libTools">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">2</property>
<property name="margin_right">2</property>
<property name="vexpand">False</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Showing:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="lib_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSearchEntry" id="searchLibEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<property name="placeholder_text" translatable="yes">Search</property>
<signal name="changed" handler="do_refilter_library" object="libraryContainer" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="tagCardCombo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="has_entry">True</property>
<child internal-child="entry">
<object class="GtkEntry" id="tagCardEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="caps_lock_warning">False</property>
<property name="placeholder_text" translatable="yes">Tag selected cards</property>
<property name="completion">tagCompleter</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="tagCardButton">
<property name="label" translatable="yes">Tag card(s)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="do_tag_cards" object="tagCardEntry" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkOverlay" id="libraryContainer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</object>
</interface>

299
legacy/gui/mainwindow.glade Normal file
View File

@@ -0,0 +1,299 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="mainWindow">
<property name="name">Card Vault</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">Card Vault</property>
<property name="default_width">900</property>
<property name="default_height">700</property>
<property name="icon_name">cardvault</property>
<signal name="delete-event" handler="do_delete_event" swapped="no"/>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkMenuBar" id="menuBar">
<property name="name">menuBar</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="Main">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Data</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="mainMenu">
<property name="name">mainMenu</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="saveLibrary">
<property name="label">gtk-save</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Save library to disk</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="do_save_library" swapped="no"/>
<accelerator key="s" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="exportLibrary">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Export Library Data</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_export_library" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="importLibrary">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Import Library Data</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_import_library" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="main_load_data">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Download card data to hard drive</property>
<property name="label" translatable="yes">Download Card Data</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_download_card_data" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Clear Data</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="main_clr_usr_data">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Clear User Data</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_card_data_user" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="main_clr_crd_data">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Clear Card Data</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_card_data_card" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="quitProgram">
<property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="do_delete_event" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="View">
<property name="name">View</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="viewMenu">
<property name="name">viewMenu</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkRadioMenuItem" id="searchViewItem">
<property name="name">search</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Search</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<signal name="activate" handler="on_view_changed" swapped="no"/>
<accelerator key="1" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkRadioMenuItem" id="libraryViewItem">
<property name="name">library</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Library</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">searchViewItem</property>
<signal name="activate" handler="on_view_changed" swapped="no"/>
<accelerator key="2" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkRadioMenuItem" id="wantsViewItem">
<property name="name">wants</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Wants</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">searchViewItem</property>
<signal name="activate" handler="on_view_changed" swapped="no"/>
<accelerator key="3" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="Options">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Options</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="prefs_item">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Preferences</property>
<property name="use_underline">True</property>
<signal name="activate" handler="prefs_open" swapped="no"/>
<accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="contentPage">
<property name="name">contentPage</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkStatusbar" id="statusBar">
<property name="name">statusBar</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_start">10</property>
<property name="margin_end">10</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="spacing">2</property>
<child>
<object class="GtkSpinner" id="statusbar_spinner">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="statusbar_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkImage" id="statusbar_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
</interface>

281
legacy/gui/overlays.glade Normal file
View File

@@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkGrid" id="imageLoding">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">10</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Loading Image</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkSpinner">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">end</property>
<property name="active">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<object class="GtkGrid" id="libEmpty">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">end</property>
<property name="pixel_size">100</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Use the Search View to add cards to your library</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">center</property>
<property name="label" translatable="yes">No cards in library</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="2"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<object class="GtkGrid" id="noResults">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">end</property>
<property name="pixel_size">100</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">center</property>
<property name="label" translatable="yes">No Results</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="2"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">start</property>
<property name="label" translatable="yes">No cards found</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
<object class="GtkGrid" id="pageNotFound">
<property name="name">Not Found</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="row_spacing">10</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">end</property>
<property name="pixel_size">100</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Page not found</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<object class="GtkGrid" id="searchOverlay">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="row_spacing">10</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">center</property>
<property name="label" translatable="yes">Search</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="2"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Use the entry on the left to search for cards</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">end</property>
<property name="pixel_size">100</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<object class="GtkGrid" id="wantsOverlay">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="orientation">vertical</property>
<property name="row_spacing">10</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">end</property>
<property name="pixel_size">100</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">center</property>
<property name="label" translatable="yes">No Results</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="2"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Use the Search function to add cards to your wants</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
</interface>

410
legacy/gui/search.glade Normal file
View File

@@ -0,0 +1,410 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkMenu" id="searchListPopup">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="show" handler="search_tree_popup_showed" swapped="no"/>
<child>
<object class="GtkMenuItem" id="searchListPopupDetails">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Show Details</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_show_card_details" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="searchListPopupAdd">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Add to Library</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_search_add_to_lib" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="searchListPopupAddTag">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Add &amp; Tag</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="searchListPopupWants">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Add to Wants List</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
<object class="GtkPaned" id="searchView">
<property name="name">Search</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkBox" id="searchLeftPane">
<property name="name">leftPane</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<object class="GtkBox" id="searchBox">
<property name="name">searchBox</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<object class="GtkEntry" id="searchEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="activate" handler="do_search_cards" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="searchButton">
<property name="label" translatable="yes">Search</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="do_search_cards" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="filterGrid">
<property name="name">filterGrid</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">2</property>
<property name="column_spacing">2</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Filters</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Mana Color</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="manaFilterGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkToggleButton" id="whiteManaButton">
<property name="name">W</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="blueManaButton">
<property name="name">U</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="redManaButton">
<property name="name">R</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="greenManaButton">
<property name="name">G</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="blackManaButton">
<property name="name">B</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="clearManaFilterButton">
<property name="label">gtk-clear</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="do_clear_mana_filter" object="manaFilterGrid" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Rarity</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="rarityCombo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active_id">0</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Type</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="typeCombo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active_id">0</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Edition</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="setEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="secondary_icon_stock">gtk-clear</property>
<signal name="icon-press" handler="do_clear_set_filter" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="searchClearAll">
<property name="label" translatable="yes">Clear All</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="do_search_clear_all_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<object class="GtkBox" id="searchRightPane">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<object class="GtkBox" id="selectionToolsBox">
<property name="can_focus">False</property>
<property name="margin_left">2</property>
<property name="margin_right">2</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="search_title_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">Showing results for:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="search_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">2</property>
<property name="margin_right">2</property>
<attributes>
<attribute name="weight" value="semibold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="addRemoveButton">
<property name="label" translatable="yes">Add to Library</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="do_add_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkOverlay" id="searchResults">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</object>
</interface>

222
legacy/gui/wants.glade Normal file
View File

@@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="wantsListsStore">
<columns>
<!-- column-name name -->
<column type="gchararray"/>
<!-- column-name Displayname -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkPaned" id="wantsView">
<property name="name">Wants</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="show" handler="do_reload_wants" swapped="no"/>
<child>
<object class="GtkBox" id="wantsLeftPane">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="wantsListsTree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="model">wantsListsStore</property>
<property name="search_column">0</property>
<signal name="button-press-event" handler="do_wants_tree_press_event" swapped="no"/>
<signal name="row-activated" handler="on_want_list_selected" object="wantsTreeSelection" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="wantsTreeSelection"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="col_name">
<property name="title" translatable="yes">Wants List</property>
<child>
<object class="GtkCellRendererText" id="cell"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkEntry" id="addWantsListEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="placeholder_text" translatable="yes">New Wants List</property>
<signal name="activate" handler="on_new_wants_list_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="addWatnsListButton">
<property name="label" translatable="yes">Add</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_new_wants_list_clicked" object="addWantsListEntry" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<object class="GtkBox" id="wantsRightPane">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="wantsToolBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">2</property>
<property name="margin_right">2</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Showing:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="wants_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">2</property>
<property name="margin_right">2</property>
<attributes>
<attribute name="weight" value="semibold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkOverlay" id="wantsListContainer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</object>
<object class="GtkMenu" id="wants_cardListPopup">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="wntCrdLst_add">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Add to Library</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_want_cards_add_activated" swapped="no"/>
<accelerator key="a" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="wntCrdLst_remove">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Remove Card</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_want_cards_remove_activated" swapped="no"/>
<accelerator key="Delete" signal="activate"/>
</object>
</child>
</object>
<object class="GtkMenu" id="wants_wantsListPopup">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="wantsListRenameItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Rename</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_rename_wants_list" object="wantsListsTree" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="wantsListDeleteItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Delete</property>
<property name="use_underline">True</property>
<signal name="activate" handler="do_delete_wants_list" object="wantsListsTree" swapped="no"/>
</object>
</child>
</object>
</interface>

326
legacy/handlers.py Normal file
View File

@@ -0,0 +1,326 @@
import math
import sys
import gi
gi.require_version('Gtk', '3.0')
import time, datetime
import os
import threading
from gi.repository import Gtk, GObject
from cardvault import util, application
from legacy.search import SearchHandlers
from legacy.library import LibraryHandlers
from legacy.wants import WantsHandlers
class Handlers(SearchHandlers, LibraryHandlers, WantsHandlers):
def __init__(self, app: 'application.Application'):
"""Initialize handlers for UI signals"""
self.app = app
# Token to cancel a running download
self.cancel_token = False
# Call constructor of view handlers classes
SearchHandlers.__init__(self, app)
LibraryHandlers.__init__(self, app)
WantsHandlers.__init__(self, app)
# --------------------------------- Main Window Handlers ----------------------------------------------
def do_save_library(self, item):
self.app.save_data()
def do_export_library(self, item):
dialog = Gtk.FileChooserDialog("Export Library", self.app.ui.get_object("mainWindow"),
Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
dialog.set_current_name("mtg_export-" + datetime.datetime.now().strftime("%Y-%m-%d"))
dialog.set_current_folder(os.path.expanduser("~"))
response = dialog.run()
if response == Gtk.ResponseType.OK:
# prepare export file
file = {"library": self.app.library, "tags": self.app.tags, "wants": self.app.wants}
util.export_library(dialog.get_filename(), file)
self.app.push_status("Library exported")
dialog.destroy()
def export_cell_toggled(self, widget, pos):
model = self.app.ui.get_object("export_treestore")
iter = model.get_iter(pos)
model.set_value(iter, 0, not widget.get_active())
if len(pos.split(":")) > 1:
# A child node has been clicked
pass
def do_export_json(self, item):
"""
Export user data to file
Called By: Export menu item
"""
# dialog = self.app.ui.get_object("export_dialog")
# dialog.set_transient_for(self.app.ui.get_object("mainWindow"))
#
# store = self.app.ui.get_object("export_treestore") # type: Gtk.TreeStore
# store.clear()
# store.append(None, [True, False, "Library"])
# store.append(None, [True, False, "Decks"])
# store.append(None, [True, False, "Wants Lists"])
#
# lib_iter = store.get_iter_first()
# deck_iter = store.iter_next(lib_iter)
# wants_iter = store.iter_next(deck_iter)
#
# store.append(lib_iter, [True, True, "Untagged Cards"])
# for tag in self.app.tags.keys():
# store.append(lib_iter, [True, True, tag])
#
# for name in self.app.wants.keys():
# store.append(wants_iter, [True, True, name])
#
# self.app.ui.get_object("export_sel_tree").expand_all()
#
# response = dialog.run()
# dialog.hide()
#
# if not response == Gtk.ResponseType.OK:
# return
# TODO Read treemodel to select witch parts to export
dialog = Gtk.FileChooserDialog("Export Library", self.app.ui.get_object("mainWindow"),
Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
dialog.set_current_name("mtg_export-" + datetime.datetime.now().strftime("%Y-%m-%d") + ".json")
dialog.set_current_folder(os.path.expanduser("~"))
response = dialog.run()
if response == Gtk.ResponseType.OK:
# prepare export file
file = {"library": self.app.library, "tags": self.app.tags, "wants": self.app.wants}
util.export_json(dialog.get_filename(), file)
self.app.push_status("Library exported")
dialog.destroy()
def do_import_library(self, item):
"""Called by menu item import library"""
# Show file picker dialog for import
dialog = Gtk.FileChooserDialog("Import Library", self.app.ui.get_object("mainWindow"),
Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
dialog.set_current_folder(os.path.expanduser("~"))
response = dialog.run()
if response == Gtk.ResponseType.OK:
override_question = self.app.show_dialog_yn(
"Import Library", "Importing a library will override your current library.\nProceed?")
if override_question == Gtk.ResponseType.YES:
imports = util.import_library(dialog.get_filename())
self.app.library = imports[0]
self.app.tags = imports[1]
self.app.wants = imports[2]
self.app.db_override_user_data()
self.app.current_page.emit('show')
dialog.destroy()
def do_card_data_user(self, menu_item):
"""
Handler for Clear User Data menu item.
"""
response = self.app.show_dialog_yn("Deleting All User Data", "You are about to delete all data in the "
"library.\nThis can not be undone.\nProceed?")
if response == Gtk.ResponseType.YES:
util.log("Deleting all local card data", util.LogLevel.Info)
self.app.db_delete_user_data()
util.log("Done", util.LogLevel.Info)
self.app.push_status("Library deleted")
def do_card_data_card(self, item):
"""Handler for Clear Card Data menu item"""
response = self.app.show_dialog_yn("Deleting All Card Data", "You are about to delete all local card data.\n"
"Further searches will use the internet to search "
"for cards.\nProceed?")
if response == Gtk.ResponseType.YES:
util.log("Deleting all library data", util.LogLevel.Info)
self.app.db_delete_card_data()
util.log("Done", util.LogLevel.Info)
self.app.push_status("Local card data deleted. Switching to online mode.")
def prefs_open(self, item):
"""
Handler for open preferences menu item
Called By: prefs_item menu item
"""
self.app.show_preferences_dialog()
def on_view_changed(self, item):
if item.get_active():
container = self.app.ui.get_object("contentPage")
new_page = self.app.pages[item.get_name()]
if self.app.current_page:
container.remove(self.app.current_page)
self.app.current_page = new_page
container.pack_start(self.app.current_page, True, True, 0)
container.show_all()
self.app.current_page.emit('show')
app_title = new_page.get_name() + " - " + util.APPLICATION_TITLE
self.app.ui.get_object("mainWindow").set_title(app_title)
self.app.config["last_viewed"] = new_page.get_name().lower()
self.app.save_config()
def do_delete_event(self, arg1, arg2):
if self.app.unsaved_changes():
response = self.app.show_dialog_ync("Unsaved Changes",
"You have unsaved changes in your library. "
"Save before exiting?")
if response == Gtk.ResponseType.YES:
self.app.save_data()
return False
elif response == Gtk.ResponseType.CANCEL:
return True
def do_cancel_download(self, item: Gtk.MenuItem):
"""The cancel button was pressed, set cancel_token to stop download thread"""
self.cancel_token = True
# Delete Dialog
self.app.ui.get_object("loadDataDialog").hide()
self.app.push_status("Download canceled")
util.log("Download canceled by user", util.LogLevel.Info)
def download_canceled(self):
"""The download thread was canceled and finished executing"""
self.cancel_token = False
util.log("Download thread ended", util.LogLevel.Info)
def download_failed(self, err):
# Delete Dialog
self.app.ui.get_object("loadDataDialog").hide()
self.app.push_status("Download canceled")
self.app.show_message("Download Failed", str(err))
def download_finished(self):
"""Download thread finished without errors"""
self.cancel_token = False
self.app.set_online(False)
self.app.ui.get_object("loadDataDialog").hide()
self.app.push_status("Card data downloaded")
util.log("Card data download finished", util.LogLevel.Info)
def do_download_card_data(self, item: Gtk.MenuItem):
"""Download button was pressed in the menu bar. Starts a thread to load data from the internet"""
info_string = "Start downloading card information from the internet?\n" \
"You can cancel the download at any point."
response = self.app.show_dialog_yn("Download Card Data", info_string)
if response == Gtk.ResponseType.NO:
return
# Launch download info dialog
dl_dialog = self.app.ui.get_object("loadDataDialog")
dl_dialog.set_transient_for(self.app.ui.get_object("mainWindow"))
dl_dialog.show()
# Hide Progress UI until download started
self.app.ui.get_object("dl_progress_bar").set_visible(False)
self.app.ui.get_object("dl_progress_label").set_visible(False)
# Create and start the download in a separate thread so it will not block the UI
thread = threading.Thread(target=self.load_thread)
thread.daemon = True
thread.start()
util.log("Attempt downloading all cards. This may take a while...", util.LogLevel.Info)
def load_thread(self):
"""Worker thread to download info using the mtgsdk"""
# Gatherer uses rate limit on Card.all()
# Takes ~10 minutes to download all cards
# all = self.load_thread_gatherer()
# Download from mtgjson.com
GObject.idle_add(self.load_show_insert_ui, "Downloading...")
# Waiting in case a canceled thread is still running.
while self.cancel_token:
continue
util.log("Starting download", util.LogLevel.Info)
s = time.time()
all_json = util.net_all_cards_mtgjson()
e = time.time()
util.log("Finished in {}s".format(round(e - s, 3)), util.LogLevel.Info)
if self.cancel_token:
GObject.idle_add(self.download_canceled)
return
self.app.db_delete_card_data()
GObject.idle_add(self.load_show_insert_ui, "Saving data to disk...")
util.log("Saving to sqlite", util.LogLevel.Info)
s = time.time()
GObject.idle_add(self.app.db.db_insert_data_card, all_json)
e = time.time()
util.log("Finished in {}s".format(round(e - s, 3)), util.LogLevel.Info)
self.download_finished()
def load_thread_gatherer(self):
all = []
all_num = util.get_all_cards_num()
all_pages = int(math.ceil(all_num / 100))
# Paging for ui control between downloads
for i in range(all_pages):
req_start = time.time()
try:
new_cards = Card.where(page=i).where(pageSize=100).all()
except MtgException as err:
util.log(str(err), util.LogLevel.Error)
return
all = all + new_cards
req_end = time.time()
# Check if the action was canceled during download
if self.cancel_token:
GObject.idle_add(self.download_canceled)
return
# Activate download UI
self.app.ui.get_object("dl_spinner").set_visible(False)
self.app.ui.get_object("dl_progress_bar").set_visible(True)
self.app.ui.get_object("dl_progress_label").set_visible(True)
passed = str(round(req_end - req_start, 3))
GObject.idle_add(self.load_update_ui, all, all_num, passed)
return all
def load_update_ui(self, current_list: list, max_cards: int, time_passed: str):
"""Called from withing the worker thread. Updates the download dialog with infos."""
# Get info widgets
info_label = self.app.ui.get_object("dl_info_label")
progress_label = self.app.ui.get_object("dl_progress_label")
bar = self.app.ui.get_object("dl_progress_bar")
# Compute numbers for display
size_human = util.sizeof_fmt(sys.getsizeof(current_list))
size_bytes = sys.getsizeof(current_list)
percent = len(current_list) / max_cards
# Update UI
info_label.set_text("Downloading Cards...")
progress_label.set_text("{:.1%} ({})".format(percent, size_human))
bar.set_fraction(percent)
util.log("Downloading: {:.1%} | {} Bytes | {}s".format(percent, size_bytes, time_passed), util.LogLevel.Info)
def load_show_insert_ui(self, info: str):
"""Called from worker thread after download finished. Sets UI to display the passed string"""
self.app.ui.get_object("dl_info_label").set_text(info)
self.app.ui.get_object("dl_spinner").set_visible(True)
self.app.ui.get_object("dl_progress_bar").set_visible(False)
self.app.ui.get_object("dl_progress_label").set_visible(False)

249
legacy/library.py Normal file
View File

@@ -0,0 +1,249 @@
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from cardvault import application
from cardvault import util
from cardvault import cardlist
class LibraryHandlers:
def __init__(self, app: 'application.Application'):
"""Initialize the library view"""
self.app = app
self.active_tag = "All"
# Create Tree View for library
container = self.app.ui.get_object("libraryContainer")
card_list = cardlist.CardList(True, self.app, util.GENERIC_TREE_COLORS)
card_list.set_name("libScroller")
# Show details
card_list.tree.connect("row-activated", self.on_library_card_selected)
# Show Context menu
card_list.tree.connect("button-press-event", self.on_library_tree_press_event)
card_list.filter.set_visible_func(self.app.filter_lib_func)
container.add(card_list)
container.add_overlay(self.app.ui.get_object("noResults"))
container.show_all()
self.app.ui.get_object("noResults").set_visible(False)
def do_reload_library(self, view):
"""Handler for the 'show' signal"""
self.reload_library()
def do_tag_entry_changed(self, entry):
input_valid = entry.get_text() and entry.get_text() != ""
self.app.ui.get_object("newTagButton").set_sensitive(input_valid)
def do_new_tag_clicked(self, entry):
self.add_new_tag(entry.get_text())
entry.set_text("")
def do_show_all_clicked(self, button):
# Clear selection in tag list
self.app.ui.get_object("tagTree").get_selection().unselect_all()
self.active_tag = "All"
self.reload_library("All")
def do_show_untagged_clicked(self, button):
# Clear selection in tag list
self.app.ui.get_object("tagTree").get_selection().unselect_all()
self.active_tag = "Untagged"
self.reload_library("Untagged")
def do_tag_cards(self, entry):
card_view = self.app.ui.get_object("libraryContainer").get_child()
selected_cards = card_view.get_selected_cards()
tag = entry.get_text()
if tag != "":
self.tag_cards(selected_cards, tag)
self.reload_library(tag)
entry.set_text("")
def on_tag_selected(self, selection, path, column):
(model, pathlist) = selection.get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
tag = model.get_value(tree_iter, 0)
self.active_tag = tag
self.reload_library(tag)
def do_tag_tree_press_event(self, tree, event):
if event.button == 3: # right click
path = tree.get_path_at_pos(int(event.x), int(event.y))
if path:
tree_iter = tree.get_model().get_iter(path[0])
tag = tree.get_model().get_value(tree_iter, 0)
self.app.ui.get_object("tagListPopup").popup(None, None, None, None, 0, event.time)
def do_tag_list_rename(self, tree):
(model, pathlist) = tree.get_selection().get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
tag = model.get_value(tree_iter, 0)
new_name = self.app.show_name_enter_dialog("Rename Tag", tag)
if new_name and new_name != "":
self.app.tag_rename(tag, new_name)
self.app.current_page.emit('show')
def do_tag_list_delete(self, tree):
(model, pathlist) = tree.get_selection().get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
tag = model.get_value(tree_iter, 0)
question = "Really delete '{}'?".format(tag)
response = self.app.show_dialog_yn("Delete Tag", question)
if response == Gtk.ResponseType.YES:
self.app.tag_delete(tag)
self.app.current_page.emit('show')
@staticmethod
def do_refilter_library(container):
# Access Card View inside of container
container.get_child().filter.refilter()
def lib_tree_popup_showed(self, menu):
"""
Construct the context menu for the card tree in library view.
Menu items can vary if one or more cards are selected an if they are already tagged.
Called By: libListPopup UI Element
"""
tree = self.app.ui.get_object("libraryContainer").get_child()
selected = tree.get_selected_cards()
root = self.app.ui.get_object("tagItem")
tags_men = Gtk.Menu()
root.set_submenu(tags_men)
for list_name in self.app.tags.keys():
item = Gtk.MenuItem()
tags_men.add(item)
item.set_label(list_name)
item.connect('activate', self.tag_cards_sig, selected, list_name)
# Add separator
tags_men.add(Gtk.SeparatorMenuItem())
# Add new tag item
new_tag = Gtk.MenuItem("New Tag")
new_tag.connect('activate', self.lib_new_tag_and_add, selected)
tags_men.add(new_tag)
root.show_all()
# Check if a selected card is tagged
for id_list in self.app.tags.values():
for card_id in selected.keys():
if id_list.__contains__(card_id):
self.app.ui.get_object("untagItem").set_sensitive(True)
return
def do_popup_untag_cards(self, item):
# Get selected cards
card_list = self.app.ui.get_object("libraryContainer").get_child()
cards = card_list.get_selected_cards()
tag = self.active_tag
for card in cards.values():
self.app.untag_card(card, tag)
self.reload_library(tag)
self.reload_tag_list(clear_selection=True)
def do_popup_remove_card(self, item):
# Get selected cards
card_list = self.app.ui.get_object("libraryContainer").get_child()
cards = card_list.get_selected_cards()
# Remove selected cards
for card in cards.values():
self.app.lib_card_remove(card)
self.reload_library(self.active_tag)
self.reload_tag_list(clear_selection=True)
# ---------------------------------Library Tree----------------------------------------------
def on_library_card_selected(self, tree, row_no, column):
(model, path_list) = tree.get_selection().get_selected_rows()
for path in path_list:
tree_iter = model.get_iter(path)
card_id = model.get_value(tree_iter, 0)
card_list = self.app.ui.get_object("libraryContainer").get_child()
card = card_list.lib[card_id]
self.app.show_card_details(card)
def on_library_tree_press_event(self, treeview, event):
if event.button == 3: # right click
path = treeview.get_path_at_pos(int(event.x), int(event.y))
# Get the selection
selection = treeview.get_selection()
# Get the selected path(s)
rows = selection.get_selected_rows()
# If not clicked on selection, change selected rows
if path[0] not in rows[1]:
selection.unselect_all()
selection.select_path(path[0])
self.app.ui.get_object("libListPopup").emit('show')
self.app.ui.get_object("libListPopup").popup(None, None, None, None, 0, event.time)
return True
# -------------------------- Class Functions -------------------------------
def reload_library(self, tag="All"):
if tag == "Untagged":
lib = self.app.get_untagged_cards()
elif tag == "All":
lib = self.app.library
else:
lib = self.app.get_tagged_cards(tag)
self.reload_tag_list(not (tag == "All" or tag == "Untagged"))
tag_combo = self.app.ui.get_object("tagCardCombo")
tag_combo.set_model(self.app.ui.get_object("tagStore"))
card_tree = self.app.ui.get_object("libraryContainer").get_child()
if lib:
self.app.ui.get_object("noResults").set_visible(False)
card_tree.update(lib)
else:
card_tree.store.clear()
self.app.ui.get_object("noResults").set_visible(True)
self.app.ui.get_object("lib_title").set_text(tag)
def lib_new_tag_and_add(self, item, cards):
response = self.app.show_name_enter_dialog("Enter name for new Tag", "")
if not response == "":
self.app.tag_new(response)
self.tag_cards(cards, response)
else:
util.log("No tag name entered", util.LogLevel.Warning)
self.app.push_status("No name for new tag entered")
self.reload_library(self.active_tag)
def add_new_tag(self, name):
self.app.tag_new(name)
self.reload_tag_list(True)
def reload_tag_list(self, clear_selection=False):
"""Reload left pane tag list"""
tree = self.app.ui.get_object("tagTree")
(path, column) = tree.get_cursor()
store = tree.get_model()
store.clear()
for tag, ids in self.app.tags.items():
store.append([tag, tag + " (" + str(len(ids)) + ")"])
if clear_selection:
tree.set_cursor(path if path else 0)
store.set_sort_column_id(1, Gtk.SortType.ASCENDING)
def tag_cards_sig(self, wigdet, cards, tag):
self.tag_cards(cards, tag)
self.reload_library(self.active_tag)
def tag_cards(self, card_list, tag):
# Check if tag exist and create if necessary
if not self.app.tags.__contains__(tag):
self.app.tag_new(tag)
for card in card_list.values():
if not self.app.tags[tag].__contains__(card.multiverse_id):
self.app.tag_card(card, tag)

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

BIN
legacy/resources/mana/0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
legacy/resources/mana/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
legacy/resources/mana/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
legacy/resources/mana/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
legacy/resources/mana/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
legacy/resources/mana/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
legacy/resources/mana/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
legacy/resources/mana/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
legacy/resources/mana/8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
legacy/resources/mana/9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
legacy/resources/mana/B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
legacy/resources/mana/C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
legacy/resources/mana/G.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
legacy/resources/mana/R.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
legacy/resources/mana/S.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
legacy/resources/mana/T.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
legacy/resources/mana/U.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
legacy/resources/mana/W.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
legacy/resources/mana/X.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
legacy/resources/mana/Y.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
legacy/resources/mana/Z.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

341
legacy/search.py Normal file
View File

@@ -0,0 +1,341 @@
import time
from urllib.error import URLError, HTTPError
from cardvault import application, cardlist, util
# Deprecated
from mtgsdk import Card
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class SearchHandlers:
def __init__(self, app: 'application.Application'):
self.app = app
buttons = [x for x in self.app.ui.get_object("manaFilterGrid").get_children()
if isinstance(x, Gtk.ToggleButton)]
self._init_mana_buttons(buttons)
self._init_set_entry(self.app.ui.get_object("setEntry"))
self._init_combo_box(self.app.ui.get_object("rarityCombo"), util.rarity_dict.keys())
self._init_combo_box(self.app.ui.get_object("typeCombo"), util.card_types)
self._init_results_tree()
def do_search_cards(self, sender):
search_term = self.app.ui.get_object("searchEntry").get_text()
filters = self.get_filters()
results = self.search_cards(search_term, filters)
card_list = self.app.ui.get_object("searchResults").get_child()
card_list.update(results)
self.app.ui.get_object("searchOverlay").set_visible(False)
self.app.ui.get_object("search_title_label").set_visible(True)
self.app.ui.get_object("search_title").set_text(search_term)
@staticmethod
def do_clear_mana_filter(mana_filter_grid):
for toggle_button in mana_filter_grid.get_children():
if isinstance(toggle_button, Gtk.ToggleButton):
toggle_button.set_active(False)
@staticmethod
def do_clear_set_filter(entry, icon_pos, button):
entry.set_text("")
def do_add_clicked(self, button):
card_view = self.app.ui.get_object("searchResults").get_child()
(model, pathlist) = card_view.selection.get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
card_id = model.get_value(tree_iter, 0)
card = card_view.lib[card_id]
self.app.lib_card_add(card)
self.reload_search_view()
self.app.ui.get_object("searchEntry").grab_focus()
def search_tree_popup_showed(self, menu):
# Create tag submenu
tags_item = self.app.ui.get_object("searchListPopupAddTag")
tags_sub = Gtk.Menu()
tags_item.set_submenu(tags_sub)
for list_name in self.app.tags.keys():
item = Gtk.MenuItem()
tags_sub.add(item)
item.set_label(list_name)
item.connect('activate', self.search_popup_add_tags)
# Add separator
tags_sub.add(Gtk.SeparatorMenuItem())
# Add new tag item
new_tag = Gtk.MenuItem("New Tag")
new_tag.connect('activate', self.new_tag_and_add)
tags_sub.add(new_tag)
tags_item.show_all()
# Create wants Submenu
wants_item = self.app.ui.get_object("searchListPopupWants")
wants_sub = Gtk.Menu()
wants_item.set_submenu(wants_sub)
for list_name in self.app.wants.keys():
item = Gtk.MenuItem()
wants_sub.add(item)
item.set_label(list_name)
item.connect('activate', self.search_popup_add_wants)
# Add separator
wants_sub.add(Gtk.SeparatorMenuItem())
# Add new tag item
new_want = Gtk.MenuItem("New Want List")
new_want.connect('activate', self.new_wants_and_add)
wants_sub.add(new_want)
wants_item.show_all()
def new_tag_and_add(self, menu_item):
# Get selected cards
card_list = self.app.ui.get_object("searchResults").get_child()
cards = card_list.get_selected_cards()
response = self.app.show_name_enter_dialog("Enter name for new Tag", "")
if not response == "":
self.app.tag_new(response)
for card in cards.values():
self.app.lib_card_add(card, response)
else:
util.log("No tag name entered", util.LogLevel.Warning)
self.app.push_status("No name for new tag entered")
self.reload_search_view()
def new_wants_and_add(self, menu_item):
# Get selected cards
card_list = self.app.ui.get_object("searchResults").get_child()
cards = card_list.get_selected_cards()
response = self.app.show_name_enter_dialog("Enter name for new Want List", "")
if not response == "":
self.app.wants_new(response)
for card in cards.values():
self.app.wants_card_add(response, card)
else:
util.log("No list name entered", util.LogLevel.Warning)
self.app.push_status("No name for new wants list entered")
self.reload_search_view()
def search_popup_add_tags(self, item):
# Get selected cards
card_list = self.app.ui.get_object("searchResults").get_child()
cards = card_list.get_selected_cards()
for card in cards.values():
self.app.lib_card_add(card, item.get_label())
self.reload_search_view()
self.app.push_status("Added " + str(len(cards)) + " card(s) to library.")
def search_popup_add_wants(self, item):
# Get selected cards
card_list = self.app.ui.get_object("searchResults").get_child()
cards = card_list.get_selected_cards()
for card in cards.values():
self.app.wants_card_add(item.get_label(), card)
self.reload_search_view()
self.app.push_status("Added " + str(len(cards)) + " card(s) to Want List '" + item.get_label() + "'")
def do_search_clear_all_clicked(self, button):
""" Rest all controls in search view """
self.app.ui.get_object("searchEntry").set_text("")
self.do_clear_mana_filter(self.app.ui.get_object("manaFilterGrid"))
self.app.ui.get_object("rarityCombo").set_active(0)
self.app.ui.get_object("typeCombo").set_active(0)
self.app.ui.get_object("setEntry").set_text("")
def do_show_card_details(self, menu_item):
tree = self.app.ui.get_object("searchResults").get_child()
cards = tree.get_selected_cards()
for card in cards.values():
self.app.show_card_details(card)
def do_search_add_to_lib(self, menu_item):
tree = self.app.ui.get_object("searchResults").get_child()
cards = tree.get_selected_cards()
for card in cards.values():
self.app.lib_card_add(card)
self.reload_search_view()
def reload_search_view(self):
""" Reload the card tree """
results_tree = self.app.ui.get_object("searchResults").get_child()
cards = results_tree.lib
results_tree.update(cards)
def get_filters(self) -> dict:
""" Read selected filters from UI and return values as dict """
output = {"mana": []}
for button in self.app.ui.get_object("manaFilterGrid").get_children():
if isinstance(button, Gtk.ToggleButton):
if button.get_active():
output["mana"].append(button.get_name())
# Rarity
combo = self.app.ui.get_object("rarityCombo")
output["rarity"] = self._get_combo_value(combo)
# Type
combo = self.app.ui.get_object("typeCombo")
output["type"] = self._get_combo_value(combo)
# Set
name = self.app.ui.get_object("setEntry").get_text()
output["set"] = ""
for mtgset in self.app.get_all_sets().values():
if mtgset['name'] == name:
output["set"] = mtgset['code']
return output
def search_cards(self, term: str, filters: dict) -> dict:
"""Return a dict of cards based on a search term and filters"""
cards = {}
# Check if a local database can be used for searching
if self.app.config["local_db"]:
util.log("Starting local search for '" + term + "'", util.LogLevel.Info)
start = time.time()
cards = self.app.db.search_by_name_filtered(term, filters, 100)
end = time.time()
util.log("Card info fetched in {}s".format(round(end - start, 3)), util.LogLevel.Info)
else:
util.log("Starting online search for '" + term + "'", util.LogLevel.Info)
util.log("Used Filters: " + str(filters), util.LogLevel.Info)
# Load card info from internet
try:
util.log("Fetching card info ...", util.LogLevel.Info)
start = time.time()
cards = Card.where(name=term) \
.where(colorIdentity=",".join(filters["mana"])) \
.where(types=filters["type"]) \
.where(set=filters["set"]) \
.where(rarity=filters["rarity"]) \
.where(pageSize=50) \
.where(page=1).all()
cards = [card.__dict__ for card in cards]
end = time.time()
util.log("Card info fetched in {}s".format(round(end - start, 3)), util.LogLevel.Info)
except (URLError, HTTPError) as err:
util.log(err, util.LogLevel.Error)
return {}
if not self.app.config["show_all_in_search"]:
cards = self._remove_duplicates(cards)
if len(cards) == 0:
# TODO UI show no cards found
util.log("No Cards found", util.LogLevel.Info)
return {}
util.log("Found " + str(len(cards)) + " cards", util.LogLevel.Info)
return {card['multiverse_id']: card for card in cards}
# ---------------------------------Search Tree----------------------------------------------
def on_search_card_selected(self, tree, row_no, column):
(model, path_list) = tree.get_selection().get_selected_rows()
for path in path_list:
tree_iter = model.get_iter(path)
card_id = model.get_value(tree_iter, 0)
card_list = self.app.ui.get_object("searchResults").get_child()
card = card_list.lib[card_id]
self.app.show_card_details(card)
def on_search_selection_changed(self, selection):
(model, pathlist) = selection.get_selected_rows()
tools = self.app.ui.get_object("selectionToolsBox")
add_remove_button = self.app.ui.get_object("addRemoveButton")
if pathlist:
add_remove_button.set_sensitive(True)
else:
add_remove_button.set_sensitive(False)
def on_search_tree_press_event(self, treeview, event):
if event.button == 3: # right click
path = treeview.get_path_at_pos(int(event.x), int(event.y))
# Get the selection
selection = treeview.get_selection()
# Get the selected path(s)
rows = selection.get_selected_rows()
# If not clicked on selection, change selected rows
if path:
if path[0] not in rows[1]:
selection.unselect_all()
selection.select_path(path[0])
self.app.ui.get_object("searchListPopup").emit('show')
self.app.ui.get_object("searchListPopup").popup(None, None, None, None, 0, event.time)
return True
# -------------------------- Class Functions -------------------------------
def _init_results_tree(self):
overlay = self.app.ui.get_object("searchResults")
card_list = cardlist.CardList(False, self.app, util.SEARCH_TREE_COLORS)
card_list.set_name("resultsScroller")
card_list.tree.connect("row-activated", self.on_search_card_selected)
card_list.selection.connect("changed", self.on_search_selection_changed)
overlay.add(card_list)
overlay.add_overlay(self.app.ui.get_object("searchOverlay"))
overlay.show_all()
# Connect signal for context menu
card_list.tree.connect("button-press-event", self.on_search_tree_press_event)
@staticmethod
def _init_combo_box(combo, card_list: list):
""" Initialize a combo box model """
model = Gtk.ListStore(str)
model.append(["All"])
for entry in card_list:
model.append([entry.title()])
combo.set_model(model)
cell = Gtk.CellRendererText()
combo.pack_start(cell, True)
combo.add_attribute(cell, "text", 0)
combo.set_active(0)
@staticmethod
def _remove_duplicates(cards: list) -> list:
""" Remove cards with the same name from a list """
unique_cards = []
unique_names = []
# Reverse cardlist so we get the version with the most modern art
for card in reversed(cards):
if card['name'] not in unique_names:
unique_names.append(card['name'])
unique_cards.append(card)
return unique_cards
@staticmethod
def _get_combo_value(combo) -> str:
""" Get value from a combo box control """
tree_iter = combo.get_active_iter()
value = combo.get_model().get_value(tree_iter, 0)
return value.replace("All", "")
def _init_mana_buttons(self, button_list):
""" Initialize mana buttons """
for button in button_list:
image = Gtk.Image.new_from_pixbuf(self.app.get_mana_icons("{" + button.get_name() + "}"))
button.set_image(image)
def _init_set_entry(self, entry):
""" Initialize model for set entry """
set_store = Gtk.ListStore(str, str)
for mtgset in self.app.get_all_sets().values():
set_store.append([mtgset.get('name'), mtgset.get('code')])
completer = Gtk.EntryCompletion()
completer.set_model(set_store)
completer.set_text_column(0)
entry.set_completion(completer)

420
legacy/util.py Normal file
View File

@@ -0,0 +1,420 @@
import copy
import enum
import json
import jsonpickle
import os
import re
import sys
import six.moves.cPickle as pickle
from time import localtime, strftime, time
from PIL import Image as PImage
import urllib.request
from urllib import request
from mtgsdk import Set, Card, MtgException
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GdkPixbuf, GLib
# Title of the Program Window
APPLICATION_TITLE = "Card Vault"
# Program version
VERSION = "0.6.0"
# Path of image cache
CACHE_PATH = os.path.expanduser('~') + "/.cardvault/"
IMAGE_CACHE_PATH = os.path.expanduser('~') + "/.cardvault/images/"
ICON_CACHE_PATH = os.path.expanduser('~') + "/.cardvault/icons/"
# Log level of the application
# 1 Info
# 2 Warning
# 3 Error
LOG_LEVEL = 1
# Name of the database
DB_NAME = "cardvault.db"
ALL_NUM_URL = 'https://api.magicthegathering.io/v1/cards?page=0&pageSize=100'
ALL_SETS_JSON_URL = 'https://mtgjson.com/json/AllSets-x.json'
# URL for card images. Insert card.multiverse_id.
CARD_IMAGE_URL = 'http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid={}&type=card'
# Location of manual wiki
MANUAL_LOCATION = 'https://github.com/luxick/cardvault'
# Colors for card rows in search view
SEARCH_TREE_COLORS = {
"unowned": "black",
"wanted": "#D39F30",
"owned": "#62B62F"
}
# Colors for card rows in every default view
GENERIC_TREE_COLORS = {
"unowned": "black",
"wanted": "black",
"owned": "black"
}
LEGALITY_COLORS = {
"Banned": "#C65642",
"Restricted": "#D39F30",
"Legal": "#62B62F"
}
DEFAULT_CONFIG = {
"last_viewed": "",
"show_all_in_search": True,
"start_page": "search",
"local_db": False,
"first_run": True,
"log_level": 3
}
card_colors = {
'White': 'W',
'Blue': 'U',
'Black': 'B',
'Red': 'R',
'Green': 'G'
}
color_sort_order = {
'W': 0,
'U': 1,
'B': 2,
'R': 3,
'G': 4
}
rarity_dict = {
"special": 0,
"common": 1,
"uncommon": 2,
"rare": 3,
"mythic rare": 4
}
card_types = ["Creature", "Artifact", "Instant", "Enchantment", "Sorcery", "Land", "Planeswalker"]
online_icons = {
True: 'network-wired',
False: 'drive-harddisk'
}
online_tooltips = {
True: 'Using online card data',
False: 'Using card data from local database.'
}
class LogLevel(enum.Enum):
Error = 1
Warning = 2
Info = 3
class TerminalColors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def log(msg: str, ll: LogLevel):
if ll.value <= LOG_LEVEL:
lv = "[" + ll.name + "] "
if ll.value == 2:
c = TerminalColors.WARNING
elif ll.value == 1:
c = TerminalColors.BOLD + TerminalColors.FAIL
else:
c = ""
tc = strftime("%H:%M:%S ", localtime())
print(c + lv + tc + msg + TerminalColors.ENDC)
def parse_config(filename: str, default: dict):
config = copy.copy(default)
try:
with open(filename) as configfile:
loaded_config = json.load(configfile)
config.update(loaded_config)
except IOError:
# Will just use the default config
# and create the file for manual editing
save_config(config, filename)
except ValueError:
# There's a syntax error in the config file
log("Syntax error wihle parsing config file", LogLevel.Error)
return
return config
def save_config(config: dict, filename: str):
path = os.path.dirname(filename)
if not os.path.isdir(path):
os.mkdir(path)
with open(filename, 'wb') as configfile:
configfile.write(json.dumps(config, sort_keys=True,
indent=4, separators=(',', ': ')).encode('utf-8'))
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
def get_root_filename(filename: str) -> str:
return os.path.expanduser(os.path.join('~', '.cardvault', filename))
def get_ui_filename(filename: str) -> str:
return os.path.join(os.path.dirname(__file__), 'gui', filename)
def reload_image_cache(path: str) -> dict:
cache = {}
if not os.path.isdir(path):
os.mkdir(path)
imagefiles = os.listdir(path)
for imagefile in imagefiles:
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(path + imagefile)
# Strip filename extension
imagename = os.path.splitext(imagefile)[0]
cache[int(imagename)] = pixbuf
except OSError as err:
log("Error loading image: " + str(err), LogLevel.Error)
except GLib.GError as err:
log("Error loading image: " + str(err), LogLevel.Error)
return cache
def reload_preconstructed_icons(path: str) -> dict:
cache = {}
if not os.path.exists(path):
os.makedirs(path)
files = os.listdir(path)
for file in files:
# Split filename into single icon names and remove extension
without_ext = file.split(".")[0]
names = without_ext.split("_")
# Compute size of the finished icon
pic_width = len(names) * 105
pic_height = 105
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(ICON_CACHE_PATH + file)
pixbuf = pixbuf.scale_simple(pic_width / 5, pic_height / 5, GdkPixbuf.InterpType.HYPER)
# Set name for icon
iconname = "_".join(names)
cache[iconname] = pixbuf
except OSError as err:
log("Error loading image: " + str(err), LogLevel.Error)
return cache
def load_mana_icons(path: str) -> dict:
if not os.path.exists(path):
log("Directory for mana icons not found " + path, LogLevel.Error)
return {}
icons = {}
files = os.listdir(path)
for file in files:
img = PImage.open(path + file)
# Strip file extension
name = os.path.splitext(file)[0]
icons[name] = img
return icons
def net_all_cards_mtgjson() -> dict:
with urllib.request.urlopen(ALL_SETS_JSON_URL) as url:
data = json.loads(url.read().decode())
return data
def net_load_set_list() -> dict:
""" Load the list of all MTG sets from the Gather"""
try:
start = time()
sets = Set.all()
stop = time()
log("Fetched set list in {}s".format(round(stop - start, 3)), LogLevel.Info)
except MtgException as err:
log(str(err), LogLevel.Error)
return {}
return [set.__dict__ for set in sets]
def load_sets(filename: str) -> dict:
"""
Load sets from local file if possible.
Called by: Application if in online mode
"""
if not os.path.isfile(filename):
# use mtgsdk api to retrieve al list of all sets
sets = net_load_set_list()
# Serialize the loaded data to a file
pickle.dump(sets, open(filename, 'wb'))
# Deserialize set data from local file
sets = pickle.load(open(filename, 'rb'))
# Sort the loaded sets based on the sets name
output = {}
for set in sorted(sets, key=lambda x: x.get('name')):
output[set.get('code')] = set
return output
def export_library(path, file):
try:
pickle.dump(file, open(path, 'wb'))
log("Library exported to \"" + path + "\"", LogLevel.Info)
except OSError as err:
log(str(err), LogLevel.Error)
def export_json(path, file):
"""Write file in json format"""
try:
f = open(path, 'w')
s = jsonpickle.encode(file)
f.write(s)
except OSError as err:
log(str(err), LogLevel.Error)
def import_library(path: str) -> ():
try:
imported = pickle.load(open(path, 'rb'))
except pickle.UnpicklingError as err:
log(str(err) + " while importing", LogLevel.Error)
return
# Parse imported file
try:
library = imported["library"]
tags = imported["tags"]
wants = imported["wants"]
except KeyError as err:
log("Invalid library format " + str(err), LogLevel.Error)
library = {}
tags = {}
wants = {}
log("Library imported", LogLevel.Info)
return library, tags, wants
def save_file(path, file):
# Serialize using cPickle
try:
pickle.dump(file, open(path, 'wb'))
except OSError as err:
log(str(err), LogLevel.Error)
return
log("Saved file " + path, LogLevel.Info)
def load_file(path: str):
if not os.path.isfile(path):
log(path + " does not exist", LogLevel.Warning)
return
try:
loaded = pickle.load(open(path, 'rb'))
except OSError as err:
log(str(err), LogLevel.Error)
return
return loaded
def load_dummy_image(size_x: int, size_y: int) -> GdkPixbuf:
return GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.dirname(__file__)
+ '/resources/images/dummy.jpg', size_x, size_y)
def load_card_image(card: dict, size_x: int, size_y: int, cache: dict) -> GdkPixbuf:
""" Retrieve an card image from cache or alternatively load from gatherer"""
try:
image = cache[card.get('multiverse_id')]
except KeyError:
log("No local image for " + card.get('name') + ". Loading from " + card.get('image_url'), LogLevel.Info)
filename, image = net_load_card_image(card, size_x, size_y)
cache[card.get('multiverse_id')] = image
return image
def net_load_card_image(card: dict, size_x: int, size_y: int) -> (str, GdkPixbuf):
url = card.get('image_url')
if url is None:
log("No Image URL for " + card.get('name'), LogLevel.Warning)
return load_dummy_image(size_x, size_y)
filename = IMAGE_CACHE_PATH + str(card.get('multiverse_id')) + ".png"
request.urlretrieve(url, filename)
return filename, GdkPixbuf.Pixbuf.new_from_file_at_size(filename, size_x, size_y)
def create_mana_icons(icons: dict, mana_string: str) -> GdkPixbuf:
# Convert the string to a List
safe_string = mana_string.replace("/", "-")
glyphs = re.findall("{(.*?)}", safe_string)
if len(glyphs) == 0:
return
# Compute horizontal size for the final image
size = len(glyphs) * 105
image = PImage.new("RGBA", (size, 105))
# Increment for each position of an icon
# (Workaround: 2 or more of the same icon will be rendered in the same position)
c = 0
# Go through all entries an add the correspondent icon to the final image
for icon in glyphs:
x_pos = c * 105
try:
loaded = icons[icon]
except KeyError:
log("No icon file named '" + icon + "' found.", LogLevel.Warning)
return
image.paste(loaded, (x_pos, 0))
c += 1
# Save Icon file
path = ICON_CACHE_PATH + "_".join(glyphs) + ".png"
image.save(path)
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(path)
pixbuf = pixbuf.scale_simple(image.width / 5, image.height / 5, GdkPixbuf.InterpType.HYPER)
except:
return
return pixbuf
def unique_list(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
def sizeof_fmt(num, suffix='B'):
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
def get_all_cards_num() -> int:
req = urllib.request.Request(ALL_NUM_URL, headers={'User-Agent': 'Mozilla/5.0'})
response = urllib.request.urlopen(req)
headers = response.info()._headers
for header, value in headers:
if header == 'Total-Count':
return int(value)

173
legacy/wants.py Normal file
View File

@@ -0,0 +1,173 @@
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from cardvault import cardlist
from cardvault import application
from cardvault import util
class WantsHandlers:
def __init__(self, app: 'application.Application'):
self.app = app
self.init_wants_view()
def do_reload_wants(self, view):
self.reload_wants_view()
def on_new_wants_list_clicked(self, entry):
name = entry.get_text()
entry.set_text("")
# Check if list name already exists
if self.app.wants.__contains__(name):
return
self.app.wants_new(name)
self.reload_wants_view()
def on_want_list_selected(self, selection, path, column):
(model, pathlist) = selection.get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
list_name = model.get_value(tree_iter, 0)
self.reload_wants_view(list_name)
def do_wants_tree_press_event(self, treeview, event):
if event.button == 3: # right click
path = treeview.get_path_at_pos(int(event.x), int(event.y))
# Get the selection
selection = treeview.get_selection()
# Get the selected path(s)
rows = selection.get_selected_rows()
# If not clicked on selection, change selected rows
if path:
if path[0] not in rows[1]:
selection.unselect_all()
selection.select_path(path[0])
self.app.ui.get_object("wants_wantsListPopup").popup(None, None, None, None, 0, event.time)
return True
def do_rename_wants_list(self, tree):
(model, pathlist) = tree.get_selection().get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
tag = model.get_value(tree_iter, 0)
new_name = self.app.show_name_enter_dialog("Rename Want List", tag)
if not tag == new_name:
self.app.wants_rename(tag, new_name)
self.app.current_page.emit('show')
def do_delete_wants_list(self, tree):
(model, pathlist) = tree.get_selection().get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
name = model.get_value(tree_iter, 0)
self.app.wants_delete(name)
self.app.current_page.emit('show')
def on_want_cards_add_activated(self, menu_item):
# Get selected cards
tree = self.app.ui.get_object("wantsListContainer").get_child()
selected = tree.get_selected_cards()
# Get selected list
list_tree = self.app.ui.get_object("wantsListsTree")
(model, pathlist) = list_tree.get_selection().get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
list_name = model.get_value(tree_iter, 0)
for card in selected.values():
self.app.lib_card_add(card)
self.app.wants_card_remove(card, list_name)
self.reload_wants_view(list_name)
def on_want_cards_remove_activated(self, menu_item):
# Get selected cards
tree = self.app.ui.get_object("wantsListContainer").get_child()
selected = tree.get_selected_cards()
# Get selected list
list_tree = self.app.ui.get_object("wantsListsTree")
(model, pathlist) = list_tree.get_selection().get_selected_rows()
for path in pathlist:
tree_iter = model.get_iter(path)
list_name = model.get_value(tree_iter, 0)
for card in selected.values():
self.app.wants_card_remove(card, list_name)
self.reload_wants_view(list_name)
# ---------------------------------Wants Tree----------------------------------------------
def on_wants_card_selected(self, tree, row, column):
(model, path_list) = tree.get_selection().get_selected_rows()
for path in path_list:
tree_iter = model.get_iter(path)
card_id = model.get_value(tree_iter, 0)
card_list = self.app.ui.get_object("wantsListContainer").get_child()
card = card_list.lib[card_id]
self.app.show_card_details(card)
def on_wants_cards_press_event(self, treeview, event):
if event.button == 3: # right click
path = treeview.get_path_at_pos(int(event.x), int(event.y))
# Get the selection
selection = treeview.get_selection()
# Get the selected path(s)
rows = selection.get_selected_rows()
# If not clicked on selection, change selected rows
if path[0] not in rows[1]:
selection.unselect_all()
selection.select_path(path[0])
# Show popup and emit 'show' to trigger update function of popup
self.app.ui.get_object("wants_cardListPopup").emit('show')
self.app.ui.get_object("wants_cardListPopup").popup(None, None, None, None, 0, event.time)
return True
# -------------------------- Class Functions -------------------------------
def init_wants_view(self):
# Get container for Cardlist Tree
container = self.app.ui.get_object("wantsListContainer")
# Create new Cardlist
card_list = cardlist.CardList(True, self.app, util.GENERIC_TREE_COLORS)
card_list.set_name("wantsScroller")
# Show details
card_list.tree.connect("row-activated", self.on_wants_card_selected)
card_list.tree.connect("button-press-event", self.on_wants_cards_press_event)
# Add card list to container
container.add(card_list)
container.add_overlay(self.app.ui.get_object("wantsOverlay"))
container.show_all()
# Hide no results overlay
self.app.ui.get_object("wantsOverlay").set_visible(False)
def reload_wants_view(self, selected_list: str = None):
tree = self.app.ui.get_object("wantsListContainer").get_child() # type: cardlist.CardList
cards = self.app.get_wanted_cards(selected_list)
self.reload_wants_list(True)
if cards:
self.app.ui.get_object("wantsOverlay").set_visible(False)
tree.update(cards)
else:
tree.store.clear()
self.app.ui.get_object("wantsOverlay").set_visible(True)
self.app.ui.get_object("wants_title").set_text(str(selected_list))
def reload_wants_list(self, preserve=False):
tree = self.app.ui.get_object("wantsListsTree")
(path, column) = tree.get_cursor()
store = tree.get_model()
store.clear()
for list_name, cards in self.app.wants.items():
store.append([list_name, list_name + " (" + str(len(cards)) + ")"])
if preserve:
tree.set_cursor(path if path else 0)
store.set_sort_column_id(1, Gtk.SortType.ASCENDING)