Eclipse RCP ListProvider tweaking - eclipse

I have problem with changing this image list provider in to thumbnail provider. In case of need I will post View for it too.
public Object[] getElements(Object inputElement) {
if (iDirname == null)
return null;
File dir = new File(iDirname);
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File directory, String filename) {
if (filename.endsWith("jpg") || (filename.endsWith("bmp")) || (filename.endsWith("png") || (filename.endsWith("JPG") || (filename.endsWith("BMP")) || (filename.endsWith("PNG")))))
return true;
else
return false;
}
};
String[] dirList = null;
if (dir.isDirectory()) {
dirList = dir.list(filter);
for (int i=0; i<dirList.length;++i){
//dirList2[i] = new Image(device, dirList2[i]); added this to try passing array of Images - failed.
dirList[i] = iDirname + File.separatorChar + dirList[i];
}
}
return dirList;
}
And the view
public void createPartControl(Composite parent) {
iViewer = new ListViewer(parent);
iViewer.setContentProvider(new DirListProvider());
getSite().setSelectionProvider(iViewer);
makeActions();
hookContextMenu();
contributeToActionBars();
}
I don't know how to change provided path lists to the thumbnail displaying. Should I get the provided content in to Array and iterate through it creating Images? If so how?
Thanks in advance for your help.
EDIT:
I added
ImageDescriptor[] dirList = null;
if (dir.isDirectory()) {
String[] dirList2 = dir.list(filter);
for (int i=0; i<dirList2.length;++i){
dirList[i] = ImageDescriptor.createFromImageData(new ImageData(iDirname + File.separatorChar + dirList2[i]));
//dirList[i] = iDirname + File.separatorChar + dirList[i];
}
}
return dirList;
but this is not showing anything at all.
When you are telling me to use Composite, is it my parent variable? I still don't know how to display the images from paths passed by ListProvider. I am really green in this :/

What you are missing here is a LabelProvider. You can use a LabelProvider to provide an image for each element in your viewer's input.
However, Francis Upton is right, I don't think ListViewer will really suit your needs as you will end up with a single column of images. Although you won't be able to add the images directly to your Composite, you will need to set them as the background image of a label.
There are a couple of other things to consider:
You need to dispose() of your Images once you're done with them as they use up System handles. Therefore you need to keep track of the Images you create in your getElements(Object) method.
If the directories you are reading the images from do not already contain thumbnails, you will need to scale the images before presenting them on your UI.
Remember, the array type you return from your ContentProvider's getElements(Object) method defines the type that will get passed into your LabelProvider's methods. So you started off returning an array of strings representing paths to the images. Your LabelProvider would need to load these into images to be returned from the provider's getImage method - but bear in mind what I said about disposing of these images! Then you switched to returning an Array of image descriptors, in this case you would need to cast your incoming Object to an ImageDescriptor and use that to create the Image in the getImage method. Maybe once you have this working you can think about whether this meets your needs, and then possibly look at doing a different implementation, such as the composite/gridlayout/label approach.

I would not use a ListViewer for this. I would just create a Composite and then using GridLayout set up the number of columns you want and margins and so forth, and then just add the images directly to the composite. As far as I know you cannot put arbitrary things like imagines in an SWT List, so the ListViewer is not going to help you. You can do all of this in the createPartControl method.

Related

Gtk (mm) limit width of combobox

