Proper multiple cell selection in JTable

on Friday, September 13, 2013
I find it so strange that you can't really select multiple cells in the JTable. For example, try selecting cells that are diagonal from each other. The whole "square" will be selected. This is because in cell selection mode, the selection is based on the intersection of the row model and column model. Thus, certain selections are impossible. This frustrated me to no end and I took it upon myself to find a solution.

After much research, I came up with this (untested, hackish code):

public class CellSelectionTable extends JTable {

        CellSelectionModel cellSelectionModel;

        public CellSelectionTable(TableModel tableModel) {
            super(tableModel);
            cellSelectionModel = new CellSelectionModel(getRowCount(), getColumnCount());
        }

        @Override
        public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
            cellSelectionModel.changeSelection(rowIndex, columnIndex, toggle, extend);
            super.changeSelection(rowIndex, columnIndex, toggle, extend);
        }

        private void addCellSelection(int row, int col) {
            cellSelectionModel.addSelection(row, col);
        }

        @Override
        public void selectAll() {
            cellSelectionModel.selectAll();
            super.selectAll();
        }

        @Override
        public void clearSelection() {
            if (cellSelectionModel != null) {
                cellSelectionModel.clearSelection();
            }
            super.clearSelection();
        }

        @Override
        public boolean isCellSelected(int row, int column) {
            return cellSelectionModel.isCellSelected(row, column);
        }


/**
 * 2D selection model (e.g. 2D table)
 *
 * @author RadianceOng
 */
public class CellSelectionModel {

    private boolean[][] cellSelected;
    int rows;
    int cols;
    Point anchor;
    List<ChangeListener> changeListeners;

    public CellSelectionModel(int row, int col) {
        cellSelected = new boolean[row][col];
        anchor = new Point(0, 0);
        rows = row;
        cols = col;
        changeListeners = new ArrayList<> ();

        clearSelection();
    }

    public void addSelection(int row, int col) {
        cellSelected[row][col] = true;
        setAnchor(row, col);
    }

    public void removeSelection(int row, int col) {
        cellSelected[row][col] = false;
        setAnchor(row, col);
    }

    public void toggleSelection(int row, int col) {
        cellSelected[row][col] = !cellSelected[row][col];
        setAnchor(row, col);
    }

    public void changeSelection(int row, int col, boolean toggle, boolean extend) {
        if (extend) {
            extendSelection(row, col, toggle);
        } else {
            if (toggle) {
                toggleSelection(row, col);
            } else {
                clearSelection();
                addSelection(row, col);
            }
        }
       
        stateChange();
    }

    public void extendSelection(int row, int col, boolean toggle) {
        boolean anchorState = true;
        if (toggle) {
            anchorState = isCellSelected(row, col);
        }

        Point firstPoint = new Point(Math.min(anchor.x, col), Math.min(anchor.y, row));
        Point lastPoint = new Point(Math.max(anchor.x, col), Math.max(anchor.y, row));

        for (int i = firstPoint.y; i <= lastPoint.y; i++) {
            for (int j = firstPoint.x; j <= lastPoint.x; j++) {
                cellSelected[i][j] = anchorState;
            }
        }
        //setAnchor(lastPoint.y, lastPoint.x);
    }

    public void selectAll() {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                cellSelected[i][j] = true;
            }
        }
        setAnchor(rows - 1, cols - 1);
    }

    public void clearSelection() {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                cellSelected[i][j] = false;
            }
        }
        setAnchor(rows - 1, cols - 1);
    }

    public boolean isCellSelected(int row, int col) {
        return cellSelected[row][col];
    }
   
    /**
     * Visits the entire selection, calling the visitor on every cell
     * @param v
     */
    public void visitCellSelection(CellSelectionVisitor v) {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                v.isSelected(cellSelected[i][j], i, j);
            }
        }
    }
   
    public void addChangeListener(ChangeListener l) {
        changeListeners.add(l);
    }
   
    public void removeChangeListener(ChangeListener l) {
        changeListeners.remove(l);
    }

    private void setAnchor(int row, int col) {
        anchor.x = col;
        anchor.y = row;
    }
   
    private void stateChange() {
        ChangeEvent e = new ChangeEvent(this);
        for(ChangeListener l : changeListeners) {
            l.stateChanged(e);
        }
    }
   
    public interface CellSelectionVisitor {
        /**
         * Called with state of cell
         * @param selected
         * @param row
         * @param col
         */
        void isSelected(boolean selected, int row, int col);
    }

That awkward moment...

on Friday, December 21, 2012
So,that awkward moment when you realize you made a simple error after debugging for a while...

