diff --git a/requester/pooler.go b/requester/pooler.go index fbd4666..c9b5668 100644 --- a/requester/pooler.go +++ b/requester/pooler.go @@ -29,6 +29,8 @@ type Pooler struct { maxrequests int // Packet prefix. pp string + // Current requests counter mutex. + cur_requests_mutex sync.Mutex } func (p *Pooler) Initialize() { @@ -73,11 +75,15 @@ func (p *Pooler) PingServers(servers_type string) { } } wait.Add(1) + p.cur_requests_mutex.Lock() cur_requests += 1 + p.cur_requests_mutex.Unlock() go func(srv *datamodels.Server) { defer wait.Done() p.pingServersExecutor(srv) + p.cur_requests_mutex.Lock() cur_requests -= 1 + p.cur_requests_mutex.Unlock() }(server_to_ping.Server) } wait.Wait() diff --git a/ui/qt5/about_dialog.go b/ui/qt5/about_dialog.go new file mode 100644 index 0000000..55badb7 --- /dev/null +++ b/ui/qt5/about_dialog.go @@ -0,0 +1,10 @@ +// 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 ui diff --git a/ui/qt5/exported.go b/ui/qt5/exported.go new file mode 100644 index 0000000..eb12052 --- /dev/null +++ b/ui/qt5/exported.go @@ -0,0 +1,25 @@ +// 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 ui + +import ( + // local + "github.com/pztrn/urtrator/context" +) + +var ( + ctx *context.Context +) + +func NewMainWindow(c *context.Context) *MainWindow { + ctx = c + m := MainWindow{} + return &m +} diff --git a/ui/qt5/mainwindow.go b/ui/qt5/mainwindow.go new file mode 100644 index 0000000..d3811de --- /dev/null +++ b/ui/qt5/mainwindow.go @@ -0,0 +1,118 @@ +// 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 ui + +import ( + // stdlib + "fmt" + //"runtime" + //"sort" + //"strconv" + //"strings" + + // Local + //"github.com/pztrn/urtrator/datamodels" + //"github.com/pztrn/urtrator/ioq3dataparser" + + // github + "github.com/therecipe/qt/widgets" +) + +type MainWindow struct { + ////////////////////////////////////////////////// + // Main widgets and pointers. + ////////////////////////////////////////////////// + // Application. + app *widgets.QApplication + // Main window. + window *widgets.QMainWindow + // Main menu. + mainmenu *widgets.QMenuBar + // Main vertical box. + vbox *widgets.QVBoxLayout + // Toolbar. + toolbar *widgets.QToolBar + // Splitter. + splitter *widgets.QSplitter + // Tabs widget. + tabs *widgets.QTabWidget + + ////////////////////////////////////////////////// + // Servers lists and related. + ////////////////////////////////////////////////// + // "Servers" tab list. + all_servers *widgets.QTreeView + // Hide offline servers checkbox. + all_servers_hide_offline *widgets.QCheckBox + // Hide private servers? + all_servers_hide_private *widgets.QCheckBox + // Server's version. + all_servers_version *widgets.QComboBox + // Game mode. + all_servers_gamemode *widgets.QComboBox + // Favorites tab list. + fav_servers *widgets.QTreeView + // Hide offline servers checkbox. + fav_servers_hide_offline *widgets.QCheckBox + // Hide private servers? + fav_servers_hide_private *widgets.QCheckBox + // Server's version. + fav_servers_version *widgets.QComboBox + // Game mode. + fav_servers_gamemode *widgets.QComboBox + // Sidebar's server's information widget. + sidebar_server_info *widgets.QTreeView + // Sidebar's server's players widget. + sidebar_server_players *widgets.QTreeView + + ////////////////////////////////////////////////// + // Datas. + ////////////////////////////////////////////////// + // Window size. + window_width int + window_height int + // Window position. + window_pos_x int + window_pos_y int + // Supported game modes. + gamemodes map[string]string + // Columns names for servers tabs. + column_names map[string]string + // Real columns positions on servers tabs. + column_pos map[string]map[string]int +} + +func (m *MainWindow) close(a bool) { + fmt.Println("Closing URTrator...") + m.app.Quit() +} + +func (m *MainWindow) dropDatabasesData(bool) { + fmt.Println("About to drop databases data...") +} + +func (m *MainWindow) showAboutDialog(a bool) { + fmt.Println("Showing about dialog...") +} + +func (m *MainWindow) showAboutQtDialog(a bool) { + fmt.Println("Showing about Qt dialog...") + widgets.QMessageBox_AboutQt(m.window, "About Qt") +} + + +func (m *MainWindow) showOptionsDialog(a bool) { + fmt.Println("Showing options dialog...") +} + +func (m *MainWindow) splitterMoved(pos int, index int) { + fmt.Println("Splitter moved!") + fmt.Println(index, pos) +} diff --git a/ui/qt5/mainwindow_init.go b/ui/qt5/mainwindow_init.go new file mode 100644 index 0000000..ff58e67 --- /dev/null +++ b/ui/qt5/mainwindow_init.go @@ -0,0 +1,345 @@ +// 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 ui + +import ( + // stdlib + "fmt" + "os" + //"runtime" + "sort" + "strconv" + + // local + "github.com/pztrn/urtrator/common" + + // Qt5 + "github.com/therecipe/qt/core" + "github.com/therecipe/qt/widgets" +) + +func (m *MainWindow) Initialize() { + fmt.Println("Initializing main window...") + + m.app = widgets.NewQApplication(len(os.Args), os.Args) + + m.initializeStorages() + + m.window = widgets.NewQMainWindow(nil, 0) + m.window.SetWindowTitle("URTrator") + + // Restoring window position. + var win_pos_x_str string = "0" + var win_pos_y_str string = "0" + saved_win_pos_x_str, ok := ctx.Cfg.Cfg["/mainwindow/position_x"] + if ok { + win_pos_x_str = saved_win_pos_x_str + } + saved_win_pos_y_str, ok := ctx.Cfg.Cfg["/mainwindow/position_y"] + if ok { + win_pos_y_str = saved_win_pos_y_str + } + win_pos_x, _ := strconv.Atoi(win_pos_x_str) + win_pos_y, _ := strconv.Atoi(win_pos_y_str) + + // Restoring window size. + var win_size_width_str string = "1000" + var win_size_height_str string = "600" + saved_win_size_width_str, ok := ctx.Cfg.Cfg["/mainwindow/width"] + if ok { + win_size_width_str = saved_win_size_width_str + } + saved_win_size_height_str, ok := ctx.Cfg.Cfg["/mainwindow/height"] + if ok { + win_size_height_str = saved_win_size_height_str + } + + m.window_width, _ = strconv.Atoi(win_size_width_str) + m.window_height, _ = strconv.Atoi(win_size_height_str) + m.window.SetGeometry2(win_pos_x, win_pos_y, m.window_width, m.window_height) + + m.initializeMenu() + + // Central widget. + cv := widgets.NewQWidget(nil, core.Qt__Widget) + //cv_policy := widgets.NewQSizePolicy2(widgets.QSizePolicy__Expanding, widgets.QSizePolicy__Expanding, widgets.QSizePolicy__DefaultType) + //cv.SetSizePolicy(cv_policy) + m.window.SetCentralWidget(cv) + + // Main vertical box. + m.vbox = widgets.NewQVBoxLayout() + m.vbox.SetContentsMargins(4, 4, 4, 4) + cv.SetLayout(m.vbox) + + m.initializeToolbar() + m.initializeTabs() + m.initializeSidebar() + + m.window.Show() + + // Restore splitter position. + // We will restore saved thing, or will use "window_width - 150". + saved_pane_pos, ok := ctx.Cfg.Cfg["/mainwindow/pane_negative_position"] + if ok { + pane_negative_pos, _ := strconv.Atoi(saved_pane_pos) + new_splitter_pos := m.window_width - pane_negative_pos + fmt.Println(new_splitter_pos) + m.splitter.MoveSplitter(new_splitter_pos, 1) + fmt.Println(m.splitter.ClosestLegalPosition(1, new_splitter_pos)) + } else { + g := m.window.Geometry() + w := g.Width() + m.splitter.MoveSplitter(w - 150, 1) + } + + m.splitter.ConnectSplitterMoved(m.splitterMoved) + + widgets.QApplication_Exec() +} + +func (m *MainWindow) initializeSidebar() { + sidebar_widget := widgets.NewQWidget(nil, core.Qt__Widget) + m.splitter.AddWidget(sidebar_widget) + + sidebar_layout := widgets.NewQVBoxLayout() + sidebar_layout.SetContentsMargins(4, 4, 4, 4) + sidebar_widget.SetLayout(sidebar_layout) + + // Server's information list. + m.sidebar_server_info = widgets.NewQTreeView(nil) + sidebar_layout.AddWidget(m.sidebar_server_info, 0, core.Qt__AlignHCenter & core.Qt__AlignTop) + + // Server's players widget. + m.sidebar_server_players = widgets.NewQTreeView(nil) + sidebar_layout.AddWidget(m.sidebar_server_players, 0, core.Qt__AlignHCenter & core.Qt__AlignTop) + + // Add spacer. + spacer := widgets.NewQSpacerItem(6, 6, widgets.QSizePolicy__Minimum, widgets.QSizePolicy__Expanding) + sidebar_layout.AddSpacerItem(spacer) +} + +func (m *MainWindow) initializeStorages() { + m.gamemodes = make(map[string]string) + m.gamemodes = map[string]string{ + "1": "Last Man Standing", + "2": "Free For All", + "3": "Team DM", + "4": "Team Survivor", + "5": "Follow The Leader", + "6": "Cap'n'Hold", + "7": "Capture The Flag", + "8": "Bomb", + "9": "Jump", + "10": "Freeze Tag", + "11": "Gun Game", + "12": "Instagib", + } + + // Columns names. + // Key - default position in lists. + m.column_names = map[string]string{ + "2": "Name", + "3": "Mode", + "4": "Map", + "5": "Players", + "6": "Ping", + "7": "Version", + "8": "IP", + } + // Real columns positions. + m.column_pos = make(map[string]map[string]int) + m.column_pos["Servers"] = make(map[string]int) + m.column_pos["Favorites"] = make(map[string]int) +} + +func (m *MainWindow) initializeTabs() { + m.splitter = widgets.NewQSplitter(nil) + m.splitter.SetOrientation(core.Qt__Horizontal) + m.vbox.AddWidget(m.splitter, 0, core.Qt__AlignHCenter & core.Qt__AlignTop) + + m.tabs = widgets.NewQTabWidget(nil) + m.splitter.AddWidget(m.tabs) + + // Default size policy for filters widget. + filters_size_policy := widgets.NewQSizePolicy2(widgets.QSizePolicy__Minimum, widgets.QSizePolicy__Minimum, widgets.QSizePolicy__DefaultType) + + ////////////////////////////////////////////////// + // Servers page. + ////////////////////////////////////////////////// + serverspagewidget := widgets.NewQWidget(nil, core.Qt__Widget) + serverspagewidget_policy := widgets.NewQSizePolicy2(widgets.QSizePolicy__Expanding, widgets.QSizePolicy__Expanding, widgets.QSizePolicy__DefaultType) + serverspagewidget.SetSizePolicy(serverspagewidget_policy) + m.tabs.AddTab(serverspagewidget, "Servers") + serverspagewidget_layout := widgets.NewQHBoxLayout() + serverspagewidget_layout.SetContentsMargins(4, 4, 4, 4) + serverspagewidget.SetLayout(serverspagewidget_layout) + + // Servers list. + m.all_servers = widgets.NewQTreeView(nil) + serverspagewidget_layout.AddWidget(m.all_servers, 0, core.Qt__AlignLeft & core.Qt__AlignTop) + + // Servers list filters widget. + serverspagewidget_filters_widget := widgets.NewQWidget(nil, core.Qt__Widget) + serverspagewidget_filters_widget_policy := widgets.NewQSizePolicy2(widgets.QSizePolicy__Minimum, widgets.QSizePolicy__Minimum, widgets.QSizePolicy__DefaultType) + serverspagewidget_filters_widget.SetSizePolicy(serverspagewidget_filters_widget_policy) + serverspagewidget_layout.AddWidget(serverspagewidget_filters_widget, 0, core.Qt__AlignRight & core.Qt__AlignTop) + + // Servers list filters layout. + serverspagewidget_filters_layout := widgets.NewQVBoxLayout() + serverspagewidget_filters_widget.SetLayout(serverspagewidget_filters_layout) + serverspagewidget_filters_layout.SetContentsMargins(4, 4, 4, 4) + + // Filters itself. + + // Hide offline servers checkbox. + m.all_servers_hide_offline = widgets.NewQCheckBox(nil) + m.all_servers_hide_offline.SetText("Hide offline servers") + m.all_servers_hide_offline.SetSizePolicy(filters_size_policy) + serverspagewidget_filters_layout.AddWidget(m.all_servers_hide_offline, 0, core.Qt__AlignTop) + // Restore value of hide offline servers checkbox. + // Set to checked for new installations. + all_servers_hide_offline_cb_val, ok := ctx.Cfg.Cfg["/serverslist/all_servers/hide_offline"] + if !ok { + m.all_servers_hide_offline.SetCheckState(2) + } else { + if all_servers_hide_offline_cb_val == "1" { + m.all_servers_hide_offline.SetCheckState(2) + } + } + + // Hide private servers. + m.all_servers_hide_private = widgets.NewQCheckBox(nil) + m.all_servers_hide_private.SetText("Hide private servers") + m.all_servers_hide_private.SetSizePolicy(filters_size_policy) + serverspagewidget_filters_layout.AddWidget(m.all_servers_hide_private, 0, core.Qt__AlignTop) + // Restore checkbox value. + all_servers_hide_private_cb_val, ok := ctx.Cfg.Cfg["/serverslist/all_servers/hide_private"] + if !ok { + m.all_servers_hide_private.SetCheckState(2) + } else { + if all_servers_hide_private_cb_val == "1" { + m.all_servers_hide_private.SetCheckState(2) + } + } + + // Game version. + m.all_servers_version = widgets.NewQComboBox(nil) + m.all_servers_version.SetSizePolicy(filters_size_policy) + serverspagewidget_filters_layout.AddWidget(m.all_servers_version, 0, core.Qt__AlignTop) + // Fill game version combobox with supported versions. + m.all_servers_version.AddItems(common.SUPPORTED_URT_VERSIONS) + + // Game mode. + m.all_servers_gamemode = widgets.NewQComboBox(nil) + m.all_servers_gamemode.SetSizePolicy(filters_size_policy) + serverspagewidget_filters_layout.AddWidget(m.all_servers_gamemode, 0, core.Qt__AlignTop) + // Fill game mode with supported gamemodes. + // First - create sorted gamemodes keys slice. + gm_keys := make([]int, 0, len(m.gamemodes)) + for k, _ := range m.gamemodes { + ki, _ := strconv.Atoi(k) + gm_keys = append(gm_keys, ki) + } + sort.Ints(gm_keys) + // Create a strings slice with gamemodes, using sorted keys. + gmodes := make([]string, 0, len(m.gamemodes)) + // Add "All gamemodes" as first gamemode :) + gmodes = append(gmodes, "All gamemodes") + for i := range gm_keys { + ks := strconv.Itoa(gm_keys[i]) + gmodes = append(gmodes, m.gamemodes[ks]) + } + m.all_servers_gamemode.AddItems(gmodes) + + // After creating filters - add spacer to move them on top of widget. + all_servers_filters_spacer := widgets.NewQSpacerItem(6, 6, widgets.QSizePolicy__Minimum, widgets.QSizePolicy__Expanding) + serverspagewidget_filters_layout.AddSpacerItem(all_servers_filters_spacer) + + ////////////////////////////////////////////////// + // Favorites page. + ////////////////////////////////////////////////// + favoritespagewidget := widgets.NewQWidget(nil, core.Qt__Widget) + favoritespagewidget_policy := widgets.NewQSizePolicy2(widgets.QSizePolicy__Expanding, widgets.QSizePolicy__Expanding, widgets.QSizePolicy__DefaultType) + favoritespagewidget.SetSizePolicy(favoritespagewidget_policy) + m.tabs.AddTab(favoritespagewidget, "Favorites") + favoritespagewidget_layout := widgets.NewQHBoxLayout() + favoritespagewidget_layout.SetContentsMargins(4, 4, 4, 4) + favoritespagewidget.SetLayout(favoritespagewidget_layout) + + // Favorites list. + m.fav_servers = widgets.NewQTreeView(nil) + favoritespagewidget_layout.AddWidget(m.fav_servers, 0, core.Qt__AlignHCenter & core.Qt__AlignTop) + + // Favorites list filters widget. + favoritespagewidget_filters_widget := widgets.NewQWidget(nil, core.Qt__Widget) + favoritespagewidget_filters_widget_policy := widgets.NewQSizePolicy2(widgets.QSizePolicy__Minimum, widgets.QSizePolicy__Minimum, widgets.QSizePolicy__DefaultType) + favoritespagewidget_filters_widget.SetSizePolicy(favoritespagewidget_filters_widget_policy) + favoritespagewidget_layout.AddWidget(favoritespagewidget_filters_widget, 0, core.Qt__AlignRight & core.Qt__AlignTop) + + // Favorites list filters layout. + favoritespagewidget_filters_layout := widgets.NewQVBoxLayout() + favoritespagewidget_filters_widget.SetLayout(favoritespagewidget_filters_layout) + favoritespagewidget_filters_layout.SetContentsMargins(4, 4, 4, 4) + + // Filters itself. + + // Hide offline servers checkbox. + m.fav_servers_hide_offline = widgets.NewQCheckBox(nil) + m.fav_servers_hide_offline.SetText("Hide offline servers") + m.fav_servers_hide_offline.SetSizePolicy(filters_size_policy) + favoritespagewidget_filters_layout.AddWidget(m.fav_servers_hide_offline, 0, core.Qt__AlignTop) + // Restore it's value. + favorite_servers_hide_offline_cb_val, ok := ctx.Cfg.Cfg["/serverslist/favorite/hide_offline"] + if !ok { + m.fav_servers_hide_offline.SetCheckState(2) + } else { + if favorite_servers_hide_offline_cb_val == "1" { + m.fav_servers_hide_offline.SetCheckState(2) + } + } + + // Hide private servers. + m.fav_servers_hide_private = widgets.NewQCheckBox(nil) + m.fav_servers_hide_private.SetText("Hide private servers") + m.fav_servers_hide_private.SetSizePolicy(filters_size_policy) + favoritespagewidget_filters_layout.AddWidget(m.fav_servers_hide_private, 0, core.Qt__AlignTop) + fav_servers_hide_private_cb_val, ok := ctx.Cfg.Cfg["/serverslist/favorite/hide_private"] + if !ok { + m.fav_servers_hide_private.SetCheckState(2) + } else { + if fav_servers_hide_private_cb_val == "1" { + m.fav_servers_hide_private.SetCheckState(2) + } + } + + // Game version. + m.fav_servers_version = widgets.NewQComboBox(nil) + m.fav_servers_version.SetSizePolicy(filters_size_policy) + favoritespagewidget_filters_layout.AddWidget(m.fav_servers_version, 0, core.Qt__AlignTop) + // Fill game version combobox with supported versions. + m.fav_servers_version.AddItems(common.SUPPORTED_URT_VERSIONS) + + // Game mode. + m.fav_servers_gamemode = widgets.NewQComboBox(nil) + m.fav_servers_gamemode.SetSizePolicy(filters_size_policy) + favoritespagewidget_filters_layout.AddWidget(m.fav_servers_gamemode, 0, core.Qt__AlignTop) + // Fill game mode with supported gamemodes. + // As we have previously created this list - reuse it. + m.fav_servers_gamemode.AddItems(gmodes) + + // After creating filters - add spacer to move them on top of widget. + fav_servers_filters_spacer := widgets.NewQSpacerItem(6, 6, widgets.QSizePolicy__Minimum, widgets.QSizePolicy__Expanding) + favoritespagewidget_filters_layout.AddSpacerItem(fav_servers_filters_spacer) +} + +func (m *MainWindow) initializeToolbar() { + m.toolbar = widgets.NewQToolBar("Main Toolbar", m.window) + m.window.AddToolBar(core.Qt__TopToolBarArea, m.toolbar) +} diff --git a/ui/qt5/mainwindow_init_menu_mac.go b/ui/qt5/mainwindow_init_menu_mac.go new file mode 100644 index 0000000..7091c9b --- /dev/null +++ b/ui/qt5/mainwindow_init_menu_mac.go @@ -0,0 +1,72 @@ +// 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. + +// build darwin +package ui + +import ( + // stdlib + //"fmt" + //"os" + //"runtime" + + // Qt5 + "github.com/therecipe/qt/widgets" +) + +func (m *MainWindow) initializeMenu() { + m.mainmenu = widgets.NewQMenuBar(nil) + + ////////////////////////////////////////////////// + // File menu. + ////////////////////////////////////////////////// + filemenu := widgets.NewQMenu2("&File", nil) + + // Options action. + file_options := filemenu.AddAction("&Options") + file_options.SetMenuRole(widgets.QAction__PreferencesRole) + file_options.ConnectTriggered(m.showOptionsDialog) + + // Separator :) + filemenu.AddSeparator() + + // Exit URTrator. + file_exit := filemenu.AddAction("&Exit") + file_exit.SetMenuRole(widgets.QAction__QuitRole) + file_exit.ConnectTriggered(m.close) + + m.mainmenu.AddMenu(filemenu) + ////////////////////////////////////////////////// + // About menu + ////////////////////////////////////////////////// + aboutmenu := widgets.NewQMenu2("&Help", nil) + + // About URTrator. + about_about := aboutmenu.AddAction("&About URTrator...") + about_about.SetMenuRole(widgets.QAction__AboutRole) + about_about.ConnectTriggered(m.showAboutDialog) + + // About Qt. + about_about_qt := aboutmenu.AddAction("About &Qt...") + about_about_qt.SetMenuRole(widgets.QAction__AboutQtRole) + about_about_qt.ConnectTriggered(m.showAboutQtDialog) + + // Separator :) + aboutmenu.AddSeparator() + + // Drop database data. + about_drop_database := aboutmenu.AddAction("&Drop database...") + //about_drop_database.SetMenuRole(widgets.QAction__ApplicationSpecificRole) + about_drop_database.ConnectTriggered(m.dropDatabasesData) + + m.mainmenu.AddMenu(aboutmenu) + + m.window.SetMenuBar(m.mainmenu) +}