Because I use Comboboxes that may contain text entries of very long size,
which leads to the combobox increasing its width far beyond reasonable size,
I am trying to give a maximum width to the combobox.
If I am doing this like this:
class MyCombo : public Gtk::ComboBox {
private:
CellRendererText render;
public:
MyCombo() {
render.property_width_chars() = 10;
render.property_ellipsize() = Pango::ELLIPSIZE_END;
pack_start(render, true);
}
};
The result will be an empty cell of the desired width, which seems logical since I did not specify which column to show. But how can I do this with that attempt? Using pack_start will just bypass the renderer...
Another approach is this one:
class MyCombo : public Gtk::ComboBox {
private:
CellRendererText render;
public:
MyCombo() {
pack_start(render, true);
set_cell_data_func(render, sigc::mem_fun(*this, &MyCombo::render_iter));
}
void render_iter(const TreeModel::const_iterator& iter) {
Glib::ustring data = get_string_from_iter(iter);
int desired_width_chars = 10; //for example
render.property_text() = ellipsize_string(data, desired_width_chars);
}
};
Using that approach, it works, but the text in the popup (what opens up when u click the combobox) is also shortened which is not what I want (obviously the user should be able to read the whole string and I dont care about the popup widht.)
Can you please help me with this? I would be happy for any advice/alternative solutions.
Regards tagelicht
NOTE: set_wrap_width is a function that wraps the total number of entries in the combo box over a number of columns specified; it does not answer the question.
Using set_wrap_width(1) | Using set_wrap_width(5)
Following Noup's answer as a guide I managed to get the below code; which directly answers the question and its requirements (C++/Gtkmm).
// Get the first cell renderer of the ComboBox.
auto v_cellRenderer = (Gtk::CellRendererText*)v_comboBox.get_first_cell();
// Probably obsolete; Sets character width to 1.
v_cellRenderer->property_width_chars() = 1;
// Sets the ellipses ("...") to be at the end, where text overflows.
// See Pango::ELLIPSIZE enum for other values.
v_cellRenderer->property_ellipsize() = Pango::ELLIPSIZE_END;
// Sets the size of the box, change this to suit your needs.
// -1 sets it to automatic scaling: (width, height).
v_cellRenderer->set_fixed_size(200, -1);
Result (image):
Result of code
BE AWARE: Depending on where you perform the above code; either all the cells will be the same size, or just the box itself (intended).
From experimenting, I've found:
In the parent object constructor: All cell sizes are the same.
In a separate function: Only the first cell (the box) is affected.
I'd recommend you put the code in a function that's connected to the comboBox's changed signal, such as:
v_comboBox.signal_changed().connect(sigc::mem_fun(*this, &YourClass::comboBox_changedFunction));
This may be what you are looking for:
cell_renderer_text.set_wrap_width(10)
This is for Python, but you get the idea :-)
Unfortunately, the documentation is scarce. I found this by poking around in Anjuta/Glade.
Edit:
the docs are here. They are not overly helpful, but they do exist.
As an alternative, the following works for me without having to set wrap_width nor to subclass ComboBox (in Gtk#):
ComboBoxText cb = new ComboBoxText();
cb.Hexpand = true; //If there's available space, we use it
CellRendererText renderer = (cb.Cells[0] as CellRendererText); //Get the ComboBoxText only renderer
renderer.WidthChars = 20; //Always show at least 20 chars
renderer.Ellipsize = Pango.EllipsizeMode.End;
Note: I'm using Expand to use space if it's available. If you just want to keep the combo box on a fixed width, just remove that bit.

Widget changing hangs the main window in GTK+

I am writing a program in Vala using GTK+. It has a function that creates a ListBox that contains a lot of EventBox objects. There is one issue: there is one function that downloads the image and it takes a lot of time, so the main window didn't show up unless all downloads are finished. This is not what I wanted, I wanted main window to appear and then images to download and to be shown. So I separated image load to separate function, but main window still doesn't show unless all downloads are finished. What am I doing wrong?
Here is the function I'm using:
foreach (MediaInfo post in feedPosts)
feedList.prepend(post);
foreach (PostBox box in feedList.boxes)
box.loadImage();
("feedList" is a class inherited from Gtk.ListBox and "boxes" is a list containing all of PostBox (which is inherited from Gtk.EventBox) objects)
This is feedList.prepend function:
public void append(MediaInfo post)
{
Gtk.Separator separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
base.prepend(separator);
PostBox box = new PostBox(post);
base.prepend(box);
boxes.append(box);
}
And this is the constructor and loadImage functions of PostBox class:
public PostBox(MediaInfo post)
{
box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
this.add(box);
this.post = post;
userToolbar = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
userNameLabel = new Gtk.Label("#" + post.postedUser.username);
this.userNameLabel.set_markup(
"<span underline='none' font_weight='bold' size='large'>" +
post.postedUser.username + "</span>"
);
userToolbar.add(userNameLabel);
box.pack_start(userToolbar, false, true);
image = new Gtk.Image();
box.add(image);
box.add(new Gtk.Label(post.title));
box.add(new Gtk.Label( post.likesCount.to_string() + " likes."));
print("finished.\n");
return;
}
public void loadImage()
{
var imageFileName = PhotoStream.App.CACHE_URL + getFileName(post.image.url);
downloadFile(post.image.url, imageFileName);
Pixbuf imagePixbuf = new Pixbuf.from_file(imageFileName);
imagePixbuf = imagePixbuf.scale_simple(IMAGE_SIZE, IMAGE_SIZE, Gdk.InterpType.BILINEAR);
image.set_from_pixbuf(imagePixbuf);
}
You have written the download operations in another method, however the operations are still synchronous, i.e. they block the thread. You never want to do computationaly or otherwise expensive things in the GUI thread, because that makes the GUI unresponsive.
You should start your downloads asynchronously, and trigger a callback method when the download is complete. In the callback, then you may for example change the image placeholders to actual images.
I replaced all my async methods with multithreading and now it is working the way I want it to work.

How get all widgets of certain type?

I have an AbsolutePanel and different widgets (Buttons, Images, Labels, e.t.c.) on it. Is it possible to get collection or array or whatever of all widgets of certain type, for example - Image?
Here you go :
We can use iterator.
Iterator<Widget> arrayOfWidgets = abslPanel.iterator();
while (arrayOfWidgets.hasNext()){
Widget ch = arrayOfWidgets .next();
if (ch instanceof Button) {
//Do something (in your case make an arraylist of your objects)
}
}
If you add gwtquery to your project it is really easy:
List<Image> allImages = $("*", myPanel).widgets(Image.class);
And even you could use sophisticated css selectors to perform a finer discrimination:
List<MyWidget> allMyWidgets = $("*:nth-child(even)", myPanel).widgets(MyWidget.class);

What is the proper way in OpenLayers (OSM) to trigger a popup for a feature?

I have the feature ID, I can grab the marker layer on GeoRSS loadend, but I'm still not sure how to cause the popup to appear programmatically.
I'll create the popup on demand if that's necessary, but it seems as though I should be able to get the id of the marker as drawn on the map and call some event on that. I've tried using jQuery and calling the $(marker-id).click() event on the map elements, but that doesn't seem to be working. What am I missing?
Since I was asked for code, and since I presumed it to be boilerplate, here's where I am so far:
map = new OpenLayers.Map('myMap');
map.addLayer(new OpenLayers.Layer.OSM());
map.addLayer(new OpenLayers.Layer.GeoRSS(name,url));
//I've done some stuff as well in re: projections and centering and
//setting extents, but those really don't pertain to this question.
Elsewhere I've done a bit of jQuery templating and built me a nice list of all the points that are being shown on the map. I know how to do a callback from the layer loadend and get the layer object, I know how to retrieve my layer out of the map manually, I know how to iter over the layers collection and find my layer. So I can grab any of those details about the popup, but I still don't know how to go about using the built-in methods of the DOM or of this API to make it as easy as element.click() which is what I would prefer to do.
You don't have to click the feature to open a popup.
First you need a reference to the feature from the feature id. I would do that in the loadend event of the GeoRSS layer, using the markers property on the layer.
Assuming you have a reference to your feature, I would write a method which handles the automatic popup:
var popups = {}; // to be able to handle them later
function addPopup(feature) {
var text = getHtmlContent(feature); // handle the content in a separate function.
var popupId = evt.xy.x + "," + evt.xy.y;
var popup = popups[popupId];
if (!popup || !popup.map) {
popup = new OpenLayers.Popup.Anchored(
popupId,
feature.lonlat,
null,
" ",
null,
true,
function(evt) {
delete popups[this.id];
this.hide();
OpenLayers.Event.stop(evt);
}
);
popup.autoSize = true;
popup.useInlineStyles = false;
popups[popupId] = popup;
feature.layer.map.addPopup(popup, true);
}
popup.setContentHTML(popup.contentHTML + text);
popup.show();
}
fwiw I finally came back to this and did something entirely different, but his answer was a good one.
//I have a list of boxes that contain the information on the map (think google maps)
$('.paginatedItem').live('mouseenter', onFeatureSelected).live('mouseleave',onFeatureUnselected);
function onFeatureSelected(event) {
// I stuff the lookup attribute (I'm lazy) into a global
// a global, because there can be only one
hoveredItem = $(this).attr('lookup');
/* Do something here to indicate the onhover */
// find the layer pagination id
var feature = findFeatureById(hoveredItem);
if (feature) {
// use the pagination id to find the event, and then trigger the click for that event to show the popup
// also, pass a null event, since we don't necessarily have one.
feature.marker.events.listeners.click[0].func.call(feature, event)
}
}
function onFeatureUnselected(event) {
/* Do something here to indicate the onhover */
// find the layer pagination id
var feature = findFeatureById(hoveredItem);
if (feature) {
// use the pagination id to find the event, and then trigger the click for that event to show the popup
// also, pass a null event, since we don't necessarily have one.
feature.marker.events.listeners.click[0].func.call(feature, event)
}
/* Do something here to stop the indication of the onhover */
hoveredItem = null;
}
function findFeatureById(featureId) {
for (var key in map.layers) {
var layer = map.layers[key];
if (layer.hasOwnProperty('features')) {
for (var key1 in layer.features) {
var feature = layer.features[key1];
if (feature.hasOwnProperty('id') && feature.id == featureId) {
return feature;
}
}
}
}
return null;
}
also note that I keep map as a global so I don't have to reacquire it everytime I want to use it

Using the Selection service on something that is *not* a JFace view

I am building an image Editor as an Eclipse plugin.
I would like to use the Properties view to view & edit properties of the model underneath the image. Accordingly I am calling ..
getSite().setSelectionProvider( this );
.. within createPartControl, and implementing the ISelectionProvider interface in my EditorPart implementation, so that the model is returned as the selection (which must therefore implement the ISelection interface).
The next step is for the Editor to implement IAdaptable to supply an adapter for the selected object.
My problem however is that getAdapter is never called with IPropertySource.class, and therefore the Properties View never gets what it needs to make sense of the image model.
Your help is much appreciated.
M.
The answer in the end broke down into a few pieces ...
1.) When your selection does change (if a user has zoomed into the image, for example) be sure to tell Eclipse this. It won't happen otherwise.
2.) When sending your SelectionChangedEvent, wrap up your IAdaptable in a StructuredSelection object - otherwise the Properties view will ignore it.
This boiled down to the following method
public void fireSelectionChanged()
{
final SelectionChangedEvent event = new SelectionChangedEvent( this, new StructuredSelection( this ) );
Object[] listeners = selectionChangedListeners.getListeners();
for (int i = 0; i < listeners.length; ++i)
{
final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
SafeRunnable.run(new SafeRunnable() {
public void run() {
l.selectionChanged( event );
}
});
}
}
... on an class that implemented ISelectionProvider & IAdaptable.
M.