diff --git a/configuration/config_object.go b/configuration/config_object.go index d846412..0e28939 100644 --- a/configuration/config_object.go +++ b/configuration/config_object.go @@ -18,6 +18,8 @@ import ( ) type Config struct { + // Configuration from database. + Cfg map[string]string // Temporary (or runtime) configuration things. TEMP map[string]string } @@ -51,6 +53,7 @@ func (c *Config) initializePathsNix() { func (c *Config) initializeStorages() { c.TEMP = make(map[string]string) + c.Cfg = make(map[string]string) } func (c *Config) Initialize() { diff --git a/context/context_object.go b/context/context_object.go index 0bc8dde..36965eb 100644 --- a/context/context_object.go +++ b/context/context_object.go @@ -53,7 +53,7 @@ func (ctx *Context) initializeConfig() { func (ctx *Context) initializeDatabase() { ctx.Database = database.New(ctx.Cfg) - ctx.Database.Initialize() + ctx.Database.Initialize(ctx.Cfg) ctx.Database.Migrate() } diff --git a/database/database_object.go b/database/database_object.go index 7d50ca8..31d92ad 100644 --- a/database/database_object.go +++ b/database/database_object.go @@ -17,6 +17,7 @@ import ( "strconv" // local + "github.com/pztrn/urtrator/configuration" "github.com/pztrn/urtrator/datamodels" // Other @@ -25,16 +26,31 @@ import ( ) type Database struct { + // Configuration. + cfg *configuration.Config // Pointer to initialized database connection. Db *sqlx.DB } func (d *Database) Close() { fmt.Println("Closing database...") + + // Save configuration. + // Delete previous configuration. + d.Db.MustExec("DELETE FROM configuration") + tx := d.Db.MustBegin() + for k, v := range cfg.Cfg { + cfg_item := datamodels.Configuration{} + cfg_item.Key = k + cfg_item.Value = v + tx.NamedExec("INSERT INTO configuration (key, value) VALUES (:key, :value)", &cfg_item) + } + tx.Commit() + d.Db.Close() } -func (d *Database) Initialize() { +func (d *Database) Initialize(cfg *configuration.Config) { fmt.Println("Initializing database...") // Connect to database. @@ -45,6 +61,15 @@ func (d *Database) Initialize() { fmt.Println(err.Error()) } d.Db = db + + // Load configuration. + cfgs := []datamodels.Configuration{} + d.Db.Select(&cfgs, "SELECT * FROM configuration") + if len(cfgs) > 0 { + for i := range cfgs { + cfg.Cfg[cfgs[i].Key] = cfgs[i].Value + } + } } func (d *Database) Migrate() { diff --git a/database/migrations.go b/database/migrations.go index 6ed4c58..d8220b3 100644 --- a/database/migrations.go +++ b/database/migrations.go @@ -39,30 +39,13 @@ INSERT INTO database (version) VALUES (1); // Migrate database to latest version. // ToDo: make it more good :). func migrate_full(db *Database, version int) { - if version < 1 { - start_to_one(db) - version = 1 - } - if version == 1 { - one_to_two(db) - version = 2 - } - if version == 2 { - two_to_three(db) - version = 3 - } - if version == 3 { - three_to_four(db) - version = 4 - } - if version == 4 { - four_to_five(db) - version = 5 - } - if version == 5 { - five_to_six(db) - version = 6 - } + if version < 1 {start_to_one(db); version = 1} + if version == 1 {one_to_two(db); version = 2} + if version == 2 {two_to_three(db); version = 3} + if version == 3 {three_to_four(db); version = 4} + if version == 4 {four_to_five(db); version = 5} + if version == 5 {five_to_six(db); version = 6 } + if version == 6 {six_to_seven(db); version = 7} } // Initial database structure. @@ -108,3 +91,10 @@ func five_to_six(db *Database) { db.Db.MustExec("ALTER TABLE servers ADD profile_to_use VARCHAR(128) DEFAULT ''") db.Db.MustExec("UPDATE database SET version=6") } + +// Configuration storage. +func six_to_seven(db *Database) { + fmt.Println("Upgrading database from 6 to 7...") + db.Db.MustExec("CREATE TABLE configuration (key VARCHAR(128) NOT NULL, value VARCHAR(1024) NOT NULL)") + db.Db.MustExec("UPDATE database SET version=7") +} diff --git a/datamodels/configuration.go b/datamodels/configuration.go new file mode 100644 index 0000000..a33f447 --- /dev/null +++ b/datamodels/configuration.go @@ -0,0 +1,15 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package datamodels + +type Configuration struct { + Key string `db:"key"` + Value string `db:"value"` +} diff --git a/ui/mainwindow.go b/ui/mainwindow.go index 123f575..8ea9b37 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -66,6 +66,10 @@ type MainWindow struct { qc_server_address *gtk.Entry // Quick connect: password qc_password *gtk.Entry + // Tray icon. + tray_icon *gtk.StatusIcon + // Tray menu. + tray_menu *gtk.Menu // Storages. // All servers store. @@ -83,6 +87,10 @@ type MainWindow struct { // Other // Old profiles count. old_profiles_count int + + // Flags. + // Window is hidden? + hidden bool } func (m *MainWindow) addToFavorites() { @@ -280,6 +288,11 @@ func (m *MainWindow) Initialize() { // Sidebar initialization. m.initializeSidebar() + // Tray icon. + if ctx.Cfg.Cfg["/general/show_tray_icon"] == "1" { + m.initializeTrayIcon() + } + m.vbox.PackStart(m.hpane, true, true, 5) // Temporary hack. @@ -445,6 +458,9 @@ func (m *MainWindow) initializeStorages() { // Profiles count after filling combobox. Defaulting to 0. m.old_profiles_count = 0 + + // Window hidden flag. + m.hidden = false } func (m *MainWindow) InitializeTabs() { @@ -605,6 +621,41 @@ func (m *MainWindow) InitializeToolbar() { m.toolbar.Insert(fav_delete_button, 4) } +func (m *MainWindow) initializeTrayIcon() { + fmt.Println("Initializing tray icon...") + + icon_bytes, _ := base64.StdEncoding.DecodeString(common.Logo) + icon_pixbuf := gdkpixbuf.NewLoader() + icon_pixbuf.Write(icon_bytes) + logo = icon_pixbuf.GetPixbuf() + + m.tray_icon = gtk.NewStatusIconFromPixbuf(logo) + m.tray_icon.SetName("URTrator") + m.tray_icon.SetTitle("URTrator") + m.tray_icon.SetTooltipText("URTrator is ready") + + m.tray_menu = gtk.NewMenu() + + // Open/Close URTrator menu item. + open_close_item := gtk.NewMenuItemWithLabel("Show / Hide URTrator") + open_close_item.Connect("activate", m.showHide) + m.tray_menu.Append(open_close_item) + + // Separator + sep1 := gtk.NewSeparatorMenuItem() + m.tray_menu.Append(sep1) + + // Exit menu item. + exit_item := gtk.NewMenuItemWithLabel("Exit") + exit_item.Connect("activate", m.window.Destroy) + m.tray_menu.Append(exit_item) + + // Connect things. + m.tray_icon.Connect("activate", m.showHide) + m.tray_icon.Connect("popup-menu", m.showTrayMenu) + m.tray_menu.ShowAll() +} + func (m *MainWindow) launchGame() error { fmt.Println("Launching Urban Terror...") @@ -800,6 +851,22 @@ func (m *MainWindow) loadProfiles() { fmt.Println("Added " + strconv.Itoa(m.old_profiles_count) + " profiles") } +func (m *MainWindow) showHide() { + // ToDo: set window position on restore. Window loosing it on + // multimonitor configurations. + if m.hidden { + m.window.Show() + m.hidden = false + } else { + m.window.Hide() + m.hidden = true + } +} + +func (m *MainWindow) showTrayMenu(cbx *glib.CallbackContext) { + m.tray_menu.Popup(nil, nil, gtk.StatusIconPositionMenu, m.tray_icon, uint(cbx.Args(0)), uint32(cbx.Args(1))) +} + func (m *MainWindow) unlockInterface() { m.launch_button.SetSensitive(true) m.statusbar.Push(m.statusbar_context_id, "URTrator is ready.") diff --git a/ui/options.go b/ui/options.go index 7a87dc8..682ce6a 100644 --- a/ui/options.go +++ b/ui/options.go @@ -62,6 +62,15 @@ func (o *OptionsDialog) closeOptionsDialogWithDiscard() { func (o *OptionsDialog) closeOptionsDialogWithSaving() { fmt.Println("Saving changes to options...") + o.saveGeneral() + + mbox_string := "Some options require application restart to be applied." + m := gtk.NewMessageDialog(o.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, mbox_string) + m.Response(func() { + m.Destroy() + }) + m.Run() + ctx.Eventer.LaunchEvent("loadProfiles") o.window.Destroy() @@ -106,6 +115,15 @@ func (o *OptionsDialog) editProfile() { } } +func (o *OptionsDialog) fill() { + if ctx.Cfg.Cfg["/general/show_tray_icon"] == "1" { + o.show_tray_icon.SetActive(true) + } + if ctx.Cfg.Cfg["/general/urtrator_autoupdate"] == "1" { + o.autoupdate.SetActive(true) + } +} + func (o *OptionsDialog) initializeGeneralTab() { general_vbox := gtk.NewVBox(false, 0) @@ -214,6 +232,22 @@ func (o *OptionsDialog) loadProfiles() { } } +func (o *OptionsDialog) saveGeneral() { + if o.show_tray_icon.GetActive() { + ctx.Cfg.Cfg["/general/show_tray_icon"] = "1" + } else { + ctx.Cfg.Cfg["/general/show_tray_icon"] = "0" + } + + if o.autoupdate.GetActive() { + ctx.Cfg.Cfg["/general/urtrator_autoupdate"] = "1" + } else { + ctx.Cfg.Cfg["/general/urtrator_autoupdate"] = "0" + } + + fmt.Println(ctx.Cfg.Cfg) +} + func (o *OptionsDialog) ShowOptionsDialog() { o.window = gtk.NewWindow(gtk.WINDOW_TOPLEVEL) o.window.SetTitle("URTrator - Options") @@ -226,6 +260,7 @@ func (o *OptionsDialog) ShowOptionsDialog() { o.vbox = gtk.NewVBox(false, 0) o.initializeTabs() + o.fill() o.window.Add(o.vbox) o.window.ShowAll()