/* Copyright (C) 2011 monte This file is part of PSP NetParty. PSP NetParty is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package pspnetparty.client.swt; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MenuDetectEvent; import org.eclipse.swt.events.MenuDetectListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.ShellEvent; import org.eclipse.swt.events.ShellListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Text; import pspnetparty.client.swt.PlayClient.PortalQuery; import pspnetparty.client.swt.config.IniAppData; import pspnetparty.client.swt.config.IniSettings; import pspnetparty.client.swt.config.IniUserProfile; import pspnetparty.client.swt.config.UserProfilePage; import pspnetparty.client.swt.message.AdminNotify; import pspnetparty.client.swt.message.Chat; import pspnetparty.client.swt.message.ErrorLog; import pspnetparty.client.swt.message.InfoLog; import pspnetparty.client.swt.message.LobbyCircleChat; import pspnetparty.client.swt.message.LogViewer; import pspnetparty.client.swt.message.PrivateChat; import pspnetparty.client.swt.message.ServerLog; import pspnetparty.lib.LobbyUser; import pspnetparty.lib.LobbyUserState; import pspnetparty.lib.Utility; import pspnetparty.lib.constants.AppConstants; import pspnetparty.lib.constants.ProtocolConstants; import pspnetparty.lib.engine.PlayRoom; import pspnetparty.lib.socket.IProtocol; import pspnetparty.lib.socket.IProtocolDriver; import pspnetparty.lib.socket.IProtocolMessageHandler; import pspnetparty.lib.socket.ISocketConnection; import pspnetparty.lib.socket.TextProtocolDriver; public class ArenaWindow implements IAppWindow { enum RoomListSessionState { OFFLINE, CONNECTING, LOGIN, } enum LobbySessionState { OFFLINE, CONNECTING, LOGIN, } private static final String[] LOBBY_USER_STATES = new String[] { "参加中", "離席中", "プレイ中", "非アクティブ" }; private final long lobbyInactivityInterval = 30 * 60 * 1000; private boolean isActiveWindow; private RoomListProtocol roomListProtocol = new RoomListProtocol(); private ISocketConnection roomListConnection = ISocketConnection.NULL; private HashMap<String, Set<PlayRoom>> roomListServerMap = new HashMap<String, Set<PlayRoom>>(); private HashMap<String, PlayRoom> roomListMap = new HashMap<String, PlayRoom>(); private RoomListSessionState roomListSession = RoomListSessionState.OFFLINE; private LobbySessionState lobbySession = LobbySessionState.OFFLINE; private LobbyUserState lobbyUserState = LobbyUserState.OFFLINE; private LobbyProtocol lobbyProtocol = new LobbyProtocol(); private ISocketConnection lobbyConnection = ISocketConnection.NULL; private LobbyUser myself = new LobbyUser(null, LobbyUserState.OFFLINE); private long lobbyLastActivity = 0L; private HashMap<String, LobbyUser> allLobbyUsers = new HashMap<String, LobbyUser>(); private PlayClient application; private Shell shell; private UserProfileWindow profileWindow; private SashForm mainSash; private Composite toolBarContainer; private Button roomListServerLoginButton; private Composite roomSearchFormControlContainer; private Button roomSearchFormHasPassword; private Button roomSearchFormOnlyVacant; private Button roomSearchOpenRoomWindow; private Label roomListServerAddressLabel; private Label roomListServerStatusLabel; private Label roomSearchResultLabel; private Combo roomSearchFormTitleCombo; private Combo roomSearchFormTitleNgCombo; private Combo roomSearchFormMasterNameCombo; private Combo roomSearchFormMasterNameNgCombo; private Button roomSearchFormClear; private Button roomSearchFormFilter; private TableViewer roomListTableViewer; private RoomMasterFilter roomMasterFilter = new RoomMasterFilter(true); private RoomMasterFilter roomMasterNgFilter = new RoomMasterFilter(false); private RoomTitleFilter roomTitleFilter = new RoomTitleFilter(true); private RoomTitleFilter roomTitleNgFilter = new RoomTitleFilter(false); private RoomPasswordFilter roomPasswordFilter = new RoomPasswordFilter(); private RoomVacantFilter roomVacantFilter = new RoomVacantFilter(); private ComboHistoryManager roomTitleHistoryManager; private ComboHistoryManager roomTitleNgHistoryManager; private ComboHistoryManager roomMasterNameHistoryManager; private ComboHistoryManager roomMasterNameNgHistoryManager; private Button lobbyServerLoginButton; private Label lobbyServerAddressLabel; private Label lobbyUserCountLabel; private Label lobbyUserNameLabel; private Combo lobbyUserStateCombo; private Button lobbyProfileEdit; private Button lobbyCircleAdd; private TabFolder lobbyCircleTabFolder; private LobbyCircleTab lobbyTab; private LobbyCircleTab pmTab; private TabItem systemLogTab; private Map<String, LobbyCircleTab> myCircleTabs = new LinkedHashMap<String, LobbyCircleTab>(); private Map<String, Map<String, LobbyUser>> circleMap = new HashMap<String, Map<String, LobbyUser>>(); private Menu menuLobbyTab; private MenuItem menuItemAddCircle; private Menu menuCircleTab; private MenuItem menuItemCloseCircle; private Text systemLogText; public ArenaWindow(final PlayClient application) { this.application = application; ImageRegistry imageRegistry = application.getImageRegistry(); // ColorRegistry colorRegistry = application.getColorRegistry(); shell = new Shell(SWT.SHELL_TRIM); shell.setText("アリーナ - " + AppConstants.APP_NAME); try { shell.setImages(application.getShellImages()); } catch (RuntimeException e) { e.printStackTrace(); } GridLayout gridLayout; GridData gridData; gridLayout = new GridLayout(1, false); gridLayout.horizontalSpacing = 0; gridLayout.verticalSpacing = 0; gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; shell.setLayout(gridLayout); toolBarContainer = new Composite(shell, SWT.NONE); toolBarContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); toolBarContainer.setLayout(new FillLayout()); application.createToolBar(toolBarContainer, this); mainSash = new SashForm(shell, SWT.VERTICAL); mainSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Composite roomListContainer = new Composite(mainSash, SWT.NONE); gridLayout = new GridLayout(1, false); gridLayout.horizontalSpacing = 0; gridLayout.verticalSpacing = 3; gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; gridLayout.marginTop = 3; roomListContainer.setLayout(gridLayout); Composite roomSearchFormContainer = new Composite(roomListContainer, SWT.NONE); gridLayout = new GridLayout(5, false); gridLayout.horizontalSpacing = 3; gridLayout.verticalSpacing = 3; gridLayout.marginWidth = 1; gridLayout.marginHeight = 1; roomSearchFormContainer.setLayout(gridLayout); roomSearchFormContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); roomSearchFormControlContainer = new Composite(roomSearchFormContainer, SWT.NONE); roomSearchFormControlContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 5, 1)); gridLayout = new GridLayout(6, false); gridLayout.horizontalSpacing = 6; gridLayout.verticalSpacing = 0; gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; roomSearchFormControlContainer.setLayout(gridLayout); roomListServerLoginButton = new Button(roomSearchFormControlContainer, SWT.TOGGLE); gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false); roomListServerLoginButton.setLayoutData(gridData); application.initControl(roomListServerLoginButton); Composite roomListStatusBarContainer = new Composite(roomSearchFormControlContainer, SWT.NONE); roomListStatusBarContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); gridLayout = new GridLayout(6, false); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; roomListStatusBarContainer.setLayout(gridLayout); roomListServerAddressLabel = new Label(roomListStatusBarContainer, SWT.NONE); roomListServerAddressLabel.setText("サーバー: "); gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false); gridData.heightHint = 15; new Label(roomListStatusBarContainer, SWT.SEPARATOR | SWT.VERTICAL).setLayoutData(gridData); roomListServerStatusLabel = new Label(roomListStatusBarContainer, SWT.NONE); roomListServerStatusLabel.setText("ログインしていません"); new Label(roomListStatusBarContainer, SWT.SEPARATOR | SWT.VERTICAL).setLayoutData(gridData); roomSearchResultLabel = new Label(roomListStatusBarContainer, SWT.NONE); roomSearchResultLabel.setText("検索結果: なし"); roomSearchResultLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false)); Label roomSearchServerAddressLabel = new Label(roomSearchFormControlContainer, SWT.NONE); roomSearchServerAddressLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); application.initControl(roomSearchServerAddressLabel); roomSearchFormHasPassword = new Button(roomSearchFormControlContainer, SWT.CHECK | SWT.FLAT); roomSearchFormHasPassword.setText("鍵付き"); application.initControl(roomSearchFormHasPassword); roomSearchFormOnlyVacant = new Button(roomSearchFormControlContainer, SWT.CHECK | SWT.FLAT); roomSearchFormOnlyVacant.setText("満室非表示"); roomSearchFormOnlyVacant.setSelection(true); application.initControl(roomSearchFormOnlyVacant); roomSearchOpenRoomWindow = new Button(roomSearchFormControlContainer, SWT.PUSH); roomSearchOpenRoomWindow.setText("部屋を作成する"); application.initControl(roomSearchOpenRoomWindow); Label roomSearchFormMasterNameLabel = new Label(roomSearchFormContainer, SWT.NONE); roomSearchFormMasterNameLabel.setText("部屋主"); gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false); gridData.horizontalIndent = 4; roomSearchFormMasterNameLabel.setLayoutData(gridData); application.initControl(roomSearchFormMasterNameLabel); roomSearchFormMasterNameCombo = new Combo(roomSearchFormContainer, SWT.BORDER); roomSearchFormMasterNameCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); application.initControl(roomSearchFormMasterNameCombo); Label roomSeaerchFormMasterNameNgLabel = new Label(roomSearchFormContainer, SWT.NONE); roomSeaerchFormMasterNameNgLabel.setText("除外"); roomSeaerchFormMasterNameNgLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); application.initControl(roomSeaerchFormMasterNameNgLabel); roomSearchFormMasterNameNgCombo = new Combo(roomSearchFormContainer, SWT.NONE); roomSearchFormMasterNameNgCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); application.initControl(roomSearchFormMasterNameNgCombo); roomSearchFormClear = new Button(roomSearchFormContainer, SWT.PUSH); roomSearchFormClear.setText("クリア"); roomSearchFormClear.setEnabled(false); roomSearchFormClear.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); Label roomSearchFormTitleLabel = new Label(roomSearchFormContainer, SWT.NONE); roomSearchFormTitleLabel.setText("部屋名"); gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false); gridData.horizontalIndent = 4; roomSearchFormTitleLabel.setLayoutData(gridData); application.initControl(roomSearchFormTitleLabel); roomSearchFormTitleCombo = new Combo(roomSearchFormContainer, SWT.BORDER); roomSearchFormTitleCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); application.initControl(roomSearchFormTitleCombo); Label roomSearchFormTitleNgLabel = new Label(roomSearchFormContainer, SWT.NONE); roomSearchFormTitleNgLabel.setText("除外"); roomSearchFormTitleNgLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); application.initControl(roomSearchFormTitleNgLabel); roomSearchFormTitleNgCombo = new Combo(roomSearchFormContainer, SWT.NONE); roomSearchFormTitleNgCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); application.initControl(roomSearchFormTitleNgCombo); roomSearchFormFilter = new Button(roomSearchFormContainer, SWT.TOGGLE); roomSearchFormFilter.setText("絞り込む"); roomSearchFormFilter.setEnabled(false); roomSearchFormFilter.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); roomListTableViewer = new TableViewer(roomListContainer, SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION); Table roomListTable = roomListTableViewer.getTable(); roomListTable.setHeaderVisible(true); roomListTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); application.initControl(roomListTable); TableColumn columnMasterName = new TableColumn(roomListTable, SWT.LEFT); columnMasterName.setText("部屋主"); SwtUtils.installSorter(roomListTableViewer, columnMasterName, PlayRoomUtils.MASTER_NAME_SORTER); TableColumn columnTitle = new TableColumn(roomListTable, SWT.LEFT); columnTitle.setText("部屋名"); SwtUtils.installSorter(roomListTableViewer, columnTitle, PlayRoomUtils.TITLE_SORTER); TableColumn columnCapacity = new TableColumn(roomListTable, SWT.CENTER); columnCapacity.setText("定員"); SwtUtils.installSorter(roomListTableViewer, columnCapacity, PlayRoomUtils.CAPACITY_SORTER); TableColumn columnDescription = new TableColumn(roomListTable, SWT.LEFT); columnDescription.setText("部屋の紹介"); TableColumn columnRoomServer = new TableColumn(roomListTable, SWT.LEFT); columnRoomServer.setText("ルームサーバー"); SwtUtils.installSorter(roomListTableViewer, columnRoomServer, PlayRoomUtils.ADDRESS_SORTER); TableColumn columnTimestamp = new TableColumn(roomListTable, SWT.CENTER); columnTimestamp.setText("作成日時"); SwtUtils.installSorter(roomListTableViewer, columnTimestamp, PlayRoomUtils.TIMESTAMP_SORTER); SwtUtils.enableColumnDrag(roomListTable); roomListTableViewer.setContentProvider(PlayRoomUtils.CONTENT_PROVIDER); roomListTableViewer.setLabelProvider(PlayRoomUtils.LABEL_PROVIDER); roomListTableViewer.setInput(roomListMap); roomListTableViewer.addFilter(roomMasterFilter); roomListTableViewer.addFilter(roomMasterNgFilter); roomListTableViewer.addFilter(roomTitleFilter); roomListTableViewer.addFilter(roomTitleNgFilter); roomListTableViewer.addFilter(roomPasswordFilter); roomListTableViewer.addFilter(roomVacantFilter); Composite lobbyContainer = new Composite(mainSash, SWT.NONE); gridLayout = new GridLayout(1, false); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; gridLayout.marginBottom = 2; gridLayout.verticalSpacing = 6; lobbyContainer.setLayout(gridLayout); Composite lobbyControlContainer = new Composite(lobbyContainer, SWT.NONE); lobbyControlContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); gridLayout = new GridLayout(8, false); gridLayout.marginWidth = 1; gridLayout.marginHeight = 0; gridLayout.marginLeft = 1; gridLayout.marginTop = 3; lobbyControlContainer.setLayout(gridLayout); lobbyServerLoginButton = new Button(lobbyControlContainer, SWT.TOGGLE); lobbyServerLoginButton.setText("ロビーログイン"); gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false); lobbyServerLoginButton.setLayoutData(gridData); application.initControl(lobbyServerLoginButton); lobbyServerAddressLabel = new Label(lobbyControlContainer, SWT.NONE); lobbyServerAddressLabel.setText("サーバー: "); application.initControl(lobbyServerAddressLabel); gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false); gridData.heightHint = 15; new Label(lobbyControlContainer, SWT.SEPARATOR | SWT.VERTICAL).setLayoutData(gridData); lobbyUserCountLabel = new Label(lobbyControlContainer, SWT.NONE); lobbyUserCountLabel.setText("ユーザー数: "); application.initControl(lobbyUserCountLabel); lobbyUserNameLabel = new Label(lobbyControlContainer, SWT.NONE); lobbyUserNameLabel.setText("ユーザー名"); lobbyUserNameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false)); application.initControl(lobbyUserNameLabel); lobbyUserStateCombo = new Combo(lobbyControlContainer, SWT.BORDER | SWT.READ_ONLY); lobbyUserStateCombo.setItems(LOBBY_USER_STATES); lobbyUserStateCombo.setEnabled(false); application.initControl(lobbyUserStateCombo); lobbyProfileEdit = new Button(lobbyControlContainer, SWT.PUSH); lobbyProfileEdit.setText("プロフィール編集"); application.initControl(lobbyProfileEdit); lobbyCircleAdd = new Button(lobbyControlContainer, SWT.PUSH); lobbyCircleAdd.setText("サークル追加 / 一覧"); application.initControl(lobbyCircleAdd); lobbyCircleTabFolder = new TabFolder(lobbyContainer, SWT.TOP); lobbyCircleTabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); Image lobbyIcon = imageRegistry.get(PlayClient.ICON_TAB_LOBBY); Image lobbyIconNotify = imageRegistry.get(PlayClient.ICON_TAB_LOBBY_NOTIFY); lobbyTab = new LobbyCircleTab("ロビー全体", allLobbyUsers, lobbyIcon, lobbyIconNotify, true); lobbyTab.tabItem.setToolTipText("ログインしている全てのユーザー"); systemLogTab = new TabItem(lobbyCircleTabFolder, SWT.NONE); systemLogTab.setText("ログ"); systemLogTab.setToolTipText("システムログ"); systemLogTab.setImage(imageRegistry.get(PlayClient.ICON_TAB_LOG)); Composite logContainer = new Composite(lobbyCircleTabFolder, SWT.NONE); logContainer.setLayout(new FillLayout()); systemLogTab.setControl(logContainer); systemLogText = new Text(logContainer, SWT.MULTI | SWT.READ_ONLY | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); application.initControl(systemLogText); Image pmIcon = imageRegistry.get(PlayClient.ICON_TAB_PM); Image pmIconNotify = imageRegistry.get(PlayClient.ICON_TAB_PM_NOTIFY); pmTab = new LobbyCircleTab("PM", allLobbyUsers, pmIcon, pmIconNotify, false); pmTab.tabItem.setToolTipText("プライベートメッセージ"); initWidgetListeners(); updateRoomListServerLoginButton(false); String[] stringList; IniAppData appData = application.getAppData(); stringList = appData.getSearchHistoryRoomMaster(); roomMasterNameHistoryManager = new ComboHistoryManager(roomSearchFormMasterNameCombo, stringList, 20, false); stringList = appData.getSearchHistoryRoomMasterNG(); roomMasterNameNgHistoryManager = new ComboHistoryManager(roomSearchFormMasterNameNgCombo, stringList, 20, false); stringList = appData.getSearchHistoryTitle(); roomTitleHistoryManager = new ComboHistoryManager(roomSearchFormTitleCombo, stringList, 20, false); stringList = appData.getSearchHistoryTitleNG(); roomTitleNgHistoryManager = new ComboHistoryManager(roomSearchFormTitleNgCombo, stringList, 20, false); mainSash.setWeights(appData.getArenaSashWeights()); appData.restoreSearchRoomTable(roomListTable); appData.restoreArenaWindow(shell); for (String circle : application.getUserProfile().getCircles()) { requestCircleJoin(circle); } lobbyCircleTabFolder.setSelection(lobbyTab.tabItem); } private void initWidgetListeners() { roomListServerLoginButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { if (roomListConnection.isConnected()) { roomListConnection.disconnect(); } else { connectToRoomListServer(); } } }); roomListServerLoginButton.addMouseListener(new MouseListener() { @Override public void mouseUp(MouseEvent e) { if (roomListSession != RoomListSessionState.OFFLINE) return; if (e.button != 3) return; if (e.x < 0 || e.y < 0) return; Point size = roomListServerLoginButton.getSize(); if (e.x > size.x || e.y > size.y) return; showRoomListServerSelectDialog(); } @Override public void mouseDown(MouseEvent e) { } @Override public void mouseDoubleClick(MouseEvent e) { } }); roomSearchOpenRoomWindow.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { application.getRoomWindow().show(); } }); roomListTableViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent e) { IStructuredSelection sel = (IStructuredSelection) e.getSelection(); PlayRoom room = (PlayRoom) sel.getFirstElement(); if (room == null) return; ArenaWindow.this.application.getRoomWindow().autoConnectAsParticipant(room); } }); SelectionListener formSelectionListener = new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { Combo combo = (Combo) e.widget; String text = combo.getText(); combo.setSelection(new Point(text.length(), text.length())); } @Override public void widgetDefaultSelected(SelectionEvent e) { } }; roomSearchFormMasterNameCombo.addSelectionListener(formSelectionListener); roomSearchFormMasterNameNgCombo.addSelectionListener(formSelectionListener); roomSearchFormTitleCombo.addSelectionListener(formSelectionListener); roomSearchFormTitleNgCombo.addSelectionListener(formSelectionListener); KeyListener roomSearchFormKeyListerner = new KeyListener() { @Override public void keyReleased(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { if (!roomSearchFormFilter.getEnabled()) return; switch (e.character) { case SWT.CR: case SWT.LF: e.doit = false; roomSearchFormFilter.setSelection(true); applyRoomSearchFilter(); break; default: roomSearchFormFilter.setSelection(false); } } }; roomSearchFormMasterNameCombo.addKeyListener(roomSearchFormKeyListerner); roomSearchFormMasterNameNgCombo.addKeyListener(roomSearchFormKeyListerner); roomSearchFormTitleCombo.addKeyListener(roomSearchFormKeyListerner); roomSearchFormTitleNgCombo.addKeyListener(roomSearchFormKeyListerner); roomSearchFormTitleCombo.addVerifyListener(SwtUtils.NOT_ACCEPT_CONTROL_CHAR_LISTENER); roomSearchFormMasterNameCombo.addVerifyListener(SwtUtils.NOT_ACCEPT_CONTROL_CHAR_LISTENER); roomSearchFormHasPassword.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { roomPasswordFilter.hasPassword = roomSearchFormHasPassword.getSelection(); roomListTableViewer.refresh(); updateRoomSearchResult(); } }); roomSearchFormOnlyVacant.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { roomVacantFilter.onlyVacant = roomSearchFormOnlyVacant.getSelection(); roomListTableViewer.refresh(); updateRoomSearchResult(); } }); roomSearchFormFilter.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { applyRoomSearchFilter(); } }); roomSearchFormClear.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { roomSearchFormMasterNameCombo.setText(""); roomSearchFormMasterNameNgCombo.setText(""); roomSearchFormTitleCombo.setText(""); roomSearchFormTitleNgCombo.setText(""); roomSearchFormFilter.setSelection(false); applyRoomSearchFilter(); } }); lobbyServerLoginButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { if (lobbyConnection.isConnected()) { lobbyConnection.disconnect(); } else { connectToLobbyServer(); } } }); lobbyServerLoginButton.addMouseListener(new MouseListener() { @Override public void mouseUp(MouseEvent e) { if (lobbySession != LobbySessionState.OFFLINE) return; if (e.button != 3) return; if (e.x < 0 || e.y < 0) return; Point size = lobbyServerLoginButton.getSize(); if (e.x > size.x || e.y > size.y) return; selectLobbyServer(); } @Override public void mouseDown(MouseEvent e) { } @Override public void mouseDoubleClick(MouseEvent e) { } }); lobbyUserStateCombo.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { sendLobbyState(); lobbyTab.chatText.setFocus(); } }); lobbyProfileEdit.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { if (application.openConfigDialog(shell, UserProfilePage.PAGE_ID) && lobbySession == LobbySessionState.LOGIN) { IniUserProfile profile = application.getUserProfile(); StringBuilder sb = new StringBuilder(); sb.append(ProtocolConstants.Lobby.COMMAND_UPDATE_PROFILE); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(profile.getUrl()); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(profile.getIconUrl()); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(profile.getProfile()); lobbyConnection.send(Utility.encode(sb)); } shell.setFocus(); } }); lobbyCircleAdd.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { openAddCircleDialog(); } }); menuLobbyTab = new Menu(shell, SWT.POP_UP); menuItemAddCircle = new MenuItem(menuLobbyTab, SWT.PUSH); menuItemAddCircle.setText("サークル追加"); menuItemAddCircle.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { openAddCircleDialog(); } }); menuCircleTab = new Menu(shell, SWT.POP_UP); menuItemCloseCircle = new MenuItem(menuCircleTab, SWT.PUSH); menuItemCloseCircle.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event e) { String circle = (String) menuItemCloseCircle.getData(); if (Utility.isEmpty(circle)) return; requestCircleLeave(circle); } }); lobbyCircleTabFolder.addMenuDetectListener(new MenuDetectListener() { @Override public void menuDetected(MenuDetectEvent e) { TabItem item = lobbyCircleTabFolder.getItem(lobbyCircleTabFolder.toControl(new Point(e.x, e.y))); if (item == null || lobbyCircleTabFolder.indexOf(item) < 3) { lobbyCircleTabFolder.setMenu(menuLobbyTab); } else { lobbyCircleTabFolder.setMenu(menuCircleTab); menuItemCloseCircle.setText("[" + item.getText() + "] を閉じる"); menuItemCloseCircle.setData(item.getText()); } } }); lobbyCircleTabFolder.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event e) { int index = lobbyCircleTabFolder.getSelectionIndex(); LobbyCircleTab tab; switch (index) { case 0: tab = lobbyTab; break; case 1: Image icon = application.getImageRegistry().get(PlayClient.ICON_TAB_LOG); systemLogTab.setImage(icon); return; case 2: tab = pmTab; break; default: TabItem item = lobbyCircleTabFolder.getItem(index); tab = myCircleTabs.get(item.getText()); } tab.tabItem.setImage(tab.icon); if (tab.chatText != null) tab.chatText.setFocus(); } }); shell.addShellListener(new ShellListener() { @Override public void shellClosed(ShellEvent e) { } @Override public void shellIconified(ShellEvent e) { isActiveWindow = false; } @Override public void shellDeiconified(ShellEvent e) { isActiveWindow = true; } @Override public void shellDeactivated(ShellEvent e) { isActiveWindow = false; } @Override public void shellActivated(ShellEvent e) { isActiveWindow = true; } }); shell.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (roomListConnection.isConnected()) roomListConnection.disconnect(); if (lobbyConnection.isConnected()) lobbyConnection.disconnect(); storeAppData(); } }); } @Override public Type getType() { return Type.ARENA; } @Override public void settingChanged() { } private void storeAppData() { IniAppData appData = application.getAppData(); appData.storeArenaWindow(shell.getBounds()); appData.setArenaSashWeights(mainSash.getWeights()); appData.storeSearchRoomTable(roomListTableViewer.getTable()); appData.setArenaLobbySashFormWeights(lobbyTab.mainSash.getWeights()); appData.storeLobbyUserTable(lobbyTab.userListTableViewer.getTable()); appData.setSearchHistoryRoomMaster(roomMasterNameHistoryManager.makeCSV()); appData.setSearchHistoryRoomMasterNG(roomMasterNameNgHistoryManager.makeCSV()); appData.setSearchHistoryTitle(roomTitleHistoryManager.makeCSV()); appData.setSearchHistoryTitleNG(roomTitleNgHistoryManager.makeCSV()); application.getUserProfile().setCircles(myCircleTabs.keySet()); } public void reflectAppearance() { lobbyTab.reflectAppearance(); pmTab.reflectAppearance(); for (LobbyCircleTab tab : myCircleTabs.values()) tab.reflectAppearance(); shell.layout(true, true); } public void show() { IniSettings settings = application.getSettings(); if (settings.isArenaAutoLoginRoomList() && !roomListConnection.isConnected()) connectToRoomListServer(); if (settings.isArenaAutoLoginLobby() && !lobbyConnection.isConnected()) connectToLobbyServer(); if (shell.getMinimized()) shell.setMinimized(false); shell.open(); } public void hide() { shell.setVisible(false); } public Shell getShell() { return shell; } public void cronJob() { if (lobbySession != LobbySessionState.LOGIN || lobbyUserState != LobbyUserState.LOGIN) return; long deadline = System.currentTimeMillis() - lobbyInactivityInterval; if (deadline < lobbyLastActivity) return; changeLobbyStateTo(LobbyUserState.INACTIVE); } public void appendToSystemLog(final String message, final boolean timestamp) { if (Utility.isEmpty(message)) return; try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { appendToSystemLog(message, timestamp); } }); return; } if (systemLogText.getCharCount() > 0) systemLogText.append("\n"); if (timestamp) { Date now = new Date(); systemLogText.append(SwtUtils.LOG_DATE_FORMAT.format(now)); systemLogText.append(" - "); } systemLogText.append(message); systemLogText.setTopIndex(systemLogText.getLineCount()); if (systemLogTab == lobbyCircleTabFolder.getItem(lobbyCircleTabFolder.getSelectionIndex())) return; Image icon = application.getImageRegistry().get(PlayClient.ICON_TAB_LOG_NOTIFY); systemLogTab.setImage(icon); } catch (SWTException e) { } } private static abstract class MultipleQueryStringFilter extends ViewerFilter { private String[] values; private boolean pass; private MultipleQueryStringFilter(boolean pass) { this.pass = pass; } public void setValues(String[] values) { if (values != null) for (int i = 0; i < values.length; i++) { values[i] = values[i].toLowerCase(); } this.values = values; } protected boolean filter(String string) { if (values == null) return true; string = string.toLowerCase(); for (String val : values) { if (string.contains(val)) return pass; } return !pass; } } private static class RoomMasterFilter extends MultipleQueryStringFilter { private RoomMasterFilter(boolean pass) { super(pass); } @Override public boolean select(Viewer viewer, Object parent, Object element) { PlayRoom room = (PlayRoom) element; return filter(room.getMasterName()); } } private static class RoomTitleFilter extends MultipleQueryStringFilter { private RoomTitleFilter(boolean pass) { super(pass); } @Override public boolean select(Viewer viewer, Object parent, Object element) { PlayRoom room = (PlayRoom) element; return filter(room.getTitle()); } } private static class RoomPasswordFilter extends ViewerFilter { private boolean hasPassword = false; @Override public boolean select(Viewer viewer, Object parent, Object element) { PlayRoom room = (PlayRoom) element; if (hasPassword) return room.hasPassword(); else return !room.hasPassword(); } } private static class RoomVacantFilter extends ViewerFilter { private boolean onlyVacant = true; @Override public boolean select(Viewer viewer, Object parent, Object element) { PlayRoom room = (PlayRoom) element; if (onlyVacant) return room.getCurrentPlayers() < room.getMaxPlayers(); else return true; } } private void setFilterValues(MultipleQueryStringFilter filter, Combo combo) { String text = combo.getText(); if (Utility.isEmpty(text)) return; text = Utility.validateNameString(text); combo.setText(text); filter.setValues(text.split(" ")); } private void setFilterValues(MultipleQueryStringFilter filter, Text control) { String text = control.getText(); if (Utility.isEmpty(text)) return; text = Utility.validateNameString(text); control.setText(text); filter.setValues(text.split(" ")); } private void applyRoomSearchFilter() { if (roomSearchFormFilter.getSelection()) { setFilterValues(roomMasterFilter, roomSearchFormMasterNameCombo); setFilterValues(roomMasterNgFilter, roomSearchFormMasterNameNgCombo); setFilterValues(roomTitleFilter, roomSearchFormTitleCombo); setFilterValues(roomTitleNgFilter, roomSearchFormTitleNgCombo); } else { roomMasterFilter.setValues(null); roomMasterNgFilter.setValues(null); roomTitleFilter.setValues(null); roomTitleNgFilter.setValues(null); } roomListTableViewer.refresh(); updateRoomSearchResult(); } private void connectToRoomListServer() { InetSocketAddress address = application.getPortalServer(); if (address == null) { appendToSystemLog("サーバーリストが設定されていません", true); return; } roomListServerLoginButton.setEnabled(false); connectToRoomListServer(address); } private void showRoomListServerSelectDialog() { String[] list = application.getServerRegistry().getPortalServers(); if (list.length == 0) { ErrorLog log = new ErrorLog("サーバーリストが設定されていません"); appendToSystemLog(log.getMessage(), true); roomListServerLoginButton.setEnabled(true); } else { SearchServerSelectDialog dialog = new SearchServerSelectDialog(shell, list); switch (dialog.open()) { case IDialogConstants.OK_ID: String selected = dialog.getSelectedServer(); InetSocketAddress address = Utility.parseSocketAddress(selected); if (address == null) { appendToSystemLog("選択されたアドレスにエラーがあります", true); } else { connectToRoomListServer(address); } break; case IDialogConstants.CANCEL_ID: roomListServerLoginButton.setEnabled(true); break; } } } private void connectToRoomListServer(final InetSocketAddress address) { roomListSession = RoomListSessionState.CONNECTING; roomListServerAddressLabel.setText("サーバー: " + Utility.socketAddressToStringByHostName(address)); roomListMap.clear(); roomListTableViewer.refresh(); roomListTableViewer.setSorter(null); roomListTableViewer.getTable().setSortDirection(SWT.NONE); Runnable task = new Runnable() { @Override public void run() { try { application.connectTcp(address, roomListProtocol); } catch (IOException e) { roomListSession = RoomListSessionState.OFFLINE; updateRoomListServerLoginButton(false); appendToSystemLog(e.getMessage(), true); } } }; application.execute(task); } private void updateRoomListServerLoginButton(final boolean loginSuccess) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { updateRoomListServerLoginButton(loginSuccess); } }); return; } roomListServerLoginButton.setText(loginSuccess ? "リスト閲覧中" : "部屋リスト閲覧"); roomListServerLoginButton.setSelection(loginSuccess); roomListServerLoginButton.setEnabled(true); roomSearchFormControlContainer.layout(); roomSearchFormFilter.setEnabled(loginSuccess); roomSearchFormClear.setEnabled(loginSuccess); } catch (SWTException e) { } } private void updateRoomSearchResult() { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { public void run() { updateRoomSearchResult(); } }); return; } String message = "検索結果: " + roomListTableViewer.getTable().getItemCount() + "件"; roomSearchResultLabel.setText(message); roomSearchFormControlContainer.layout(); roomTitleHistoryManager.addCurrentItem(); roomTitleNgHistoryManager.addCurrentItem(); roomMasterNameHistoryManager.addCurrentItem(); roomMasterNameNgHistoryManager.addCurrentItem(); roomListSession = RoomListSessionState.LOGIN; } catch (SWTException e) { } } private void addPlayRoom(final PlayRoom room) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { addPlayRoom(room); } }); return; } String source = room.getSourceServer(); Set<PlayRoom> serverRooms = roomListServerMap.get(source); if (serverRooms == null) { serverRooms = new HashSet<PlayRoom>(); roomListServerMap.put(source, serverRooms); } serverRooms.add(room); roomListMap.put(room.getRoomAddress(), room); roomListTableViewer.add(room); roomListTableViewer.refresh(room); updateRoomSearchResult(); } catch (SWTException e) { } } private void updatePlayRoom(final PlayRoom room) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { updatePlayRoom(room); } }); return; } roomListTableViewer.remove(room); roomListTableViewer.add(room); roomListTableViewer.refresh(room); } catch (SWTException e) { } } private void removePlayRoom(final PlayRoom room) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { removePlayRoom(room); } }); return; } String source = room.getSourceServer(); Set<PlayRoom> serverRooms = roomListServerMap.get(source); if (serverRooms == null) { return; } serverRooms.remove(room); roomListMap.remove(room.getRoomAddress()); roomListTableViewer.remove(room); roomListTableViewer.refresh(room); updateRoomSearchResult(); } catch (SWTException e) { } } private void removeRoomServersAllRooms(final Set<PlayRoom> rooms) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { removeRoomServersAllRooms(rooms); } }); return; } for (PlayRoom r : rooms) { roomListMap.remove(r.getRoomAddress()); roomListTableViewer.remove(r); roomListTableViewer.refresh(r); } rooms.clear(); updateRoomSearchResult(); } catch (SWTException e) { } } private class RoomListProtocol implements IProtocol { @Override public void log(String message) { appendToSystemLog(message, true); } @Override public String getProtocol() { return ProtocolConstants.PROTOCOL_ROOM_LIST; } private void updateLoginButton() { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { updateLoginButton(); } }); return; } updateRoomListServerLoginButton(true); appendToSystemLog("部屋リストの閲覧を開始しました", true); } catch (SWTException e) { } } @Override public IProtocolDriver createDriver(ISocketConnection connection) { roomListConnection = connection; updateLoginButton(); roomListSession = RoomListSessionState.LOGIN; RoomListProtocolDriver driver = new RoomListProtocolDriver(connection); return driver; } } private class RoomListProtocolDriver extends TextProtocolDriver { public RoomListProtocolDriver(ISocketConnection connection) { super(connection, roomListHandlers); } @Override public void log(String message) { appendToSystemLog(message, true); } @Override public void errorProtocolNumber(String number) { String error = String.format("サーバーとのプロトコルナンバーが一致しないので接続できません サーバー:%s クライアント:%s", number, IProtocol.NUMBER); appendToSystemLog(error, true); } @Override public void connectionDisconnected() { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { connectionDisconnected(); } }); return; } switch (roomListSession) { case CONNECTING: appendToSystemLog("部屋リストにアクセスできません", true); break; case LOGIN: appendToSystemLog("部屋リストの閲覧を終了しました", true); break; } roomListConnection = ISocketConnection.NULL; roomListSession = RoomListSessionState.OFFLINE; roomListMap.clear(); roomListTableViewer.refresh(); roomListServerAddressLabel.setText("サーバー: "); roomListServerStatusLabel.setText("ログインしていません"); roomSearchResultLabel.setText("検索結果: なし"); roomSearchFormControlContainer.layout(); updateRoomListServerLoginButton(false); } catch (SWTException e) { } } } private HashMap<String, IProtocolMessageHandler> roomListHandlers = new HashMap<String, IProtocolMessageHandler>(); { roomListHandlers.put(ProtocolConstants.RoomList.NOTIFY_ROOM_SERVER_REMOVED, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String server) { Set<PlayRoom> rooms = roomListServerMap.get(server); if (rooms == null || rooms.isEmpty()) return true; removeRoomServersAllRooms(rooms); return true; } }); roomListHandlers.put(ProtocolConstants.RoomList.NOTIFY_ROOM_CREATED, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { // R hostname:port hostname:port masterName title currentPlayers // maxPlayers hasPassword createdTime description final String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 9) return true; String source = tokens[0]; String server = tokens[1]; if (Utility.isEmpty(server)) server = source; String masterName = tokens[2]; String title = tokens[3]; int currentPlayers = Integer.parseInt(tokens[4]); int maxPlayers = Integer.parseInt(tokens[5]); boolean hasPassword = "Y".equals(tokens[6]); long created = Long.parseLong(tokens[7]); String description = Utility.multiLineToSingleLine(tokens[8]); PlayRoom room = new PlayRoom(source, server, masterName, title, hasPassword, currentPlayers, maxPlayers, created); room.setDescription(description); addPlayRoom(room); return true; } }); roomListHandlers.put(ProtocolConstants.RoomList.NOTIFY_ROOM_UPDATED, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { // U hostname:port:master title maxPlayers hasPassword // description String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 5) return true; try { String address = tokens[0]; String title = tokens[1]; int maxPlayers = Integer.parseInt(tokens[2]); boolean hasPassword = "Y".equals(tokens[3]); String description = Utility.multiLineToSingleLine(tokens[4]); PlayRoom room = roomListMap.get(address); if (room == null) return true; room.setTitle(title); room.setMaxPlayers(maxPlayers); room.setHasPassword(hasPassword); room.setDescription(description); updatePlayRoom(room); } catch (NumberFormatException e) { } return true; } }); roomListHandlers.put(ProtocolConstants.RoomList.NOTIFY_ROOM_DELETED, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { // NRD hostname:port:master String address = argument; PlayRoom room = roomListMap.remove(address); if (room == null) return true; removePlayRoom(room); return true; } }); roomListHandlers.put(ProtocolConstants.RoomList.NOTIFY_ROOM_PLAYER_COUNT_CHANGED, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { // NRPC hostname:port:master playerCount String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 2) return true; String address = tokens[0]; PlayRoom room = roomListMap.get(address); if (room == null) return true; try { int playerCount = Integer.parseInt(tokens[1]); room.setCurrentPlayers(playerCount); updatePlayRoom(room); } catch (NumberFormatException e) { } return true; } }); roomListHandlers.put(ProtocolConstants.SERVER_STATUS, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { try { int currentUsers = Integer.parseInt(argument); updateServerStatus(currentUsers); } catch (NumberFormatException e) { } return true; } private void updateServerStatus(final int currentUsers) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { updateServerStatus(currentUsers); } }); return; } String text = String.format("接続ユーザー数: %d", currentUsers); roomListServerStatusLabel.setText(text); roomSearchFormControlContainer.layout(); } catch (SWTException e) { } } }); roomListHandlers.put(ProtocolConstants.Search.NOTIFY_FROM_ADMIN, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { appendToSystemLog(argument, true); return true; } }); roomListHandlers.put(ProtocolConstants.Search.ERROR_LOGIN_BEYOND_CAPACITY, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { appendToSystemLog("サーバーのログイン上限人数に達したのでログインできません", true); return true; } }); } private Map<String, LobbyUser> getCircleMemberMap(String circle) { Map<String, LobbyUser> members = circleMap.get(circle); if (members == null) { members = new HashMap<String, LobbyUser>(); circleMap.put(circle, members); } return members; } private class LobbyCircleTab { private String circleName; private Image icon; private Image iconNotify; private SashForm mainSash; private TabItem tabItem; private LogViewer infoLogViewer; private LogViewer chatLogViewer; private Text userSearchName; private Text userSearchProfile; private Combo userSearchStateCombo; private Button userSearchClear; private Button userSearchFilter; private TableViewer userListTableViewer; private Text chatText; private Button multilineChatButton; private MenuItem menuViewProfile; private MenuItem menuPrivateChat; private Map<String, LobbyUser> memberMap; private UserNameFilter nameFilter = new UserNameFilter(); private UserProfileFilter profileFilter = new UserProfileFilter(); private UserStateFilter stateFilter = new UserStateFilter(); private LobbyCircleTab(final String tabText, Map<String, LobbyUser> memberMap, Image icon, Image iconNotify, boolean showChatInput) { this.circleName = tabText; this.memberMap = memberMap; this.icon = icon; this.iconNotify = iconNotify; IniAppData appData = application.getAppData(); GridLayout gridLayout; tabItem = new TabItem(lobbyCircleTabFolder, SWT.DEFAULT); tabItem.setImage(icon); tabItem.setText(tabText); Composite contents = new Composite(lobbyCircleTabFolder, SWT.NONE); gridLayout = new GridLayout(1, false); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; gridLayout.marginTop = 2; gridLayout.verticalSpacing = 2; contents.setLayout(gridLayout); mainSash = new SashForm(contents, SWT.HORIZONTAL); mainSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); if (showChatInput) { SashForm logSash = new SashForm(mainSash, SWT.VERTICAL); logSash.setSashWidth(1); infoLogViewer = new LogViewer(logSash, application.getSettings().getMaxLogCount(), application); chatLogViewer = new LogViewer(logSash, application.getSettings().getMaxLogCount(), application); logSash.setWeights(new int[] { 2, 8 }); } else { chatLogViewer = new LogViewer(mainSash, application.getSettings().getMaxLogCount(), application); } Composite userListContainer = new Composite(mainSash, SWT.NONE); gridLayout = new GridLayout(4, false); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; gridLayout.marginTop = 2; gridLayout.marginLeft = 2; userListContainer.setLayout(gridLayout); Label userSearchNameLabel = new Label(userListContainer, SWT.NONE); userSearchNameLabel.setText("ユーザー名"); userSearchNameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); application.initControl(userSearchNameLabel); userSearchName = new Text(userListContainer, SWT.SINGLE | SWT.BORDER); userSearchName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); application.initControl(userSearchName); Label userSearchProfileLabel = new Label(userListContainer, SWT.NONE); userSearchProfileLabel.setText("プロフィール"); userSearchProfileLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); application.initControl(userSearchProfileLabel); userSearchProfile = new Text(userListContainer, SWT.SINGLE | SWT.BORDER); userSearchProfile.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); application.initControl(userSearchProfile); Label userSearchStateLabel = new Label(userListContainer, SWT.NONE); userSearchStateLabel.setText("状態"); userSearchStateLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); application.initControl(userSearchStateLabel); userSearchStateCombo = new Combo(userListContainer, SWT.BORDER | SWT.READ_ONLY); userSearchStateCombo.setItems(LOBBY_USER_STATES); userSearchStateCombo.add("指定なし", 0); userSearchStateCombo.select(0); userSearchStateCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); application.initControl(userSearchStateCombo); userSearchClear = new Button(userListContainer, SWT.PUSH); userSearchClear.setText("クリア"); userSearchClear.setEnabled(false); userSearchClear.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); application.initControl(userSearchClear); userSearchFilter = new Button(userListContainer, SWT.TOGGLE); userSearchFilter.setText("絞り込む"); userSearchFilter.setEnabled(false); userSearchFilter.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); application.initControl(userSearchFilter); userListTableViewer = new TableViewer(userListContainer, SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION); Table userListTable = userListTableViewer.getTable(); TableColumn userSearchTableNameColumn = new TableColumn(userListTable, SWT.LEFT); userSearchTableNameColumn.setText("名前"); SwtUtils.installSorter(userListTableViewer, userSearchTableNameColumn, LobbyUserUtils.NAME_SORTER); TableColumn userSearchTableStateColumn = new TableColumn(userListTable, SWT.LEFT); userSearchTableStateColumn.setText("状態"); SwtUtils.installSorter(userListTableViewer, userSearchTableStateColumn, LobbyUserUtils.STATE_SORTER); TableColumn userSearchTableProfileColumn = new TableColumn(userListTable, SWT.LEFT); userSearchTableProfileColumn.setText("プロフィール"); userListTable.setHeaderVisible(true); userListTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1)); application.initControl(userListTable); userListTableViewer.setLabelProvider(LobbyUserUtils.LABEL_PROVIDER); userListTableViewer.setContentProvider(LobbyUserUtils.CONTENT_PROVIDER); userListTableViewer.setInput(memberMap); SwtUtils.enableColumnDrag(userListTable); userListTableViewer.addFilter(nameFilter); userListTableViewer.addFilter(profileFilter); userListTableViewer.addFilter(stateFilter); ModifyListener modifyListener = new ModifyListener() { @Override public void modifyText(ModifyEvent e) { userSearchFilter.setSelection(false); } }; userSearchName.addModifyListener(modifyListener); userSearchProfile.addModifyListener(modifyListener); userSearchStateCombo.addModifyListener(modifyListener); userSearchFilter.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { filterUserList(); } }); userSearchClear.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { userSearchName.setText(""); userSearchProfile.setText(""); userSearchStateCombo.select(0); userSearchFilter.setSelection(false); filterUserList(); } }); if (showChatInput) { Composite chatContainer = new Composite(contents, SWT.NONE); gridLayout = new GridLayout(2, false); gridLayout.marginHeight = 0; gridLayout.marginWidth = 1; gridLayout.marginBottom = 2; gridLayout.marginRight = 0; chatContainer.setLayout(gridLayout); chatContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); chatText = new Text(chatContainer, SWT.BORDER | SWT.SINGLE); chatText.setTextLimit(300); chatText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); application.initChatControl(chatText); multilineChatButton = new Button(chatContainer, SWT.PUSH); multilineChatButton.setText("複数行"); application.initControl(multilineChatButton); chatText.addKeyListener(new KeyListener() { @Override public void keyReleased(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { switch (e.character) { case SWT.CR: case SWT.LF: e.doit = false; if (sendChat(chatText.getText(), LobbyCircleTab.this)) { chatText.setText(""); } } } }); multilineChatButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { switch (lobbySession) { case LOGIN: MultiLineChatDialog dialog = new MultiLineChatDialog(shell, application); switch (dialog.open()) { case IDialogConstants.OK_ID: String message = dialog.getMessage(); sendChat(message, LobbyCircleTab.this); } } } }); } tabItem.setControl(contents); appData.restoreLobbyUserTable(userListTable); mainSash.setWeights(appData.getArenaLobbySashFormWeights()); Menu lobbyUserMenu = new Menu(shell, SWT.POP_UP); menuViewProfile = new MenuItem(lobbyUserMenu, SWT.PUSH); menuViewProfile.setText("プロフィール表示"); menuViewProfile.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { IStructuredSelection selection = (IStructuredSelection) userListTableViewer.getSelection(); LobbyUser user = (LobbyUser) selection.getFirstElement(); if (user == null) return; showProfileWindow(user); } }); menuPrivateChat = new MenuItem(lobbyUserMenu, SWT.PUSH); menuPrivateChat.setText("プライベートメッセージ"); menuPrivateChat.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { IStructuredSelection selection = (IStructuredSelection) userListTableViewer.getSelection(); LobbyUser user = (LobbyUser) selection.getFirstElement(); String myName = myself.getName(); if (user == null || myName.equals(user.getName())) return; openPrivateMessageDialog(user); } }); userListTable.setMenu(lobbyUserMenu); userListTable.addMenuDetectListener(new MenuDetectListener() { @Override public void menuDetected(MenuDetectEvent e) { IStructuredSelection selection = (IStructuredSelection) userListTableViewer.getSelection(); LobbyUser user = (LobbyUser) selection.getFirstElement(); menuViewProfile.setEnabled(user != null); menuPrivateChat.setEnabled(user != null && !myself.getName().equals(user.getName())); } }); userListTableViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent e) { StructuredSelection s = (StructuredSelection) e.getSelection(); LobbyUser user = (LobbyUser) s.getFirstElement(); if (user == null) return; showProfileWindow(user); } }); } private void showProfileWindow(LobbyUser user) { if (profileWindow == null) profileWindow = new UserProfileWindow(application, shell, myself); profileWindow.switchProfile(user); profileWindow.setVisible(true); } private void addMember(LobbyUser member) { memberMap.put(member.getName(), member); userListTableViewer.add(member); userListTableViewer.refresh(member); InfoLog log = new InfoLog(member.getName() + " が参加しました"); infoLogViewer.appendMessage(log); } private void removeMember(LobbyUser member) { if (memberMap.remove(member.getName()) == null) return; userListTableViewer.remove(member); userListTableViewer.refresh(member); InfoLog log = new InfoLog(member.getName() + " が退出しました"); infoLogViewer.appendMessage(log); } private void filterUserList() { if (userSearchFilter.getSelection()) { nameFilter.setName(userSearchName.getText()); setFilterValues(profileFilter, userSearchProfile); LobbyUserState state = null; switch (userSearchStateCombo.getSelectionIndex()) { case 1: state = LobbyUserState.LOGIN; break; case 2: state = LobbyUserState.AFK; break; case 3: state = LobbyUserState.PLAYING; break; case 4: state = LobbyUserState.INACTIVE; break; } stateFilter.setState(state); } else { nameFilter.setName(null); profileFilter.setValues(null); stateFilter.setState(null); } userListTableViewer.refresh(); } private void reflectAppearance() { if (infoLogViewer != null) infoLogViewer.applyAppearance(); chatLogViewer.applyAppearance(); } } private static class UserNameFilter extends ViewerFilter { private String name; public void setName(String name) { this.name = name; } @Override public boolean select(Viewer viewer, Object parent, Object element) { if (Utility.isEmpty(name)) return true; LobbyUser user = (LobbyUser) element; return user.getName().contains(name); } } private static class UserProfileFilter extends MultipleQueryStringFilter { private UserProfileFilter() { super(true); } @Override public boolean select(Viewer viewer, Object parent, Object element) { LobbyUser user = (LobbyUser) element; return filter(user.getProfile()); } } private static class UserStateFilter extends ViewerFilter { private LobbyUserState state; public void setState(LobbyUserState state) { this.state = state; } @Override public boolean select(Viewer viewer, Object parent, Object element) { if (state == null) return true; LobbyUser user = (LobbyUser) element; return state.equals(user.getState()); } } private interface LobbyCircleTabProcessor { public void process(LobbyCircleTab tab); } private void processTab(LobbyCircleTabProcessor processor) { processor.process(lobbyTab); processor.process(pmTab); for (LobbyCircleTab tab : myCircleTabs.values()) { processor.process(tab); } } private void connectToLobbyServer() { lobbyServerLoginButton.setEnabled(false); lobbyServerLoginButton.setSelection(true); PortalQuery query = new PortalQuery() { @Override public String getCommand() { return ProtocolConstants.Portal.COMMAND_FIND_LOBBY_SERVERS; } @Override public void failCallback(ErrorLog log) { lobbyTab.chatLogViewer.appendMessage(log); resetLobbyControllers(); } @Override public void successCallback(String address) { connectToLobbyServer(address); } }; application.queryPortalServer(query); } private void selectLobbyServer() { lobbyServerLoginButton.setEnabled(false); TextDialog dialog = new TextDialog(shell, "ロビーサーバーに接続します", "サーバーアドレスを入力してください", "接続する", 200, SWT.NONE); switch (dialog.open()) { case IDialogConstants.OK_ID: String address = dialog.getUserInput(); connectToLobbyServer(address); break; case IDialogConstants.CANCEL_ID: lobbyServerLoginButton.setEnabled(true); break; } } private void connectToLobbyServer(final String address) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { connectToLobbyServer(address); } }); return; } IniUserProfile profile = application.getUserProfile(); myself.setName(profile.getUserName()); myself.setUrl(profile.getUrl()); myself.setIconUrl(profile.getIconUrl()); myself.setProfile(profile.getProfile()); lobbyServerLoginButton.setSelection(true); lobbyServerLoginButton.setEnabled(false); lobbyServerAddressLabel.setText("サーバー: " + address); try { InetSocketAddress socketAddress = Utility.parseSocketAddress(address); application.connectTcp(socketAddress, lobbyProtocol); lobbySession = LobbySessionState.CONNECTING; } catch (IOException e) { resetLobbyControllers(); ErrorLog log = new ErrorLog(e); lobbyTab.infoLogViewer.appendMessage(log); } } catch (SWTException e) { } } private void resetLobbyControllers() { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { resetLobbyControllers(); } }); return; } lobbyServerLoginButton.setSelection(false); lobbyServerLoginButton.setEnabled(true); processTab(new LobbyCircleTabProcessor() { @Override public void process(LobbyCircleTab tab) { tab.userSearchFilter.setEnabled(false); tab.userSearchClear.setEnabled(false); } }); } catch (SWTException e) { } } private void initializeLobbySession() { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { initializeLobbySession(); } }); return; } setLobbyLoginUserName(myself.getName()); lobbyServerLoginButton.setText("ログイン中"); lobbyServerLoginButton.setEnabled(true); lobbyServerLoginButton.getParent().layout(); myself.setState(LobbyUserState.LOGIN); allLobbyUsers.put(myself.getName(), myself); lobbyTab.userListTableViewer.add(myself); pmTab.userListTableViewer.add(myself); lobbyUserStateCombo.setEnabled(true); lobbyUserStateCombo.select(0); int index = lobbyCircleTabFolder.getSelectionIndex(); switch (index) { case 0: lobbyTab.chatText.setFocus(); break; case 1: case 2: break; default: TabItem item = lobbyCircleTabFolder.getItem(index); LobbyCircleTab tab = myCircleTabs.get(item.getText()); tab.chatText.setFocus(); } ServerLog log = new ServerLog(myself.getName() + " としてログインしました"); lobbyTab.infoLogViewer.appendMessage(log); updateLobbyUserCount(); processTab(new LobbyCircleTabProcessor() { @Override public void process(LobbyCircleTab tab) { for (LobbyUser user : tab.memberMap.values()) { tab.userListTableViewer.add(user); } tab.userListTableViewer.refresh(); tab.userSearchFilter.setEnabled(true); tab.userSearchClear.setEnabled(true); } }); if (!myCircleTabs.isEmpty()) { StringBuilder sb = new StringBuilder(); for (String circle : myCircleTabs.keySet()) { sb.append(ProtocolConstants.Lobby.COMMAND_CIRCLE_JOIN); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(circle); sb.append(TextProtocolDriver.MESSAGE_SEPARATOR); } sb.deleteCharAt(sb.length() - 1); lobbyConnection.send(Utility.encode(sb)); } } catch (SWTException e) { } catch (RuntimeException e) { appendToSystemLog(Utility.stackTraceToString(e), true); } } private void updateLobbyUserCount() { lobbyUserCountLabel.setText("ユーザー数: " + allLobbyUsers.size()); lobbyUserCountLabel.getParent().layout(); } private void setLobbyLoginUserName(final String name) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { setLobbyLoginUserName(name); } }); return; } lobbyUserNameLabel.setText(name); lobbyUserNameLabel.getParent().layout(); lobbyLastActivity = System.currentTimeMillis(); } catch (SWTException e) { } } private void updateLobbyUser(final LobbyUser user, final boolean profileUpdated) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { updateLobbyUser(user, profileUpdated); } }); return; } processTab(new LobbyCircleTabProcessor() { @Override public void process(LobbyCircleTab tab) { if (tab.memberMap.containsKey(user.getName())) { tab.userListTableViewer.remove(user); tab.userListTableViewer.add(user); tab.userListTableViewer.refresh(user); } } }); if (profileUpdated && profileWindow != null) profileWindow.profileRefreshed(user); } catch (SWTException e) { } } private void addLobbyUser(final LobbyUser user) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { addLobbyUser(user); } }); return; } String name = user.getName(); allLobbyUsers.put(name, user); lobbyTab.userListTableViewer.add(user); lobbyTab.userListTableViewer.refresh(user); pmTab.userListTableViewer.add(user); pmTab.userListTableViewer.refresh(user); IniSettings settings = application.getSettings(); if (!name.equals(lobbyUserNameLabel.getText())) { InfoLog log = new InfoLog(name + " がログインしました"); lobbyTab.infoLogViewer.appendMessage(log); if (!isActiveWindow && settings.isBallonNotifyLobby() && settings.isBalloonLobbyEnterExit()) application.balloonNotify(shell, log.getMessage()); } updateLobbyUserCount(); } catch (SWTException e) { } } private void removeLobbyUser(final String name) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { removeLobbyUser(name); } }); return; } LobbyUser user = allLobbyUsers.remove(name); if (user == null) return; lobbyTab.userListTableViewer.remove(user); lobbyTab.userListTableViewer.refresh(user); pmTab.userListTableViewer.remove(user); pmTab.userListTableViewer.refresh(user); for (String circle : user.getCircles()) { LobbyCircleTab tab = myCircleTabs.get(circle); if (tab == null) { Map<String, LobbyUser> members = getCircleMemberMap(circle); members.remove(user.getName()); } else { tab.removeMember(user); } } IniSettings settings = application.getSettings(); InfoLog log = new InfoLog(name + " がログアウトしました"); lobbyTab.infoLogViewer.appendMessage(log); if (!isActiveWindow && settings.isBallonNotifyLobby() && settings.isBalloonLobbyEnterExit()) application.balloonNotify(shell, log.getMessage()); updateLobbyUserCount(); if (profileWindow != null) profileWindow.userLoggedOut(user); } catch (SWTException e) { } } private void addUserCircle(final LobbyUser user, final String circle) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { addUserCircle(user, circle); } }); return; } user.addCircle(circle); LobbyCircleTab tab = myCircleTabs.get(circle); if (tab == null) { Map<String, LobbyUser> members = getCircleMemberMap(circle); members.put(user.getName(), user); } else { tab.addMember(user); } if (profileWindow != null) profileWindow.circlesRefreshed(user); } catch (SWTException e) { } } private void removeUserCircle(final LobbyUser user, final String circle) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { removeUserCircle(user, circle); } }); return; } user.removeCircle(circle); LobbyCircleTab tab = myCircleTabs.get(circle); if (tab == null) { Map<String, LobbyUser> members = getCircleMemberMap(circle); members.remove(user.getName()); } else { tab.removeMember(user); } if (profileWindow != null) profileWindow.circlesRefreshed(user); } catch (SWTException e) { } } private static final IStructuredContentProvider CIRCLE_CONTENT_PROVIDER = new IStructuredContentProvider() { @Override public void inputChanged(Viewer arg0, Object arg1, Object arg2) { } @Override public void dispose() { } @Override public Object[] getElements(Object input) { @SuppressWarnings("unchecked") HashMap<String, HashMap<String, LobbyUser>> map = (HashMap<String, HashMap<String, LobbyUser>>) input; return map.entrySet().toArray(); } }; private static final ITableLabelProvider CIRCLE_LABEL_PROVIDER = new ITableLabelProvider() { @Override public void addListener(ILabelProviderListener arg0) { } @Override public void removeListener(ILabelProviderListener arg0) { } @Override public void dispose() { } @Override public boolean isLabelProperty(Object arg0, String arg1) { return false; } @Override public Image getColumnImage(Object element, int index) { return null; } @Override public String getColumnText(Object element, int index) { @SuppressWarnings("unchecked") Entry<String, HashMap<String, LobbyUser>> entry = (Entry<String, HashMap<String, LobbyUser>>) element; switch (index) { case 0: return entry.getKey(); case 1: return Integer.toString(entry.getValue().size()); } return ""; } }; private static final ViewerSorter CIRCLE_NAME_SORTER = new ViewerSorter() { @SuppressWarnings("unchecked") @Override public int compare(Viewer viewer, Object e1, Object e2) { Entry<String, HashMap<String, LobbyUser>> entry1 = (Entry<String, HashMap<String, LobbyUser>>) e1; Entry<String, HashMap<String, LobbyUser>> entry2 = (Entry<String, HashMap<String, LobbyUser>>) e2; return entry1.getKey().compareTo(entry2.getKey()); } }; private static final ViewerSorter CIRCLE_COUNT_SORTER = new ViewerSorter() { @SuppressWarnings("unchecked") @Override public int compare(Viewer viewer, Object e1, Object e2) { Entry<String, HashMap<String, LobbyUser>> entry1 = (Entry<String, HashMap<String, LobbyUser>>) e1; Entry<String, HashMap<String, LobbyUser>> entry2 = (Entry<String, HashMap<String, LobbyUser>>) e2; return Utility.compare(entry1.getValue().size(), entry2.getValue().size()); } }; private static final ViewerFilter CIRCLE_EMPTY_FILTER = new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parent, Object element) { @SuppressWarnings("unchecked") Entry<String, HashMap<String, LobbyUser>> entry = (Entry<String, HashMap<String, LobbyUser>>) element; return !entry.getValue().isEmpty(); } }; private class LobbyCircleDialog extends Dialog { private String circleName; public LobbyCircleDialog() { super(shell); setShellStyle(getShellStyle() | SWT.RESIZE); } @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText("サークル追加 / 一覧"); } @Override protected Control createDialogArea(Composite parent) { GridData gridData; Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(1, false)); gridData = new GridData(SWT.FILL, SWT.FILL, true, true); composite.setLayoutData(gridData); Label label = new Label(composite, SWT.NONE); label.setText("サークル名を入力するか、一覧から選択してください"); final Text text = new Text(composite, SWT.BORDER | SWT.SINGLE); gridData = new GridData(GridData.FILL, GridData.CENTER, true, false); text.setLayoutData(gridData); TableViewer circleTableViewer = new TableViewer(composite, SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION); Table circleTable = circleTableViewer.getTable(); circleTable.setHeaderVisible(true); TableColumn nameColumn = new TableColumn(circleTable, SWT.LEFT); nameColumn.setText("サークル名"); nameColumn.setWidth(200); TableColumn countColumn = new TableColumn(circleTable, SWT.RIGHT); countColumn.setText("所属人数"); countColumn.setWidth(60); gridData = new GridData(SWT.FILL, SWT.FILL, true, true); gridData.minimumHeight = 150; circleTable.setLayoutData(gridData); final Label errorLabel = new Label(composite, SWT.NONE); errorLabel.setForeground(application.getColorRegistry().get(PlayClient.COLOR_NG)); gridData = new GridData(SWT.FILL, SWT.CENTER, true, false); errorLabel.setLayoutData(gridData); circleTableViewer.setContentProvider(CIRCLE_CONTENT_PROVIDER); circleTableViewer.setLabelProvider(CIRCLE_LABEL_PROVIDER); circleTableViewer.setInput(circleMap); SwtUtils.installSorter(circleTableViewer, nameColumn, CIRCLE_NAME_SORTER); SwtUtils.installSorter(circleTableViewer, countColumn, CIRCLE_COUNT_SORTER); circleTableViewer.setSorter(CIRCLE_NAME_SORTER); circleTable.setSortColumn(nameColumn); circleTable.setSortDirection(SWT.UP); circleTableViewer.addFilter(CIRCLE_EMPTY_FILTER); text.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { String name = text.getText(); if (myCircleTabs.containsKey(name)) { getButton(OK).setEnabled(false); errorLabel.setText("すでに所属しているサークルです"); } else { getButton(OK).setEnabled(!Utility.isEmpty(name)); errorLabel.setText(""); circleName = name; } } }); circleTableViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent e) { IStructuredSelection sel = (IStructuredSelection) e.getSelection(); @SuppressWarnings("unchecked") Entry<String, HashMap<String, LobbyUser>> entry = (Entry<String, HashMap<String, LobbyUser>>) sel.getFirstElement(); if (entry == null) return; if (myCircleTabs.containsKey(entry.getKey())) { getButton(OK).setEnabled(false); errorLabel.setText("すでに所属しているサークルです"); return; } text.setText(entry.getKey()); } }); circleTableViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent e) { IStructuredSelection sel = (IStructuredSelection) e.getSelection(); @SuppressWarnings("unchecked") Entry<String, HashMap<String, LobbyUser>> entry = (Entry<String, HashMap<String, LobbyUser>>) sel.getFirstElement(); if (entry == null) return; if (myCircleTabs.containsKey(entry.getKey())) return; circleName = entry.getKey(); setReturnCode(OK); close(); } }); return composite; } @Override protected Control createButtonBar(Composite parent) { Control control = super.createButtonBar(parent); Button button = getButton(OK); button.setEnabled(false); button.setText("追加"); return control; } private String getCircleName() { return circleName; } } private void openAddCircleDialog() { LobbyCircleDialog dialog = new LobbyCircleDialog(); switch (dialog.open()) { case IDialogConstants.OK_ID: String circle = dialog.getCircleName(); if (Utility.isValidNameString(circle)) { requestCircleJoin(circle); } else { lobbyCircleTabFolder.setSelection(0); ErrorLog log = new ErrorLog("サークル名規則に従っていません"); lobbyTab.chatLogViewer.appendMessage(log); } break; } } void requestCircleJoin(String circle) { if (!Utility.isValidNameString(circle)) return; LobbyCircleTab tab = myCircleTabs.get(circle); if (tab == null) { Map<String, LobbyUser> memberMap = getCircleMemberMap(circle); Image icon = application.getImageRegistry().get(PlayClient.ICON_TAB_CIRCLE); Image iconNotify = application.getImageRegistry().get(PlayClient.ICON_TAB_CIRCLE_NOTIFY); tab = new LobbyCircleTab(circle, memberMap, icon, iconNotify, true); myCircleTabs.put(circle, tab); if (lobbyConnection.isConnected()) { StringBuilder sb = new StringBuilder(); sb.append(ProtocolConstants.Lobby.COMMAND_CIRCLE_JOIN); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(circle); lobbyConnection.send(Utility.encode(sb)); tab.userSearchFilter.setEnabled(true); tab.userSearchClear.setEnabled(true); } } lobbyCircleTabFolder.setSelection(tab.tabItem); } void requestCircleLeave(String circle) { if (!Utility.isValidNameString(circle)) return; LobbyCircleTab tab = myCircleTabs.remove(circle); if (tab == null) return; tab.tabItem.dispose(); if (!lobbyConnection.isConnected()) return; StringBuilder sb = new StringBuilder(); sb.append(ProtocolConstants.Lobby.COMMAND_CIRCLE_LEAVE); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(circle); lobbyConnection.send(Utility.encode(sb)); } private void notifyTabUpdate(final LobbyCircleTab tab) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { notifyTabUpdate(tab); } }); return; } if (tab.tabItem == lobbyCircleTabFolder.getItem(lobbyCircleTabFolder.getSelectionIndex())) return; tab.tabItem.setImage(tab.iconNotify); } catch (SWTException e) { } } private boolean sendChat(String message, LobbyCircleTab tab) { if (!Utility.isEmpty(message)) { switch (lobbySession) { case LOGIN: { changeLobbyStateTo(LobbyUserState.LOGIN); StringBuilder sb = new StringBuilder(); sb.append(ProtocolConstants.Lobby.COMMAND_CHAT); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(tab == lobbyTab ? "" : tab.circleName); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(message); lobbyConnection.send(Utility.encode(sb)); return true; } default: InfoLog log = new InfoLog("サーバーにログインしていません"); lobbyTab.chatLogViewer.appendMessage(log); } } return false; } public void openPrivateMessageDialog(LobbyUser user) { TextDialog dialog = new TextDialog(shell, "プライベートメッセージの送信", user.getName() + " にメッセージを送信します", "送信", 250); switch (dialog.open()) { case IDialogConstants.OK_ID: String message = dialog.getUserInput(); StringBuilder sb = new StringBuilder(); sb.append(ProtocolConstants.Lobby.COMMAND_PRIVATE_MESSAGE); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(user.getName()); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(message); lobbyConnection.send(Utility.encode(sb)); PrivateChat chat = new PrivateChat(myself.getName(), user.getName(), message, true); pmTab.chatLogViewer.appendMessage(chat); break; } } public void changeLobbyStateTo(final LobbyUserState state) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { changeLobbyStateTo(state); } }); return; } if (!lobbyConnection.isConnected() || lobbySession != LobbySessionState.LOGIN) return; lobbyUserState = state; int index; switch (state) { case LOGIN: index = 0; break; case AFK: index = 1; break; case PLAYING: index = 2; break; case INACTIVE: index = 3; break; default: return; } lobbyLastActivity = System.currentTimeMillis(); if (lobbyUserStateCombo.getSelectionIndex() == index) return; lobbyUserStateCombo.select(index); sendLobbyState(); } catch (SWTException e) { } } private void sendLobbyState() { switch (lobbyUserStateCombo.getSelectionIndex()) { case 0: lobbyUserState = LobbyUserState.LOGIN; break; case 1: lobbyUserState = LobbyUserState.AFK; break; case 2: lobbyUserState = LobbyUserState.PLAYING; break; case 3: lobbyUserState = LobbyUserState.INACTIVE; break; default: return; } ByteBuffer buf = Utility.encode(ProtocolConstants.Lobby.COMMAND_CHANGE_STATE + TextProtocolDriver.ARGUMENT_SEPARATOR + lobbyUserState.getAbbreviation()); lobbyConnection.send(buf); } private class LobbyProtocol implements IProtocol { @Override public void log(String message) { ErrorLog log = new ErrorLog(message); lobbyTab.chatLogViewer.appendMessage(log); } @Override public String getProtocol() { return ProtocolConstants.PROTOCOL_LOBBY; } @Override public IProtocolDriver createDriver(ISocketConnection connection) { lobbyConnection = connection; StringBuilder sb = new StringBuilder(); sb.append(ProtocolConstants.Lobby.COMMAND_LOGIN); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(myself.getName()); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(myself.getUrl()); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(myself.getIconUrl()); sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR); sb.append(myself.getProfile()); connection.send(Utility.encode(sb)); return new LobbyProtocolDriver(connection); } } private class LobbyProtocolDriver extends TextProtocolDriver { public LobbyProtocolDriver(ISocketConnection connection) { super(connection, lobbyHandlers); } @Override public void log(String message) { ErrorLog log = new ErrorLog(message); lobbyTab.chatLogViewer.appendMessage(log); } @Override public void errorProtocolNumber(String number) { String error = String.format("サーバーとのプロトコルナンバーが一致しないので接続できません サーバー:%s クライアント:%s", number, IProtocol.NUMBER); ErrorLog log = new ErrorLog(error); lobbyTab.chatLogViewer.appendMessage(log); } @Override public void connectionDisconnected() { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { connectionDisconnected(); } }); return; } lobbyConnection = ISocketConnection.NULL; lobbySession = LobbySessionState.OFFLINE; lobbyUserState = LobbyUserState.OFFLINE; lobbyServerLoginButton.setText("ロビーログイン"); setLobbyLoginUserName(""); resetLobbyControllers(); lobbyUserStateCombo.setEnabled(false); lobbyUserStateCombo.deselect(0); lobbyServerAddressLabel.setText("サーバー: "); lobbyUserCountLabel.setText("ユーザー数: "); lobbyServerAddressLabel.getParent().layout(); allLobbyUsers.clear(); lobbyTab.userListTableViewer.refresh(); pmTab.userListTableViewer.refresh(); ServerLog log = new ServerLog("ロビーサーバーからログアウトしました"); lobbyTab.infoLogViewer.appendMessage(log); for (Map<String, LobbyUser> members : circleMap.values()) { members.clear(); } for (LobbyCircleTab tab : myCircleTabs.values()) { tab.infoLogViewer.appendMessage(log); tab.userListTableViewer.refresh(); } if (application.getSettings().isBallonNotifyLobby() && !isActiveWindow) application.balloonNotify(shell, log.getMessage()); if (profileWindow != null) profileWindow.switchProfile(null); } catch (SWTException e) { } } } private HashMap<String, IProtocolMessageHandler> lobbyHandlers = new HashMap<String, IProtocolMessageHandler>(); { lobbyHandlers.put(ProtocolConstants.Lobby.COMMAND_LOGIN, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String args) { lobbySession = LobbySessionState.LOGIN; lobbyUserState = LobbyUserState.LOGIN; if (args.length() > 0) { String[] tokens = args.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 6) return false; String name = tokens[0]; LobbyUserState state = LobbyUserState.findState(tokens[1]); if (Utility.isEmpty(name) || state == null) return false; String url = tokens[2]; String iconUrl = tokens[3]; String profile = tokens[4]; String[] circleList = tokens[5].split("\n"); LobbyUser user = new LobbyUser(name, state); user.setUrl(url); user.setIconUrl(iconUrl); user.setProfile(profile); for (String circle : circleList) { if (Utility.isEmpty(circle)) continue; user.addCircle(circle); Map<String, LobbyUser> members = getCircleMemberMap(circle); members.put(user.getName(), user); } allLobbyUsers.put(name, user); } else { initializeLobbySession(); lobbyLastActivity = System.currentTimeMillis(); } return true; } }); lobbyHandlers.put(ProtocolConstants.Lobby.COMMAND_CHAT, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, 3); if (tokens.length != 3) return true; String name = tokens[0]; String circle = tokens[1]; String message = tokens[2]; boolean isMine = name.equals(myself.getName()); LobbyCircleTab tab; Chat chat; if (Utility.isEmpty(circle)) { tab = lobbyTab; chat = new Chat(name, message, isMine); } else { tab = myCircleTabs.get(circle); if (tab == null) return true; chat = new LobbyCircleChat(name, message, isMine, circle); } chat(tab, message, name, isMine); application.lobbyMessageReceived(chat); return true; } private void chat(final LobbyCircleTab tab, final String message, final String name, final boolean isMine) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { chat(tab, message, name, isMine); } }); return; } String rulerName = name; for (String line : message.replace("\r", "").split("\n", -1)) { Chat chat = new Chat(rulerName, line, isMine); tab.chatLogViewer.appendMessage(chat); rulerName = ""; } if (!isMine) { notifyTabUpdate(tab); if (!isActiveWindow && application.getSettings().isBallonNotifyLobby()) application.balloonNotify(shell, "<" + name + "> " + message); } } catch (SWTException e) { } } }); lobbyHandlers.put(ProtocolConstants.Lobby.COMMAND_PRIVATE_MESSAGE, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, 2); if (tokens.length != 2) return true; String sender = tokens[0]; String message = tokens[1]; pm(sender, message); return true; } private void pm(final String sender, final String message) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { pm(sender, message); } }); return; } String myName = myself.getName(); boolean isMine = sender.equals(myName); for (String line : message.replace("\r", "").split("\n", -1)) { PrivateChat chat = new PrivateChat(sender, myName, line, false); pmTab.chatLogViewer.appendMessage(chat); } notifyTabUpdate(pmTab); PrivateChat chat = new PrivateChat(sender, myName, message, isMine); if (!isActiveWindow) application.balloonNotify(shell, "(" + sender + " → " + myName + ") " + message); application.lobbyMessageReceived(chat); } catch (SWTException e) { } } }); lobbyHandlers.put(ProtocolConstants.Lobby.NOTIFY_FROM_ADMIN, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String message) { adminMessage(message); return true; } private void adminMessage(final String message) { try { if (SwtUtils.isNotUIThread()) { SwtUtils.DISPLAY.asyncExec(new Runnable() { @Override public void run() { adminMessage(message); } }); return; } for (String line : message.replace("\r", "").split("\n", -1)) { AdminNotify log = new AdminNotify(line); lobbyTab.chatLogViewer.appendMessage(log); } if (!isActiveWindow) application.balloonNotify(shell, "[サーバー告知] " + message); } catch (SWTException e) { } } }); lobbyHandlers.put(ProtocolConstants.Lobby.NOTIFY_STATE_CHANGE, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 2) return true; String name = tokens[0]; final LobbyUser user = allLobbyUsers.get(name); if (user == null) { return true; } LobbyUserState state = LobbyUserState.findState(tokens[1]); if (state == null) return true; user.setState(state); updateLobbyUser(user, false); return true; } }); lobbyHandlers.put(ProtocolConstants.Lobby.NOTIFY_PROFILE_UPDATE, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 4) return true; String name = tokens[0]; LobbyUser user = allLobbyUsers.get(name); if (user == null) return true; String url = tokens[1]; String iconUrl = tokens[2]; String profile = tokens[3]; user.setUrl(url); user.setIconUrl(iconUrl); user.setProfile(profile); updateLobbyUser(user, true); return true; } }); lobbyHandlers.put(ProtocolConstants.Lobby.NOTIFY_LOGIN, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 4) return true; String name = tokens[0]; String url = tokens[1]; String iconUrl = tokens[2]; String profile = tokens[3]; LobbyUser user = new LobbyUser(name, LobbyUserState.LOGIN); user.setUrl(url); user.setIconUrl(iconUrl); user.setProfile(profile); addLobbyUser(user); return true; } }); lobbyHandlers.put(ProtocolConstants.Lobby.NOTIFY_LOGOUT, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String name) { removeLobbyUser(name); return true; } }); lobbyHandlers.put(ProtocolConstants.Lobby.NOTIFY_CIRCLE_JOIN, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 2) return true; String name = tokens[0]; String circle = tokens[1]; LobbyUser user = allLobbyUsers.get(name); if (user == null) return true; addUserCircle(user, circle); return true; } }); lobbyHandlers.put(ProtocolConstants.Lobby.NOTIFY_CIRCLE_LEAVE, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1); if (tokens.length != 2) return true; String name = tokens[0]; String circle = tokens[1]; LobbyUser user = allLobbyUsers.get(name); if (user == null) return true; removeUserCircle(user, circle); return true; } }); lobbyHandlers.put(ProtocolConstants.Lobby.ERROR_LOGIN_USER_BEYOND_CAPACITY, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { ErrorLog log = new ErrorLog("ロビーが満室なので入れません"); lobbyTab.chatLogViewer.appendMessage(log); return false; } }); lobbyHandlers.put(ProtocolConstants.Lobby.ERROR_LOGIN_USER_DUPLICATED_NAME, new IProtocolMessageHandler() { @Override public boolean process(IProtocolDriver driver, String argument) { ErrorLog log = new ErrorLog("同名のユーザーが既にログインしているのでログインできません"); lobbyTab.chatLogViewer.appendMessage(log); return false; } }); } }