GlyphVector gv = font1.getGlyphVector(g.getFontRenderContext(), new Character(textChar).toString());
double normalWidth = gv.getVisualBounds().getWidth();
double normalHeight = gv.getVisualBounds().getWidth();

I guess this is what happens when you try to code in the middle of the night...

We all need a break.

C

on Friday, November 30, 2012
So glad I learned C. Even though I don't really like it. At least I can understand some low-level stuff, like this CUDA course I'm taking at Coursera right now

Custom animated Gtk.Image widget in MonoDevelop

on Thursday, November 29, 2012
So, I was going through Head First C# and doing the programming exercise using Gtk#/MonoDevelop instead of WinForms/Visual Studio. Of course, then it takes some workarounds(or I should say, different ways) to do certain things in Gtk#.

1. Embedding resources

Pretty straightforward to add resources in Visual Studio. But how to do that in MonoDevelop?
http://stackoverflow.com/questions/4383298/how-do-i-use-a-resources-file-in-monodevelop

2. Calling resources

Visual Studio auto-generates some Properties.Resources thingy. I was told that I have to do it the .NET way. Never heard of that. (Btw, still confused about what are assemblies.)

Using System.Reflection; //for Assembly
Using System.IO; //for Stream
Assembly a = Assembly.GetExecutingAssembly();
Stream s = a.GetManifestResourceStream("BeeHive.Resources.Bee animation 1.png");


Wow, looks pretty complicated, but it works. (I need to find out how it works)

3. Creating a custom PictureBox control

Well, no PictureBox in GTK. Closest thing is Gtk.Image. So that I'll use.
Apparently, if you extend a control class in Visual Studio it'll just magically appear in your toolbox. Nope, it does not work in MonoDevelop.

Found this video: http://monodevelop.com/Creating_custom_widgets_with_MonoDevelop

Although, seems a bit overkill for my purpose. I just want to extend Gtk.Image. Well, turns out, I just have to add
[System.ComponentModel.ToolboxItem(true)]
to my widget class. And NOW it appears in my toolbox. YES!

4. Animating the control

I'm just following the textbook here. Sounds like a poor way to animate a control when Gtk.Image supports animated image, I think. But never mind. So, I set a timer that changes the Pixbuf property at certain intervals. Conveniently, the Pixbuf constructor allows a Stream as an argument. But wait, even MORE conveniently,

Pixbuf (System.Reflection.Assembly, string)
Pixbuf (System.Reflection.Assembly assembly, string resource, int width, int height)



So I don't have to do all that GetManifestResourceStream crap. Wow, the latter could be quite convenient for future use.

Conclusion

Not bad, I learned a lot today. Almost wanted to give up and just use Visual Studio instead.

Programming: Principles and Practice Using C++

on Saturday, November 17, 2012
Just started reading this book by none other than Stroustrup, the father of C++. (how do you pronounce his name?!). Seems like this is a very practical book. I like. I love doing exercises while reading such books, because it helps greatly with my learning.

My first C++ book was a Qt and Design patterns which was awesome too. Now I'm "back to the basics", so to speak. We'll see how it goes!

Overloading of operators

on Sunday, November 11, 2012
So today I tried to overload the << and >> operators (in a header file)
  QDataStream &operator<<(QDataStream &out, PlayListModel& model) { model.write(out); return out; } 

but I got multiple definition errors:
D:\Qt\PlayGen-build-desktop-Qt_4_8_1_for_Desktop_-_MinGW__Qt_SDK__Debug\..\PlayGen\\..\Mewsic\playlistmodel.h:32: multiple definition of `operator<<(QDataStream&, PlayListModel&)'

I did include guard statements in my header file. Yet it didn't seem to be working here. The header file was being included multiple times. What the heck was going on?! I did a search and finally found the problem:
No, this is a misconception on how include guards work. They will prevent multiple inclusion in the samefile, but not in multiple files.
This is the reason you should only place declarations in headers, but not definitions.
http://qt-project.org/forums/viewthread/20973 
Bingo! Being new to C++, I've been facing problems similar to these and sometimes to takes a long time to figure it out. But it's well worth it. C++ is so powerful. But I still find it very complicated. All those header files, all those operator overloading and whatnot. I feel that it's very easy to do something wrong and not realise it.

Although I still love Java the most, I'm starting to like C++... Hmm...

A new start!

on Saturday, November 10, 2012
Well, after so many years of tinkering with software and such, as well as software projects in university, I've created some useful snippets of software that I think might be useful for any fellow students :)

Soon I'll add some of the code I've written. And also the problems I encountered while learning stuff.