/*
* Kontalk Java client
* Copyright (C) 2016 Kontalk Devteam <devteam@kontalk.org>
*
* This program 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 org.kontalk.view;
import javax.swing.Box;
import javax.swing.ListSelectionModel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Optional;
import com.alee.extended.panel.GroupPanel;
import com.alee.extended.panel.GroupingType;
import com.alee.laf.label.WebLabel;
import com.alee.laf.menu.WebMenuItem;
import com.alee.laf.menu.WebPopupMenu;
import org.kontalk.model.Contact;
import org.kontalk.model.chat.Chat;
import org.kontalk.model.chat.ChatList;
import org.kontalk.model.chat.GroupChat;
import org.kontalk.model.chat.Member;
import org.kontalk.model.chat.SingleChat;
import org.kontalk.model.message.KonMessage;
import org.kontalk.persistence.Config;
import org.kontalk.util.Tr;
/**
* Show a brief list of all chats.
* @author Alexander Bikadorov {@literal <bikaejkb@mail.tu-berlin.de>}
*/
final class ChatListView extends ListView<Chat> {
private final ChatList mChatList;
ChatListView(final View view, ChatList chatList) {
super(view,
new FlyweightChatItem(),
new FlyweightChatItem(),
ListSelectionModel.SINGLE_SELECTION,
false,
true);
mChatList = chatList;
this.updateOnEDT(null);
}
void selectLastChat() {
int i = Config.getInstance().getInt(Config.VIEW_SELECTED_CHAT);
if (i < 0) i = 0;
this.setSelectedItem(i);
}
void save() {
Config.getInstance().setProperty(Config.VIEW_SELECTED_CHAT,
this.getSelectedRow());
}
@Override
public int compare(Chat c1, Chat c2) {
KonMessage m = c1.getMessages().getLast().orElse(null);
KonMessage oM = c2.getMessages().getLast().orElse(null);
return m != null && oM != null ?
- m.getDate().compareTo(oM.getDate()) :
- Integer.compare(c1.getID(), c2.getID());
}
@Override
protected void updateOnEDT(Object arg) {
if (arg == null || arg == ChatList.ViewChange.MODIFIED)
this.sync(mChatList.getAll());
}
@Override
protected void selectionChanged(Optional<Chat> value) {
mView.onChatSelectionChanged(value);
}
@Override
protected WebPopupMenu rightClickMenu(List<Chat> selectedValues) {
WebPopupMenu menu = new WebPopupMenu();
if (selectedValues.isEmpty())
return menu;
Chat chat = selectedValues.get(0);
if (chat instanceof SingleChat) {
final Contact contact = ((SingleChat) chat).getMember().getContact();
if (!contact.isDeleted()) {
WebMenuItem editItem = new WebMenuItem(Tr.tr("Edit Contact"));
editItem.setToolTipText(Tr.tr("Edit contact settings"));
editItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
mView.showContactDetails(contact);
}
});
menu.add(editItem);
}
}
WebMenuItem deleteItem = new WebMenuItem(Tr.tr("Delete Chat"));
deleteItem.setToolTipText(Tr.tr("Delete this chat"));
deleteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
ChatListView.this.deleteChat(chat);
}
});
menu.add(deleteItem);
return menu;
}
private void deleteChat(Chat chat) {
if (!chat.getMessages().isEmpty()) {
String text = Tr.tr("Permanently delete all messages in this chat?");
if (chat.isGroupChat() && chat.isValid())
text += "\n\n"+Tr.tr("You will automatically leave this group.");
if (!Utils.confirmDeletion(this, text))
return;
}
mView.getControl().deleteChat(chat);
}
@Override
protected String getTooltipText(Chat value) {
return "<html><body>" +
lastActivity(value, true, false) + "<br>"
+ "</body></html>";
}
@Override
protected void onRenameEvent() {
Chat chat = this.getSelectedValue().orElse(null);
if (chat instanceof SingleChat) {
mView.requestRenameFocus(((SingleChat) chat).getMember().getContact());
return;
}
if (chat instanceof GroupChat) {
// TODO
}
}
private static final class FlyweightChatItem extends FlyweightItem<Chat> {
private final ComponentUtils.AvatarImage mAvatar;
private final WebLabel mTitleLabel;
private final WebLabel mStatusLabel;
private final WebLabel mChatStateLabel;
FlyweightChatItem() {
this.setLayout(new BorderLayout(View.GAP_DEFAULT, 0));
this.setMargin(View.MARGIN_DEFAULT);
mAvatar = new ComponentUtils.AvatarImage(View.AVATAR_LIST_SIZE);
this.add(mAvatar, BorderLayout.WEST);
mTitleLabel = new WebLabel();
mTitleLabel.setFontSize(View.FONT_SIZE_BIG);
mTitleLabel.setDrawShade(true);
mStatusLabel = new WebLabel();
mStatusLabel.setForeground(Color.GRAY);
mStatusLabel.setFontSize(View.FONT_SIZE_TINY);
this.add(mStatusLabel, BorderLayout.EAST);
mChatStateLabel = new WebLabel();
mChatStateLabel.setForeground(View.DARK_RED);
mChatStateLabel.setFontSize(View.FONT_SIZE_TINY);
mChatStateLabel.setBoldFont();
//mChatStateLabel.setMargin(0, 5, 0, 5);
this.add(
new GroupPanel(View.GAP_SMALL, false,
mTitleLabel,
new GroupPanel(GroupingType.fillFirst,
Box.createGlue(), mStatusLabel, mChatStateLabel)
), BorderLayout.CENTER);
}
@Override
protected void render(Chat value, int listWidth, boolean isSelected, boolean isLast) {
// background
this.setBackground(isSelected ? View.BLUE :
!value.isRead() ? View.LIGHT_BLUE :
Color.WHITE);
// avatar
mAvatar.setAvatarImage(value);
// title
mTitleLabel.setText(Utils.chatTitle(value));
if (value.isGroupChat())
mTitleLabel.setForeground(View.DARK_GREEN);
// state
String stateText = "";
List<Member> members = value.getAllMembers();
if (!members.isEmpty()) {
Member member = members.get(0);
switch (member.getState()) {
case composing:
stateText = Tr.tr("is writing…");
break;
//case paused: activity = T/r.tr("stopped typing"); break;
//case inactive: stateText = T/r.tr("is inactive"); break;
}
}
// not used: chatstates for group chats
// if (!stateText.isEmpty() && mValue.isGroupChat())
// stateText = member.getContact().getName() + ": " + stateText;
mChatStateLabel.setText(stateText);
// status
mStatusLabel.setText(stateText.isEmpty() ? lastActivity(value, isSelected, true) : "");
mStatusLabel.setVisible(stateText.isEmpty());
}
}
private static String lastActivity(Chat chat, boolean withLabel, boolean pretty) {
KonMessage m = chat.getMessages().getLast().orElse(null);
return m == null ? Tr.tr("No messages yet") :
(withLabel ? Tr.tr("Last message:") + " " : "") +
(pretty ? Utils.PRETTY_TIME.format(m.getDate()) :
Utils.MID_DATE_FORMAT.format(m.getDate()));
}
}