// This file is part of AceWiki.
// Copyright 2008-2013, AceWiki developers.
//
// AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU
// Lesser General Public License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// AceWiki 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with AceWiki. If
// not, see http://www.gnu.org/licenses/.
package ch.uzh.ifi.attempto.preditor;
import java.util.ArrayList;
import java.util.List;
import nextapp.echo.app.Column;
import nextapp.echo.app.Extent;
/**
* This class represents a column of menu blocks.
*
* @author Tobias Kuhn
*/
class MenuBlockColumn {
private PreditorWindow preditor;
private List<MenuBlockContent> contents = new ArrayList<MenuBlockContent>();
private int heightUnits, weight;
/**
* Creates a new menu block column.
*
* @param preditor The predictive editor object.
*/
private MenuBlockColumn(PreditorWindow preditor) {
this.preditor = preditor;
}
/**
* Creates a new menu block column with one menu block.
*
* @param content The content of the menu block.
* @param preditor The predictive editor object.
*/
public MenuBlockColumn(MenuBlockContent content, PreditorWindow preditor) {
this.preditor = preditor;
contents.add(content);
updateValues();
}
/**
* Includes the menu blocks of the other column at the bottom of this one.
*
* @param other The other menu block column.
*/
public void includeAtBottom(MenuBlockColumn other) {
this.contents.addAll(other.contents);
updateValues();
}
/**
* Includes the menu blocks of the other column at the top of this one.
*
* @param other The other menu block column.
*/
public void includeAtTop(MenuBlockColumn other) {
this.contents.addAll(0, other.contents);
updateValues();
}
/**
* Returns two menu block columns which contain the splitted content of this one. This object
* remains unchanged. The trend value indicates how to split odd numbers of menu blocks.
*
* @param trend If positive, odd numbers are split in a way that the first column has more menu
* block, and vice versa for negative values.
* @return Two menu blocks in an array.
*/
public MenuBlockColumn[] getSplitted(int trend) {
MenuBlockColumn c1 = new MenuBlockColumn(preditor);
MenuBlockColumn c2 = new MenuBlockColumn(preditor);
int threshold;
if (trend > 0) {
threshold = (int) ((contents.size() + 0.5) / 2);
} else {
threshold = contents.size() / 2;
}
for (int i=0 ; i < contents.size() ; i++) {
if (i < threshold) {
c1.contents.add(contents.get(i));
} else {
c2.contents.add(contents.get(i));
}
}
c1.updateValues();
c2.updateValues();
return new MenuBlockColumn[] {c1, c2};
}
private void updateValues() {
int hu = 2 * contents.size();
for (MenuBlockContent c : contents) {
int u = c.getUnfilteredItemCount();
if (u < 32) {
hu += u;
} else {
hu += 32;
}
}
heightUnits = hu;
weight = heightUnits * contents.size();
}
/**
* Returns the number of height units necessary to optimally display this menu block column.
* Menu blocks with a height of more than 32 units only count as 32 units.
*
* @return The number of height units.
*/
public int getHeightUnits() {
return heightUnits;
}
/**
* This method returns the "weight" of this column. This value should quantify the complexity
* of this column.
*
* @return The weight of this column.
*/
public int getWeight() {
return weight;
}
/**
* Creates and returns the GUI component of this menu block column.
*
* @param width The width of the column.
* @return The GUI component.
*/
public Column createGUI(int width) {
Column column = new Column();
column.setCellSpacing(new Extent(10));
int totalUnits = 18;
int unitsRest = totalUnits - (2 * contents.size());
int freeUnits = unitsRest;
int freeBlocksCount = contents.size();
double avHeightUnits = (double) unitsRest / contents.size();
boolean overfull = totalUnits - heightUnits < 0;
boolean even = true;
for (MenuBlockContent c : contents) {
if (overfull && c.getUnfilteredItemCount() <= avHeightUnits) {
even = false;
break;
} else if (!overfull && c.getUnfilteredItemCount() >= avHeightUnits) {
even = false;
break;
}
}
if (!even && overfull) {
List<MenuBlockContent> largeContents = new ArrayList<MenuBlockContent>(contents);
boolean changed;
do {
changed = false;
int i = 0;
while (i < largeContents.size()) {
MenuBlockContent c = largeContents.get(i);
if (avHeightUnits > c.getUnfilteredItemCount()) {
changed = true;
freeUnits -= c.getUnfilteredItemCount();
freeBlocksCount--;
largeContents.remove(c);
} else {
i++;
}
}
avHeightUnits = (double) freeUnits / freeBlocksCount;
} while (changed);
} else if (!even) {
freeUnits = totalUnits - heightUnits;
}
for (int i = 0 ; i < contents.size() ; i++) {
MenuBlockContent c = contents.get(i);
int cs = preditor.getMenuCreator().getColorShift(c.getName());
int heightUnits;
boolean isLast = (i == contents.size()-1);
if (isLast) {
heightUnits = unitsRest;
} else if (even) {
heightUnits = (int) ((double) freeUnits / freeBlocksCount + 0.999);
freeUnits -= heightUnits;
freeBlocksCount--;
} else if (overfull) {
if (avHeightUnits > c.getUnfilteredItemCount()) {
heightUnits = c.getUnfilteredItemCount();
} else {
heightUnits = (int) ((double) freeUnits / freeBlocksCount + 0.999);
freeUnits -= heightUnits;
freeBlocksCount--;
}
} else {
int extraCols = (int) ((double) freeUnits / (contents.size() - i) + 0.999);
heightUnits = c.getUnfilteredItemCount() + extraCols;
freeUnits -= extraCols;
}
unitsRest -= heightUnits;
MenuBlock mb = new MenuBlock(width, heightUnits * 15, cs, preditor);
mb.setContent(c);
column.add(mb);
}
return column;
}
/**
* This method calculates the combined weight of two menu block columns.
*
* @param c1 Menu block column number 1.
* @param c2 Menu block column number 2.
* @return The combined weight.
*/
public static int getCombinedWeight(MenuBlockColumn c1, MenuBlockColumn c2) {
int h = c1.heightUnits + c2.heightUnits;
int s = c1.contents.size() + c2.contents.size();
return h * s;
}
}