/**
  * Called to indicate a new chat state in this conversation.
  *
  * @param chatState being added.
  */
 public void newChatState(ChatState chatState) {
   LOG.debugf("Chat state update for {0} to {1}", chatFriend.getName(), chatState);
   if (currentChatState != chatState) {
     currentChatState = chatState;
     displayMessages();
   }
 }
 public void sendFeature(FriendPresence presence, Address address) throws FriendException {
   LOG.debugf("sending new address to {0}", presence);
   AddressIQ queryResult = new AddressIQ(address, factory);
   queryResult.setTo(presence.getPresenceId());
   queryResult.setFrom(connection.getLocalJid());
   queryResult.setType(IQ.Type.SET);
   connection.sendPacket(queryResult);
 }
  /** Adds the specified search result to the results list. */
  @Override
  public void addSearchResult(SearchResult result) {
    if (result.getUrn() == null) {
      // Some results can be missing a URN, specifically
      // secure results.  For now, we drop these.
      // We should figure out a way to show them later on.
      return;
    }

    LOG.debugf("Adding result urn: {0} EDT: {1}", result.getUrn(), EventQueue.isDispatchThread());
    allSearchResults.add(result);
  }
  private void handleLinkClick(String linkDescription, URL url) {

    if (ChatDocumentBuilder.LIBRARY_LINK.equals(linkDescription)) {
      ChatFriend libraryChatFriend = conversation.getChatFriend();
      LOG.debugf("Opening a view to {0}'s library", libraryChatFriend.getName());
      //            libraryNavigator.selectFriendLibrary(libraryChatFriend.getFriend());
      throw new IllegalStateException("action does't exist");

    } else if (ChatDocumentBuilder.MY_LIBRARY_LINK.equals(linkDescription)) {
      LOG.debugf("Opening a view to my library");
      //            libraryNavigator.selectLibrary();
      throw new IllegalStateException("action does't exist");
    } else {
      LOG.debugf("Hyperlink clicked: {0}", linkDescription);
      if (linkDescription.startsWith("magnet")) {
        // TODO: Need to do something with magnet links

      } else if (url != null) {
        NativeLaunchUtils.openURL(url.toString());
      }
    }
  }
  /**
   * Route a message to the {@link UDPConnectionProcessor} identified via the message's connection
   * ID. Notifies the provided listener (if any) if the channel is ready to produce events.
   */
  public void routeMessage(RUDPMessage msg, InetSocketAddress addr) {
    UDPSocketChannel[] array = _channels;
    int connID = msg.getConnectionID() & 0xff;
    UDPSocketChannel channel = null;
    // If connID equals 0 and SynMessage then associate with a connection
    // that appears to want it (connecting and with knowledge of it).
    if (connID == 0 && msg instanceof SynMessage) {
      LOG.debugf("route sym: {0}", msg);
      for (int i = 1; i < array.length; i++) {
        channel = array[i];
        if (channel == null) continue;

        LOG.debugf("non-empty index: {0}, addr: {1}", i, channel.getRemoteSocketAddress());

        if (channel.isConnectionPending() && channel.isForMe(addr, (SynMessage) msg)) {
          LOG.debugf(
              "found index: {0}, sender id: {1}", i, ((SynMessage) msg).getSenderConnectionID());
          channel.getProcessor().handleMessage(msg);
          break;
        }
      }
      // Note: eventually these messages should find a match
      // so it is safe to throw away premature ones

    } else if (array[connID] != null) { // If valid connID then send on to connection
      channel = array[connID];
      if (msg instanceof SynMessage) {
        LOG.debugf("already assigned syn: {0}", msg);
      }
      if (channel.getRemoteSocketAddress().equals(addr)) channel.getProcessor().handleMessage(msg);
    } else {
      LOG.debugf("message for non-existing connection: {0}", msg);
    }

    if (channel != null && channel.getProcessor().readyOps() != 0)
      context.getTransportListener().eventPending();
  }
  /**
   * Add a new {@link Message} to this conversation.
   *
   * @param message being added
   */
  public void newChatMessage(Message message) {
    // TODO: Refactor this,ChatDocumentBuilder, etc into cleaner/clearer, way to display msgs
    LOG.debugf("Message: from {0} text: {1}", message.getSenderName(), message.toString());
    messages.add(message);
    Type type = message.getType();

    if (type != Type.SENT) {
      currentChatState = ChatState.active;
    }

    if (message instanceof MessageFileOffer) {
      MessageFileOffer msgWithFileOffer = (MessageFileOffer) message;
      addFileOfferMessage(msgWithFileOffer);
    } else if (message instanceof NoSaveStatusMessage) {
      updateNoSaveLink(((NoSaveStatusMessage) message).getStatus());
    }

    displayMessages();
  }
  @Override
  public void hyperlinkUpdate(HyperlinkEvent e) {
    if (e instanceof FormSubmitEvent) {
      FormSubmitEvent event = (FormSubmitEvent) e;

      // Just pushed the download the file button...
      LOG.debugf("File offer download requested. FileId: {0}", event.getData());

      try {
        String dataStr = event.getData();
        String fileIdEncoded = dataStr.substring(dataStr.indexOf("=") + 1).trim();
        String fileId = URLDecoder.decode(fileIdEncoded, "UTF-8");
        downloadFileOffer(fileId);
      } catch (UnsupportedEncodingException uee) {
        throw new RuntimeException(uee); // impossible
      }
    } else if (HyperlinkEvent.EventType.ACTIVATED == e.getEventType()) {
      handleLinkClick(e.getDescription(), e.getURL());
    }
  }
  private void displayMessages(boolean friendSignedOff) {
    String chatDoc =
        ChatDocumentBuilder.buildChatText(
            messages, currentChatState, conversationName, friendSignedOff);
    LOG.debugf("Chat doc: {0}", chatDoc);
    final JScrollBar verticalScrollBar = conversationScroll.getVerticalScrollBar();
    final int scrollValue = verticalScrollBar.getValue();
    editor.setText(chatDoc);

    // LWC-2262: If the scroll bar was moved above the bottom of the scrollpane, reset the value of
    // the bar to where it was before the text was updated.  This needs to be issued to the end of
    // the
    // queue because the actual repainting/resizing of the scrollbar happens later in a
    // task added to the EDT by the plaf listener of the editor's document.
    // A better fix for this behavior may be possible
    if (verticalScrollBar.getMaximum()
        > (scrollValue + verticalScrollBar.getVisibleAmount() + PADDING)) {
      SwingUtilities.invokeLater(
          new Runnable() {
            @Override
            public void run() {
              verticalScrollBar.setValue(scrollValue);
            }
          });
    } else {
      SwingUtilities.invokeLater(
          new Runnable() {
            @Override
            public void run() {
              verticalScrollBar.setValue(verticalScrollBar.getMaximum());
            }
          });
    }

    decorateFileOfferButtons();

    chatWrapper.repaint();
  }