diff --git a/build.xml b/build.xml
index 338b101c6..e2d1c150b 100644
--- a/build.xml
+++ b/build.xml
@@ -743,7 +743,7 @@
-
+
@@ -751,8 +751,8 @@
-
+
@@ -852,6 +852,7 @@
bundle-growlnotification,bundle-audionotifier,bundle-plugin-splashscreen,
bundle-systray,bundle-browserlauncher,bundle-gibberish,
bundle-gibberish-slick,bundle-plugin-gibberishaccregwizz,
+ bundle-plugin-extended-callhistory-search,
bundle-rss,bundle-plugin-rssaccregwizz,
bundle-pluginmanager"/>
@@ -1545,6 +1546,17 @@ javax.swing.event, javax.swing.border"/>
prefix="net/java/sip/communicator/plugin/pluginmanager"/>
+
+
+
+
+
+
+
+
+
diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties
index 0ef41b046..22493d539 100644
--- a/lib/felix.client.run.properties
+++ b/lib/felix.client.run.properties
@@ -71,8 +71,8 @@ felix.auto.start.50= \
reference:file:sc-bundles/protocol-yahoo.jar \
reference:file:sc-bundles/protocol-gibberish.jar \
reference:file:sc-bundles/netaddr.jar \
- reference:file:sc-bundles/meta-cl.jar \
- reference:file:sc-bundles/protocol-rss.jar
+ reference:file:sc-bundles/protocol-rss.jar \
+ reference:file:sc-bundles/meta-cl.jar
felix.auto.start.60= \
reference:file:sc-bundles/history.jar \
@@ -94,8 +94,9 @@ felix.auto.start.60= \
reference:file:sc-bundles/msnaccregwizz.jar \
reference:file:sc-bundles/yahooaccregwizz.jar \
reference:file:sc-bundles/gibberishaccregwizz.jar \
- reference:file:sc-bundles/shutdown.jar \
- reference:file:sc-bundles/rssaccregwizz.jar
+ reference:file:sc-bundles/extendedcallhistorysearch.jar \
+ reference:file:sc-bundles/rssaccregwizz.jar \
+ reference:file:sc-bundles/shutdown.jar
# Uncomment the following lines if you want to run the architect viewer
# bundle.
diff --git a/lib/installer-exclude/jcalendar-1.3.2.jar b/lib/installer-exclude/jcalendar-1.3.2.jar
new file mode 100644
index 000000000..b5e5a2d82
Binary files /dev/null and b/lib/installer-exclude/jcalendar-1.3.2.jar differ
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/AntialiasingManager.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/AntialiasingManager.java
new file mode 100644
index 000000000..3763b9a2b
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/AntialiasingManager.java
@@ -0,0 +1,33 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.awt.*;
+
+/**
+ * Through the AntialiasingManager the developer could activate the
+ * antialiasing mechanism when painting. The method that do the job is
+ * the activateAntialiasing method. It takes a Graphics
+ * object and activates the antialiasing for it.
+ *
+ * @author Yana Stamcheva
+ */
+public class AntialiasingManager {
+
+ /**
+ * Activates the antialiasing mechanism for the given Graphics
+ * object.
+ * @param g The Graphics object.
+ */
+ public static void activateAntialiasing(Graphics g) {
+ Graphics2D g2d = (Graphics2D) g;
+
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallList.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallList.java
new file mode 100644
index 000000000..4311ea8fd
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallList.java
@@ -0,0 +1,85 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+/**
+ * The CallList is the component that contains history call records.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallList
+ extends JList
+ implements MouseListener
+{
+ public CallList()
+ {
+ this.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
+
+ this.getSelectionModel().setSelectionMode(
+ ListSelectionModel.SINGLE_SELECTION);
+
+ this.setCellRenderer(new CallListCellRenderer());
+
+ this.setModel(new CallListModel());
+
+ this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+
+ this.addMouseListener(this);
+ }
+
+ public void addItem(Object item)
+ {
+ ((CallListModel)this.getModel()).addElement(item);
+ }
+
+ public void addItem(Object item, int index)
+ {
+ ((CallListModel)this.getModel()).addElement(index, item);
+ }
+
+ public void removeAll()
+ {
+ ((CallListModel)this.getModel()).removeAll();
+ }
+
+ /**
+ * Closes or opens a group of calls.
+ */
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() > 1) {
+
+ CallListModel listModel = (CallListModel) this.getModel();
+
+ Object element = listModel.getElementAt(this.getSelectedIndex());
+
+ if (element instanceof String) {
+ if (listModel.isDateClosed(element)) {
+ listModel.openDate(element);
+ } else {
+ listModel.closeDate(element);
+ }
+ }
+ }
+ }
+
+ public void mouseEntered(MouseEvent e)
+ {}
+
+ public void mouseExited(MouseEvent e)
+ {}
+
+ public void mousePressed(MouseEvent e)
+ {}
+
+ public void mouseReleased(MouseEvent e)
+ {}
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallListCellRenderer.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallListCellRenderer.java
new file mode 100644
index 000000000..c77530fad
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallListCellRenderer.java
@@ -0,0 +1,211 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+/**
+ * The ContactListCellRenderer is the custom cell renderer used in
+ * the SIP-Communicator's ContactList. It extends JPanel instead of
+ * JLabel, which allows adding different buttons and icons to the contact cell.
+ * The cell border and background are repainted.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallListCellRenderer
+ extends JPanel
+ implements ListCellRenderer
+{
+
+ private JPanel dataPanel = new JPanel(new BorderLayout());
+
+ private JLabel nameLabel = new JLabel();
+
+ private JPanel timePanel = new JPanel(new BorderLayout(8, 0));
+
+ private JLabel timeLabel = new JLabel();
+
+ private JLabel durationLabel = new JLabel();
+
+ private JLabel iconLabel = new JLabel();
+
+ private Icon incomingIcon = Resources.getImage("incomingCall");
+
+ private Icon outgoingIcon = Resources.getImage("outgoingCall");
+
+ private boolean isSelected = false;
+
+ private boolean isLeaf = true;
+
+ private String direction;
+
+ /**
+ * Initialize the panel containing the node.
+ */
+ public CallListCellRenderer()
+ {
+
+ super(new BorderLayout(5, 5));
+
+ this.setBackground(Color.WHITE);
+
+ this.setOpaque(true);
+
+ this.dataPanel.setOpaque(false);
+
+ this.timePanel.setOpaque(false);
+
+ this.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
+
+ this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+
+ this.nameLabel.setIconTextGap(2);
+
+ this.nameLabel.setFont(this.getFont().deriveFont(Font.BOLD));
+
+ this.dataPanel.add(nameLabel, BorderLayout.WEST);
+
+ this.add(dataPanel, BorderLayout.CENTER);
+ }
+
+ /**
+ * Implements the ListCellRenderer method.
+ *
+ * Returns this panel that has been configured to display the meta contact
+ * and meta contact group cells.
+ */
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected, boolean cellHasFocus)
+ {
+
+ this.dataPanel.remove(timePanel);
+ this.dataPanel.remove(timeLabel);
+ this.dataPanel.remove(durationLabel);
+ this.remove(iconLabel);
+
+ if (value instanceof GuiCallParticipantRecord)
+ {
+
+ GuiCallParticipantRecord participant = (GuiCallParticipantRecord) value;
+
+ this.direction = participant.getDirection();
+
+ if (direction.equals(GuiCallParticipantRecord.INCOMING_CALL))
+ iconLabel.setIcon(incomingIcon);
+ else
+ iconLabel.setIcon(outgoingIcon);
+
+ this.nameLabel.setText(participant.getParticipantName());
+
+ this.timeLabel.setText(Resources.getString("at") + " "
+ + GuiUtils.formatTime(participant.getStartTime()));
+
+ this.durationLabel.setText(Resources.getString("duration") + " "
+ + GuiUtils.formatTime(participant.getCallTime()));
+
+ // this.nameLabel.setIcon(listModel
+ // .getMetaContactStatusIcon(contactItem));
+
+ this.timePanel.add(timeLabel, BorderLayout.WEST);
+ this.timePanel.add(durationLabel, BorderLayout.EAST);
+ this.dataPanel.add(timePanel, BorderLayout.EAST);
+
+ this.add(iconLabel, BorderLayout.WEST);
+
+ this.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
+
+ // We should set the bounds of the cell explicitely in order to
+ // make getComponentAt work properly.
+ this.setBounds(0, 0, list.getWidth() - 2, 25);
+
+ this.isLeaf = true;
+ }
+ else if (value instanceof String)
+ {
+
+ String dateString = (String) value;
+
+ this.nameLabel.setText(dateString);
+
+ this.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+
+ // We should set the bounds of the cell explicitely in order to
+ // make getComponentAt work properly.
+ this.setBounds(0, 0, list.getWidth() - 2, 20);
+
+ this.isLeaf = false;
+ }
+
+ this.isSelected = isSelected;
+
+ return this;
+ }
+
+ /**
+ * Paint a background for all groups and a round blue border and background
+ * when a cell is selected.
+ */
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+
+ Graphics2D g2 = (Graphics2D) g;
+
+ AntialiasingManager.activateAntialiasing(g2);
+
+ if (!this.isLeaf)
+ {
+
+ GradientPaint p = new GradientPaint(0, 0,
+ Constants.BLUE_GRAY_BORDER_COLOR, this.getWidth(), this
+ .getHeight(), Constants.MOVER_END_COLOR);
+
+ g2.setPaint(p);
+ g2.fillRoundRect(1, 1, this.getWidth(), this.getHeight() - 1, 7, 7);
+ }
+ else
+ {
+ if (direction.equals(GuiCallParticipantRecord.INCOMING_CALL))
+ {
+
+ GradientPaint p = new GradientPaint(0, 0,
+ Constants.HISTORY_IN_CALL_COLOR, this.getWidth(), this
+ .getHeight(), Constants.MOVER_END_COLOR);
+
+ g2.setPaint(p);
+ g2.fillRoundRect(1, 1, this.getWidth(), this.getHeight() - 1,
+ 7, 7);
+ }
+ else if (direction.equals(GuiCallParticipantRecord.OUTGOING_CALL))
+ {
+
+ GradientPaint p = new GradientPaint(0, 0,
+ Constants.HISTORY_OUT_CALL_COLOR, this.getWidth(), this
+ .getHeight(), Constants.MOVER_END_COLOR);
+
+ g2.setPaint(p);
+ g2.fillRoundRect(1, 1, this.getWidth(), this.getHeight() - 1,
+ 7, 7);
+ }
+ }
+
+ if (this.isSelected)
+ {
+
+ g2.setColor(Constants.SELECTED_END_COLOR);
+ g2.fillRoundRect(1, 0, this.getWidth(), this.getHeight(), 7, 7);
+
+ g2.setColor(Constants.BLUE_GRAY_BORDER_DARKER_COLOR);
+ g2.setStroke(new BasicStroke(1.5f));
+ g2.drawRoundRect(1, 0, this.getWidth() - 2, this.getHeight() - 1,
+ 7, 7);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallListModel.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallListModel.java
new file mode 100644
index 000000000..4a7d5d936
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/CallListModel.java
@@ -0,0 +1,156 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.util.*;
+
+import javax.swing.*;
+
+/**
+ * The data model of the Call list.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallListModel extends AbstractListModel
+{
+ private LinkedList callList = new LinkedList();
+
+ private Hashtable closedDates = new Hashtable();
+
+ /**
+ * Closes the given date by hiding all containing calls.
+ *
+ * @param date The date to close.
+ */
+ public void closeDate(Object date)
+ {
+ int startIndex = this.indexOf(date);
+ int endIndex = startIndex;
+ int currentSize = getSize();
+ Collection c = new ArrayList();
+
+ for(int i = startIndex + 1; i < currentSize; i ++) {
+ Object o = this.getElementAt(i);
+
+ if(o instanceof GuiCallParticipantRecord) {
+ this.closedDates.put(o, date);
+ c.add(o);
+ endIndex++;
+ }
+ else
+ break;
+ }
+ removeAll(c);
+ fireIntervalRemoved(this, startIndex, endIndex);
+ }
+
+ /**
+ * Opens the given date by showing all containing calls.
+ *
+ * @param date The date to open.
+ */
+ public void openDate(Object date)
+ {
+ int startIndex = this.indexOf(date);
+ int endIndex = startIndex;
+ Hashtable closedDatesCopy = (Hashtable)closedDates.clone();
+
+ if(closedDatesCopy.containsValue(date)) {
+ Iterator dates = closedDatesCopy.entrySet().iterator();
+
+ while(dates.hasNext()) {
+ Map.Entry entry = (Map.Entry)dates.next();
+ Object callRecord = entry.getKey();
+ Object callDate = entry.getValue();
+
+ if(callDate.equals(date)) {
+ endIndex++;
+ closedDates.remove(callRecord);
+ this.addElement(endIndex, callRecord);
+ }
+ }
+ }
+ fireIntervalAdded(this, startIndex, endIndex);
+ }
+
+ /**
+ * Checks whether the given date is closed.
+ *
+ * @param date The date to check.
+ * @return True if the date is closed, false - otherwise.
+ */
+ public boolean isDateClosed(Object date) {
+ if (this.closedDates.containsValue(date))
+ return true;
+ else
+ return false;
+ }
+
+ public int getSize()
+ {
+ return callList.size();
+ }
+
+ public Object getElementAt(int index)
+ {
+ if (index>=0)
+ return callList.get(index);
+ else
+ return null;
+ }
+
+ public void addElement(Object item)
+ {
+ synchronized (callList) {
+ this.callList.add(item);
+
+ int index = callList.indexOf(item);
+ fireIntervalAdded(this, index, index);
+ }
+ }
+
+ public void addElement(int index, Object item)
+ {
+ synchronized (callList) {
+ this.callList.add(index, item);
+ fireIntervalAdded(this, index, index);
+ }
+ }
+
+ public void removeElement(Object item)
+ {
+ synchronized (callList) {
+ this.callList.remove(item);
+ }
+ }
+
+ public void removeAll(Collection c)
+ {
+ synchronized (callList) {
+ callList.removeAll(c);
+ }
+ }
+
+ public void removeAll()
+ {
+ int currentSize = getSize();
+
+ while(callList.size() > 0) {
+ synchronized (callList) {
+ callList.removeLast();
+ }
+ }
+ fireIntervalRemoved(this, 0, currentSize);
+ }
+
+ public int indexOf(Object item)
+ {
+ synchronized (callList) {
+ return callList.indexOf(item);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/Constants.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/Constants.java
new file mode 100644
index 000000000..a3295ce58
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/Constants.java
@@ -0,0 +1,137 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.awt.*;
+
+import net.java.sip.communicator.util.*;
+
+/**
+ * All look and feel related constants are stored here.
+ *
+ * @author Yana Stamcheva
+ */
+
+public class Constants {
+
+ private static Logger logger = Logger.getLogger(Constants.class);
+
+ /*
+ * ===================================================================
+ * ---------------------- CALLTYPE CONSTANTS -------------------------
+ * ===================================================================
+ */
+
+ /**
+ * The incoming call flag.
+ */
+ public static final int INCOMING_CALL = 1;
+
+ /**
+ * The outgoing call flag.
+ */
+ public static final int OUTGOING_CALL = 2;
+
+ /**
+ * The Incoming & outcoming flag.
+ */
+ public static final int INOUT_CALL = 3;
+
+ /*
+ * ======================================================================
+ * -------------------- FONTS AND COLOR CONSTANTS ------------------------
+ * ======================================================================
+ */
+
+ /**
+ * The color used to paint the background of an incoming call history
+ * record.
+ */
+ public static final Color HISTORY_DATE_COLOR
+ = new Color(255, 201, 102);
+
+ /**
+ * The color used to paint the background of an incoming call history
+ * record.
+ */
+ public static final Color HISTORY_IN_CALL_COLOR
+ = new Color(249, 255, 197);
+
+ /**
+ * The color used to paint the background of an outgoing call history
+ * record.
+ */
+ public static final Color HISTORY_OUT_CALL_COLOR
+ = new Color(243, 244, 247);
+
+
+ /**
+ * The end color used to paint a gradient selected background of some
+ * components.
+ */
+ public static final Color SELECTED_END_COLOR
+ = new Color(209, 212, 225);
+
+ /**
+ * The start color used to paint a gradient mouse over background of some
+ * components.
+ */
+ public static final Color MOVER_START_COLOR = new Color(230,
+ 230, 230);
+
+ /**
+ * The end color used to paint a gradient mouse over background of some
+ * components.
+ */
+ public static final Color MOVER_END_COLOR = new Color(255,
+ 255, 255);
+
+ /**
+ * Gray color used to paint some borders, like the button border for
+ * example.
+ */
+ public static final Color GRAY_COLOR = new Color(154, 154,
+ 154);
+
+ /**
+ * A color between blue and gray used to paint some borders.
+ */
+ public static final Color BLUE_GRAY_BORDER_COLOR = new Color(142, 160, 188);
+
+ /**
+ * A color between blue and gray (darker than the other one), used to paint
+ * some borders.
+ */
+ public static final Color BLUE_GRAY_BORDER_DARKER_COLOR = new Color(131, 149,
+ 178);
+
+
+ /*
+ * ======================================================================
+ * --------------------------- FONT CONSTANTS ---------------------------
+ * ======================================================================
+ */
+
+ /**
+ * The name of the font used in this ui implementation.
+ */
+ public static final String FONT_NAME = "Verdana";
+
+ /**
+ * The size of the font used in this ui implementation.
+ */
+ public static final String FONT_SIZE = "12";
+
+ /**
+ * The default Font object used through this ui implementation.
+ */
+ public static final Font FONT = new Font(Constants.FONT_NAME, Font.PLAIN,
+ new Integer(Constants.FONT_SIZE).intValue());
+
+
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchActivator.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchActivator.java
new file mode 100644
index 000000000..80ccb9753
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchActivator.java
@@ -0,0 +1,61 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import net.java.sip.communicator.service.callhistory.*;
+import net.java.sip.communicator.service.gui.*;
+
+import org.osgi.framework.*;
+
+/**
+ * Call History Search PlugIn Activator
+ *
+ * @author Bourdon Maxime & Meyer Thomas
+ */
+public class ExtendedCallHistorySearchActivator
+ implements BundleActivator
+{
+ private static BundleContext context;
+
+ public void start(BundleContext bc) throws Exception
+ {
+ context = bc;
+ ServiceReference uiServiceRef = bc.getServiceReference(
+ UIService.class.getName());
+
+ UIService uiService = (UIService) bc.getService(uiServiceRef);
+
+ if (uiService.isContainerSupported(UIService.CONTAINER_TOOLS_MENU))
+ {
+ ExtendedCallHistorySearchItem extendedSearch
+ = new ExtendedCallHistorySearchItem();
+
+ uiService.addComponent(UIService.CONTAINER_TOOLS_MENU,
+ extendedSearch);
+ }
+ }
+
+ public void stop(BundleContext bc) throws Exception
+ {
+ }
+
+ /**
+ * Returns an instance of the CallHistoryService.
+ */
+ public static CallHistoryService getCallHistoryService()
+ {
+ ServiceReference callHistoryServiceRef = context
+ .getServiceReference(CallHistoryService.class.getName());
+
+ CallHistoryService callHistoryService = (CallHistoryService) context
+ .getService(callHistoryServiceRef);
+
+ return callHistoryService;
+ }
+
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchDialog.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchDialog.java
new file mode 100644
index 000000000..f6ac0f640
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchDialog.java
@@ -0,0 +1,477 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.service.callhistory.*;
+
+import com.toedter.calendar.*;
+
+/**
+ * The ExtendedCallHistorySearchDialog allows to search in call
+ * history records, by specifying a period, or a call participant name, or type
+ * of the call (incoming or outgoing).
+ *
+ * @author Maxime Bourdon & Thomas Meyer
+ */
+public class ExtendedCallHistorySearchDialog
+ extends JDialog
+ implements ActionListener,
+ ItemListener
+{
+ /* PANEL */
+ private JPanel mainSearchPanel = new JPanel(new BorderLayout());
+
+ private JPanel mainPanel = new JPanel(new BorderLayout(3, 1));
+
+ private JPanel searchPanel = new JPanel(new GridBagLayout());
+
+ private JPanel callTypePanel = new JPanel(new GridBagLayout());
+
+ private JPanel callListResultPanel = new JPanel(new BorderLayout());
+
+ /* BUTTON */
+ private JButton searchButton = new JButton(Resources.getString("search"),
+ Resources.getImage("searchIcon"));
+
+ /* TEXT FIELD */
+ private JTextField contactNameField = new JTextField();
+
+ /* LABEL */
+ private JLabel contactNameLabel = new JLabel(Resources
+ .getString("contactName") + ": ");
+
+ private JLabel sinceDateLabel
+ = new JLabel(Resources.getString("since") + ": ");
+
+ private JLabel untilDateLabel
+ = new JLabel(Resources.getString("until") + ": ");
+
+ private JLabel callTypeLabel
+ = new JLabel(Resources.getString("callType") + ": ");
+
+ /* CHECKBOX */
+ private JCheckBox inCheckBox = new JCheckBox(Resources
+ .getString("incoming"), true);
+
+ private JCheckBox outCheckBox = new JCheckBox(Resources
+ .getString("outgoing"), true);
+
+ /* SCROLL PANE */
+ private JScrollPane scrollPane = new JScrollPane();
+
+ /* contraint grid */
+ private GridBagConstraints constraintsGRbag = new GridBagConstraints();
+
+ Collection participants = null;
+
+ private CallList callList = new CallList();
+
+ /* Service */
+ CallHistoryService callAccessService;
+
+ private Date lastDateFromHistory = null;
+
+ private Collection callListCollection;
+
+ private JDateChooser untilDC
+ = new JDateChooser("dd/MM/yyyy", "##/##/####", ' ');
+
+ private JDateChooser sinceDC
+ = new JDateChooser("dd/MM/yyyy", "##/##/####", ' ');
+
+ private int direction;
+
+ /**
+ * Creates a new instance of ExtendedCallHistorySearchDialog.
+ */
+ public ExtendedCallHistorySearchDialog()
+ {
+ this.mainPanel.setPreferredSize(new Dimension(650, 550));
+
+ this.setTitle(Resources.getString("advancedCallHistorySearch"));
+
+ this.initPanels();
+
+ this.initDateChooser();
+
+ this.searchPanel.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createTitledBorder("Search"), BorderFactory
+ .createEmptyBorder(5, 5, 5, 5)));
+
+ this.callTypePanel.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createTitledBorder(""), BorderFactory
+ .createEmptyBorder(5, 5, 5, 5)));
+
+ this.callListResultPanel.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createTitledBorder(""), BorderFactory
+ .createEmptyBorder(5, 5, 5, 5)));
+
+ this.getContentPane().add(mainPanel);
+ this.pack();
+
+ /* action listener */
+ searchButton.addActionListener(this);
+ inCheckBox.addItemListener(this);
+ outCheckBox.addItemListener(this);
+
+ this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ }
+
+ /**
+ * Loads all calls from history and shows them in this dialog.
+ */
+ public void loadHistoryCalls()
+ {
+ /* get the call list collection */
+ callAccessService = ExtendedCallHistorySearchActivator
+ .getCallHistoryService();
+ callListCollection = callAccessService.findByEndDate(new Date());
+ loadTableRecords(
+ callListCollection, Constants.INOUT_CALL, null, new Date());
+ }
+
+ /**
+ * Initialize the "until" date field to the current date.
+ */
+ private void initDateChooser()
+ {
+ untilDC.getJCalendar().setWeekOfYearVisible(false);
+ untilDC.getJCalendar().setDate(new Date());
+ sinceDC.getJCalendar().setWeekOfYearVisible(false);
+ }
+
+ /**
+ * Init panels display.
+ */
+ private void initPanels()
+ {
+ this.getRootPane().setDefaultButton(searchButton);
+
+ this.mainSearchPanel.add(searchPanel, BorderLayout.NORTH);
+ this.mainSearchPanel.add(callTypePanel, BorderLayout.CENTER);
+
+ this.mainPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+ this.mainPanel.add(mainSearchPanel, BorderLayout.NORTH);
+ this.mainPanel.add(callListResultPanel, BorderLayout.CENTER);
+
+ /* SEARCH PANEL */
+ constraintsGRbag.anchor = GridBagConstraints.WEST;
+ constraintsGRbag.insets = new Insets(5, 5, 5, 5);
+ constraintsGRbag.gridwidth = 1;
+ constraintsGRbag.fill = GridBagConstraints.NONE;
+ this.searchPanel.add(contactNameLabel, constraintsGRbag);
+
+ constraintsGRbag.gridwidth = GridBagConstraints.REMAINDER;
+ constraintsGRbag.fill = GridBagConstraints.HORIZONTAL;
+ constraintsGRbag.weightx = 1.0;
+ constraintsGRbag.gridx = GridBagConstraints.RELATIVE;
+ this.searchPanel.add(contactNameField, constraintsGRbag);
+
+ /* DATE */
+ constraintsGRbag.anchor = GridBagConstraints.WEST;
+ constraintsGRbag.gridwidth = 1;
+ constraintsGRbag.gridy = 2;
+ constraintsGRbag.fill = GridBagConstraints.NONE;
+ this.searchPanel.add(sinceDateLabel, constraintsGRbag);
+
+ constraintsGRbag.gridwidth = GridBagConstraints.RELATIVE;
+ constraintsGRbag.fill = GridBagConstraints.HORIZONTAL;
+ constraintsGRbag.weightx = 1.0;
+ this.searchPanel.add(sinceDC, constraintsGRbag);
+
+ constraintsGRbag.gridy = 3;
+ constraintsGRbag.gridwidth = 1;
+ constraintsGRbag.fill = GridBagConstraints.NONE;
+ this.searchPanel.add(untilDateLabel, constraintsGRbag);
+
+ constraintsGRbag.fill = GridBagConstraints.HORIZONTAL;
+ this.searchPanel.add(untilDC, constraintsGRbag);
+
+ /* BUTTON */
+ constraintsGRbag.gridy = 4;
+ constraintsGRbag.gridx = 3;
+ constraintsGRbag.fill = GridBagConstraints.NONE;
+ constraintsGRbag.anchor = GridBagConstraints.EAST;
+ this.searchPanel.add(searchButton, constraintsGRbag);
+
+ /* CALL TYPE */
+ constraintsGRbag.anchor = GridBagConstraints.WEST;
+ constraintsGRbag.insets = new Insets(5, 5, 5, 5);
+ constraintsGRbag.gridwidth = 1;
+ constraintsGRbag.gridx = 1;
+ constraintsGRbag.gridy = 1;
+ this.callTypePanel.add(callTypeLabel, constraintsGRbag);
+ constraintsGRbag.gridx = 2;
+ this.callTypePanel.add(inCheckBox, constraintsGRbag);
+ constraintsGRbag.gridx = 3;
+ this.callTypePanel.add(outCheckBox, constraintsGRbag);
+
+ /* CALL LIST PANEL */
+ this.scrollPane.getViewport().add(callList);
+ this.callListResultPanel.add(scrollPane, BorderLayout.CENTER);
+ }
+
+ /**
+ * Loads the appropriate history calls when user clicks on the search
+ * button.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JButton sourceButton = (JButton) e.getSource();
+
+ if (sourceButton.equals(searchButton))
+ {
+ /* update the callList */
+ new Thread()
+ {
+ public void run()
+ {
+ callListCollection = callAccessService
+ .findByEndDate(new Date());
+
+ if (inCheckBox.isSelected() && outCheckBox.isSelected())
+ {
+ direction = Constants.INOUT_CALL;
+ }
+ else
+ {
+ if (inCheckBox.isSelected())
+ direction = Constants.INCOMING_CALL;
+ else
+ {
+ if (outCheckBox.isSelected())
+ direction = Constants.OUTGOING_CALL;
+ else
+ direction = Constants.INOUT_CALL;
+ }
+ }
+
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ loadTableRecords(callListCollection, direction,
+ sinceDC.getDate(), untilDC.getDate());
+ }
+ });
+ }
+ }.start();
+ }
+ }
+
+ /**
+ * Remove or add calls to the list of calls depending on the state of
+ * incoming and outgoing checkboxes.
+ */
+ public void itemStateChanged(ItemEvent e)
+ {
+ JCheckBox sourceCheckBox = (JCheckBox) e.getSource();
+
+ /* Incoming checkbox */
+ if (sourceCheckBox.equals(inCheckBox)
+ || sourceCheckBox.equals(outCheckBox))
+ {
+ /* INCOMING box Checked */
+ if (inCheckBox.isSelected())
+ {
+ /* OUTCOMING box checked */
+ if (outCheckBox.isSelected() == true)
+ {
+ loadTableRecords(callListCollection,
+ Constants.INOUT_CALL,
+ sinceDC.getDate(), untilDC.getDate());
+ }
+ // only incoming is checked
+ else
+ {
+ loadTableRecords(callListCollection,
+ Constants.INCOMING_CALL,
+ sinceDC.getDate(), untilDC.getDate());
+ }
+ }
+ /* check the OUTCOMING box */
+ else
+ {
+ // checked
+ if (outCheckBox.isSelected() == true)
+ {
+ loadTableRecords(callListCollection,
+ Constants.OUTGOING_CALL,
+ sinceDC.getDate(), untilDC.getDate());
+ }
+ /* both are unchecked */
+ else
+ loadTableRecords(callListCollection,
+ Constants.INOUT_CALL,
+ sinceDC.getDate(), untilDC.getDate());
+ }
+ }
+ }
+
+ private String processDate(Date date)
+ {
+ String resultString;
+ long currentDate = System.currentTimeMillis();
+
+ if (GuiUtils.compareDates(date, new Date(currentDate)) == 0)
+ {
+
+ resultString = Resources.getString("today");
+ }
+ else
+ {
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ resultString = GuiUtils.formatDate(date);
+ }
+
+ return resultString;
+ }
+
+ /**
+ * Check if the callRecord direction equals the direction wanted by the user
+ *
+ * @param callType integer value (incoming = 1, outgoing = 2, in/out = 3)
+ * @param callrecord A Callrecord
+ * @return A string containing the callRecord direction if it equals the
+ * callType Call, null if not
+ */
+ private String checkCallType(int callType, CallRecord callRecord)
+ {
+ String direction = null;
+
+ // in
+ if (callRecord.getDirection().equals(CallRecord.IN)
+ && ((callType == Constants.INCOMING_CALL)
+ || callType == Constants.INOUT_CALL))
+ direction = GuiCallParticipantRecord.INCOMING_CALL;
+ // out
+ else if (callRecord.getDirection().equals(CallRecord.OUT)
+ && (callType == Constants.OUTGOING_CALL
+ || callType == Constants.INOUT_CALL))
+ direction = GuiCallParticipantRecord.OUTGOING_CALL;
+
+ return direction;
+ }
+
+ /**
+ * Check if sinceDate <= callStartDate <= beforeDate
+ *
+ * @param callStartDate
+ * @param sinceDate
+ * @param beforeDate
+ * @return true if sinceDate <= callStartDate <= beforeDate false if not
+ */
+ private boolean checkDate(Date callStartDate, Date sinceDate, Date untilDate)
+ {
+ /* Test callStartDate >= sinceDate */
+ if (sinceDate != null)
+ if (callStartDate.before(sinceDate))
+ return false;
+
+ /* Test callStartDate <= beforeDate */
+ if (untilDate != null)
+ if (callStartDate.after(untilDate))
+ return false;
+
+ /* sinceDate <= callStartDate <= beforeDate */
+ return true;
+ }
+
+ /**
+ * Loads the collection of call records in the table.
+ *
+ * @param historyCalls the collection of call records
+ * @param calltype the type of the call - could be incoming or outgoing
+ * @param since the start date of the search
+ * @param before the end date of the search
+ */
+ private void loadTableRecords(Collection historyCalls, int calltype,
+ Date since, Date before)
+ {
+ boolean addMe = true;
+ lastDateFromHistory = null;
+ callList.removeAll();
+ // callList = new CallList();
+ Iterator lastCalls = historyCalls.iterator();
+
+ while (lastCalls.hasNext())
+ {
+ addMe = true;
+
+ CallRecord callRecord = (CallRecord) lastCalls.next();
+
+ /* DATE Checking */
+ Date callStartDate = callRecord.getStartTime();
+
+ if (checkDate(callStartDate, since, before))
+ {
+ if (lastDateFromHistory == null)
+ {
+ callList.addItem(processDate(callStartDate));
+ lastDateFromHistory = callStartDate;
+ }
+ else
+ {
+ int compareResult = GuiUtils.compareDates(callStartDate,
+ lastDateFromHistory);
+
+ if (compareResult != 0)
+ {
+ callList.addItem(processDate(callStartDate));
+ lastDateFromHistory = callStartDate;
+ }
+ }
+ }
+ else
+ addMe = false;
+
+ /* PARTICIPANTS Checking */
+ if (addMe)
+ {
+ Iterator participants = callRecord.getParticipantRecords()
+ .iterator();
+
+ while (participants.hasNext() && addMe)
+ {
+ CallParticipantRecord participantRecord
+ = (CallParticipantRecord) participants.next();
+
+ String participantName = participantRecord
+ .getParticipantAddress();
+
+ if (participantName.matches(
+ "(?i).*" + contactNameField.getText() + ".*"))
+ {
+ /* DIRECTION Checking */
+ String direction;
+ direction = checkCallType(calltype, callRecord);
+
+ if (direction != null)
+ callList.addItem(new GuiCallParticipantRecord(
+ participantRecord, direction));
+ else
+ addMe = false;
+ }
+ else
+ addMe = false; // useless
+ }
+ }
+ }
+
+ if (callList.getModel().getSize() > 0)
+ callList.addItem(Resources.getString("olderCalls") + "...");
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchItem.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchItem.java
new file mode 100644
index 000000000..af097ed7b
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/ExtendedCallHistorySearchItem.java
@@ -0,0 +1,59 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+/**
+ * The ExtendedCallHistorySearchButton is the button that will be
+ * added in the Call List panel and from which the user would be able to access
+ * the ExtendCallHistorySearchDialog.
+ *
+ * @author Bourdon Maxime & Meyer Thomas
+ */
+public class ExtendedCallHistorySearchItem
+ extends JMenuItem
+ implements
+ ActionListener
+{
+ private ExtendedCallHistorySearchDialog callHistorySearchDialog = null;
+
+ /**
+ * Creates an instance of ExtendedCallHistoryButton.
+ */
+ public ExtendedCallHistorySearchItem()
+ {
+ super(Resources.getString("advancedCallHistorySearch"));
+
+ this.setMnemonic(Resources.getMnemonic("advancedCallHistorySearch"));
+ this.addActionListener(this);
+ }
+
+ /**
+ * Launches the extended call history dialog when user clicks on this button.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (callHistorySearchDialog == null)
+ {
+ callHistorySearchDialog = new ExtendedCallHistorySearchDialog();
+ callHistorySearchDialog.setLocation(Toolkit.getDefaultToolkit()
+ .getScreenSize().width
+ / 2 - callHistorySearchDialog.getWidth() / 2, Toolkit
+ .getDefaultToolkit().getScreenSize().height
+ / 2 - callHistorySearchDialog.getHeight() / 2);
+ }
+
+ callHistorySearchDialog.loadHistoryCalls();
+
+ callHistorySearchDialog.setVisible(true);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/GuiCallParticipantRecord.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/GuiCallParticipantRecord.java
new file mode 100644
index 000000000..361ef8ced
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/GuiCallParticipantRecord.java
@@ -0,0 +1,119 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.callhistory.*;
+
+/**
+ * The GuiCallParticipantRecord is meant to be used in the call history
+ * to represent a history call participant record. It wraps a
+ * CallParticipant or a CallParticipantRecord object.
+ *
+ * @author Yana Stamcheva
+ */
+public class GuiCallParticipantRecord
+{
+ public static final String INCOMING_CALL = "IncomingCall";
+
+ public static final String OUTGOING_CALL = "OutgoingCall";
+
+ private String direction;
+
+ private String participantName;
+
+ private Date startTime;
+
+ private Date callTime;
+
+ /**
+ * Creates an instance of GuiCallParticipantRecord by specifying
+ * the participant name, the call direction (incoming or outgoing), the
+ * time at which the call has started and the duration of the call.
+ *
+ * @param participantName the name of the call participant
+ * @param direction the direction of the call - INCOMING_CALL
+ * or OUTGOING_CALL
+ * @param startTime the time at which the call has started
+ * @param callTime the duration of the call
+ */
+ public GuiCallParticipantRecord(String participantName,
+ String direction,
+ Date startTime,
+ Date callTime)
+ {
+ this.direction = direction;
+
+ this.participantName = participantName;
+
+ this.startTime = startTime;
+
+ this.callTime = callTime;
+ }
+
+ /**
+ * Creates an instance of GuiCallParticipantRecord by specifying
+ * the corresponding CallParticipantRecord, which gives all the
+ * information for the participant and the call duration.
+ *
+ * @param participantRecord the corresponding CallParticipantRecord
+ * @param direction the call direction - INCOMING_CALL or OUTGOING_CALL
+ */
+ public GuiCallParticipantRecord(CallParticipantRecord participantRecord,
+ String direction)
+ {
+ this.direction = direction;
+
+ this.participantName = participantRecord.getParticipantAddress();
+
+ this.startTime = participantRecord.getStartTime();
+
+ this.callTime = GuiUtils.substractDates(
+ participantRecord.getEndTime(), startTime);
+ }
+
+ /**
+ * Returns the call direction - INCOMING_CALL or OUTGOING_CALL.
+ *
+ * @return the call direction - INCOMING_CALL or OUTGOING_CALL.
+ */
+ public String getDirection()
+ {
+ return direction;
+ }
+
+ /**
+ * Returns the duration of the call.
+ *
+ * @return the duration of the call
+ */
+ public Date getCallTime()
+ {
+ return callTime;
+ }
+
+ /**
+ * Returns the name of the participant.
+ *
+ * @return the name of the participant
+ */
+ public String getParticipantName()
+ {
+ return participantName;
+ }
+
+ /**
+ * Returns the time at which the call has started.
+ *
+ * @return the time at which the call has started
+ */
+ public Date getStartTime()
+ {
+ return startTime;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/GuiUtils.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/GuiUtils.java
new file mode 100644
index 000000000..4b9b90e0f
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/GuiUtils.java
@@ -0,0 +1,205 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.awt.*;
+import java.util.*;
+
+import javax.swing.*;
+
+
+/**
+ * The StringUtils class is used through this ui implementation for
+ * some special operations with strings.
+ *
+ * @author Yana Stamcheva
+ */
+public class GuiUtils {
+
+ private static Calendar c1 = Calendar.getInstance();
+
+ private static Calendar c2 = Calendar.getInstance();
+
+ /**
+ * Replaces some chars that are special in a regular expression.
+ * @param text The initial text.
+ * @return the formatted text
+ */
+ public static String replaceSpecialRegExpChars(String text) {
+ return text.replaceAll("([.()^&$*|])", "\\\\$1");
+ }
+
+ /**
+ * Returns the width in pixels of a text.
+ * @param c the component where the text is contained
+ * @param text the text to measure
+ * @return the width in pixels of a text.
+ */
+ public static int getStringWidth(Component c, String text) {
+ return SwingUtilities.computeStringWidth(c
+ .getFontMetrics(Constants.FONT), text);
+ }
+
+ /**
+ * Compares the two dates. The comparison is based only on the day, month
+ * and year values. Returns 0 if the two dates are equals, a value < 0 if
+ * the first date is before the second one and > 0 if the first date is after
+ * the second one.
+ * @param date1 the first date to compare
+ * @param date2 the second date to compare with
+ * @return Returns 0 if the two dates are equals, a value < 0 if
+ * the first date is before the second one and > 0 if the first date is after
+ * the second one
+ */
+ public static int compareDates(Date date1, Date date2)
+ {
+ c1.setTime(date1);
+ c2.setTime(date2);
+
+ int day1 = c1.get(Calendar.DAY_OF_MONTH);
+ int month1 = c1.get(Calendar.MONTH);
+ int year1 = c1.get(Calendar.YEAR);
+
+ int day2 = c2.get(Calendar.DAY_OF_MONTH);
+ int month2 = c2.get(Calendar.MONTH);
+ int year2 = c2.get(Calendar.YEAR);
+
+ if((day1 == day2)
+ && (month1 == month2)
+ && (year1 == year2)) {
+
+ return 0;
+ }
+ else if((day1 < day2)
+ && (month1 <= month2)
+ && (year1 <= year2)) {
+
+ return -1;
+ }
+ else {
+ return 1;
+ }
+ }
+
+ /**
+ * Formats the given date. The result format is the following:
+ * [Month] [Day], [Year]. For example: Dec 24, 2000.
+ * @param date the date to format
+ * @return the formatted date string
+ */
+ public static String formatDate(Date date)
+ {
+ c1.setTime(date);
+
+ return GuiUtils.processMonth(c1.get(Calendar.MONTH) + 1) + " "
+ + GuiUtils.formatTime(c1.get(Calendar.DAY_OF_MONTH)) + ", "
+ + GuiUtils.formatTime(c1.get(Calendar.YEAR));
+ }
+
+ /**
+ * Formats the time for the given date. The result format is the following:
+ * [Hour]:[Minute]:[Second]. For example: 12:25:30.
+ * @param date the date to format
+ * @return the formatted hour string
+ */
+ public static String formatTime(Date date)
+ {
+ c1.setTime(date);
+
+ return GuiUtils.formatTime(c1.get(Calendar.HOUR_OF_DAY)) + ":"
+ + GuiUtils.formatTime(c1.get(Calendar.MINUTE)) + ":"
+ + GuiUtils.formatTime(c1.get(Calendar.SECOND)) ;
+ }
+
+ /**
+ * Substracts the two dates.
+ * @param date1 the first date argument
+ * @param date2 the second date argument
+ * @return the date resulted from the substracting
+ */
+ public static Date substractDates(Date date1, Date date2)
+ {
+ long d1 = date1.getTime();
+ long d2 = date2.getTime();
+ long difMil = d1-d2;
+ long milPerDay = 1000*60*60*24;
+ long milPerHour = 1000*60*60;
+ long milPerMin = 1000*60;
+ long milPerSec = 1000;
+
+ long days = difMil / milPerDay;
+ int hour = (int)(( difMil - days*milPerDay ) / milPerHour);
+ int min
+ = (int)(( difMil - days*milPerDay - hour*milPerHour ) / milPerMin);
+ int sec
+ = (int)(( difMil - days*milPerDay - hour*milPerHour - min*milPerMin )
+ / milPerSec);
+
+ c1.clear();
+ c1.set(Calendar.HOUR, hour);
+ c1.set(Calendar.MINUTE, min);
+ c1.set(Calendar.SECOND, sec);
+
+ return c1.getTime();
+ }
+
+ /**
+ * Replaces the month with its abbreviation.
+ * @param month Value from 1 to 12, which indicates the month.
+ * @return the corresponding month abbreviation
+ */
+ private static String processMonth(int month)
+ {
+ String monthString = "";
+ if(month == 1)
+ monthString = Resources.getString("january");
+ else if(month == 2)
+ monthString = Resources.getString("february");
+ else if(month == 3)
+ monthString = Resources.getString("march");
+ else if(month == 4)
+ monthString = Resources.getString("april");
+ else if(month == 5)
+ monthString = Resources.getString("may");
+ else if(month == 6)
+ monthString = Resources.getString("june");
+ else if(month == 7)
+ monthString = Resources.getString("july");
+ else if(month == 8)
+ monthString = Resources.getString("august");
+ else if(month == 9)
+ monthString = Resources.getString("september");
+ else if(month == 10)
+ monthString = Resources.getString("october");
+ else if(month == 11)
+ monthString = Resources.getString("november");
+ else if(month == 12)
+ monthString = Resources.getString("december");
+
+ return monthString;
+ }
+
+ /**
+ * Adds a 0 in the beginning of one digit numbers.
+ *
+ * @param time The time parameter could be hours, minutes or seconds.
+ * @return The formatted minutes string.
+ */
+ private static String formatTime(int time)
+ {
+ String timeString = new Integer(time).toString();
+
+ String resultString = "";
+ if (timeString.length() < 2)
+ resultString = resultString.concat("0").concat(timeString);
+ else
+ resultString = timeString;
+
+ return resultString;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/Resources.java b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/Resources.java
new file mode 100644
index 000000000..5e665afef
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/Resources.java
@@ -0,0 +1,129 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.plugin.extendedcallhistorysearch;
+
+import java.awt.image.*;
+import java.io.*;
+import java.util.*;
+
+import javax.imageio.*;
+import javax.swing.*;
+
+import net.java.sip.communicator.util.*;
+/**
+ * The Messages class manages the access to the internationalization
+ * properties files.
+ * @author Yana Stamcheva
+ */
+public class Resources {
+
+ private static Logger log = Logger.getLogger(Resources.class);
+
+ private static final String BUNDLE_NAME
+ = "net.java.sip.communicator.plugin.extendedcallhistorysearch.resources";
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
+ .getBundle(BUNDLE_NAME);
+
+ /**
+ * Returns an internationalized string corresponding to the given key.
+ * @param key The key of the string.
+ * @return An internationalized string corresponding to the given key.
+ */
+ public static String getString(String key)
+ {
+ String resourceString;
+ try
+ {
+ resourceString = RESOURCE_BUNDLE.getString(key);
+
+ int mnemonicIndex = resourceString.indexOf('&');
+
+ if(mnemonicIndex > -1)
+ {
+ String firstPart = resourceString.substring(0, mnemonicIndex);
+ String secondPart = resourceString.substring(mnemonicIndex + 1);
+
+ resourceString = firstPart.concat(secondPart);
+ }
+ }
+ catch (MissingResourceException e)
+ {
+ resourceString = '!' + key + '!';
+ }
+
+ return resourceString;
+ }
+
+ /**
+ * Loads an image from a given image identifier.
+ * @param imageID The identifier of the image.
+ * @return The image for the given identifier.
+ */
+ public static ImageIcon getImage(String imageID) {
+ BufferedImage image = null;
+
+ String path = Resources.getString(imageID);
+ try {
+ image = ImageIO.read(Resources.class.getClassLoader()
+ .getResourceAsStream(path));
+
+ } catch (IOException e) {
+ log.error("Failed to load image:" + path, e);
+ }
+
+ return new ImageIcon(image);
+ }
+
+ /**
+ * Returns an internationalized string corresponding to the given key.
+ * @param key The key of the string.
+ * @return An internationalized string corresponding to the given key.
+ */
+ public static char getMnemonic(String key)
+ {
+ String resourceString;
+ try {
+ resourceString = RESOURCE_BUNDLE.getString(key);
+
+ int mnemonicIndex = resourceString.indexOf('&');
+
+ if(mnemonicIndex > -1)
+ {
+ return resourceString.charAt(mnemonicIndex + 1);
+ }
+
+ }
+ catch (MissingResourceException e)
+ {
+ return '!';
+ }
+
+ return '!';
+ }
+
+ /**
+ * Loads an image from a given image identifier.
+ * @param imageID The identifier of the image.
+ * @return The image for the given identifier.
+ */
+ public static byte[] getImageInBytes(String imageID) {
+ byte[] image = new byte[100000];
+
+ String path = Resources.getString(imageID);
+ try {
+ Resources.class.getClassLoader()
+ .getResourceAsStream(path).read(image);
+
+ } catch (IOException e) {
+ log.error("Failed to load image:" + path, e);
+ }
+
+ return image;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/extendedcallhistorysearch.manifest.mf b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/extendedcallhistorysearch.manifest.mf
new file mode 100644
index 000000000..b73a13ff4
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/extendedcallhistorysearch.manifest.mf
@@ -0,0 +1,27 @@
+Bundle-Activator: net.java.sip.communicator.plugin.extendedcallhistorysearch.ExtendedCallHistorySearchActivator
+Bundle-Name: Extended Call History Search
+Bundle-Description: An Extended Call History Search
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+Import-Package: org.osgi.framework,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.service.contactlist,
+ net.java.sip.communicator.service.contactlist.event,
+ net.java.sip.communicator.service.gui,
+ net.java.sip.communicator.service.gui.event,
+ net.java.sip.communicator.service.callhistory,
+ net.java.sip.communicator.service.configuration,
+ javax.swing,
+ javax.swing.event,
+ javax.swing.table,
+ javax.swing.text,
+ javax.accessibility,
+ javax.swing.plaf,
+ javax.swing.plaf.metal,
+ javax.swing.plaf.basic,
+ javax.imageio,
+ javax.swing.filechooser,
+ javax.swing.tree,
+ javax.swing.border
+
+
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources.properties b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources.properties
new file mode 100644
index 000000000..017ff86f8
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources.properties
@@ -0,0 +1,31 @@
+advancedCallHistorySearch=&Advanced call history search
+search=Search
+contactName=Contact Name
+since=Since
+until=Until
+callType=Call Type
+incoming=Incoming
+outgoing=Outgoing
+olderCalls=Older Calls
+duration=Duration
+at=at
+all=all
+today=Today
+january=Jan
+february=Feb
+march=Mar
+april=Apr
+may=May
+june=Jun
+july=Jul
+august=Aug
+september=Sep
+october=Oct
+november=Nov
+december=Dec
+
+searchIcon=net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/searchIcon.png
+historyMenuIcon=net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/history16x16.png
+incomingCall=net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/incomingCall.png
+outgoingCall=net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/outgoingCall.png
+calendarIcon=net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/calendarIcon.png
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/calendarIcon.png b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/calendarIcon.png
new file mode 100644
index 000000000..9cc115093
Binary files /dev/null and b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/calendarIcon.png differ
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/history16x16.png b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/history16x16.png
new file mode 100644
index 000000000..67ba572f5
Binary files /dev/null and b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/history16x16.png differ
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/incomingCall.png b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/incomingCall.png
new file mode 100644
index 000000000..f50b7058d
Binary files /dev/null and b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/incomingCall.png differ
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/outgoingCall.png b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/outgoingCall.png
new file mode 100644
index 000000000..6b9b53b7a
Binary files /dev/null and b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/outgoingCall.png differ
diff --git a/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/searchIcon.png b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/searchIcon.png
new file mode 100644
index 000000000..4eb16a3f4
Binary files /dev/null and b/src/net/java/sip/communicator/plugin/extendedcallhistorysearch/resources/searchIcon.png differ