Background image for WorkingArea

Hello Beni,

See code here. How do I add a background image to cwArea? I want to add a product logo to the cwArea and when the user opens the ‘editor window’ (DefaultMultipleCDockable), the window will open normally and cover the image.

Regards,
Roger

Painting a background is done by the interface “BackgroundPaint”. You can install a custom background painting class by using the ThemeManager (see code below). You might also want to have a look at the class “BackgroundExample” from the tutorial-project.


import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;

import javax.swing.JFrame;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CWorkingArea;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.station.StationBackgroundComponent;
import bibliothek.gui.dock.themes.ThemeManager;
import bibliothek.gui.dock.util.BackgroundComponent;
import bibliothek.gui.dock.util.BackgroundPaint;
import bibliothek.gui.dock.util.PaintableComponent;
import bibliothek.gui.dock.util.UIBridge;
import bibliothek.gui.dock.util.UIValue;

public class BackgroundIconTest {
	public static void main( String[] args ) {
		JFrame frame = new JFrame();

		CControl control = new CControl( frame );

		frame.add( control.getContentArea() );

		CWorkingArea workingArea = control.createWorkingArea( "work" );
		installBackground( control, workingArea );
		workingArea.setVisible( true );
		
		DefaultSingleCDockable a = new DefaultSingleCDockable( "id-a", "Titel" );
		DefaultSingleCDockable b = new DefaultSingleCDockable( "id-b", "Titel" );
		workingArea.add( a ).setVisible( true );
		workingArea.add( b ).setVisible( true );

		DefaultSingleCDockable c = new DefaultSingleCDockable( "id-c", "Titel" );
		control.addDockable( c );
		c.setVisible( true );
		
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.setBounds( 20, 20, 1000, 1000 );
		frame.setVisible( true );
	}
	
	private static void installBackground( CControl control, final CWorkingArea area ){
		ThemeManager themeManager = control.getController().getThemeManager();
		
		// custom UIBridge may install custom background to any DockStation (note: there can only be one bridge for each KIND of component) 
		themeManager.setBackgroundPaintBridge( StationBackgroundComponent.KIND, new UIBridge<BackgroundPaint, UIValue<BackgroundPaint>>() {
			@Override
			public void set( String id, BackgroundPaint value, UIValue<BackgroundPaint> uiValue ) {
				// this cast is safe, because we installed this bridge with the constant StationBackgroundComponent.KIND 
				StationBackgroundComponent component = (StationBackgroundComponent)uiValue;

				if( area.getStation() == component.getStation()){
					uiValue.set( new LogoBackgroundPaint() );
				}else{
					uiValue.set( value );
				}
			}
			
			@Override
			public void remove( String id, UIValue<BackgroundPaint> uiValue ) {
				// ignore
			}
			
			@Override
			public void add( String id, UIValue<BackgroundPaint> uiValue ) {
				// ignore	
			}
		});
	}
	
	private static class LogoBackgroundPaint implements BackgroundPaint{
		@Override
		public void install( BackgroundComponent component ) {
			// ignore
		}

		@Override
		public void uninstall( BackgroundComponent component ) {
			// ignore
		}

		@Override
		public void paint( BackgroundComponent background, PaintableComponent paintable, Graphics g ) {
			// don't forget to paint the ordinary background first
			paintable.paintBackground( g );
			
			// then paint a custom logo
			Component c = paintable.getComponent();

			g.setColor( Color.RED );
			g.fillOval( 0, 0, c.getWidth(), c.getHeight() );
			
			g.setColor( Color.DARK_GRAY );
			g.fillOval( 50, 50, c.getWidth()-100, c.getHeight()-100 );
		}
	}
}

Hello Beni,

Ok, I’ll give it a try. Here is what I came up with the other day after I posted the question here. Basically, I created a dock without the title tab for the logo pane. My thought was to have it displayed when the application starts up and remove the dock when the user opens a dock.
i.e.

private void createLogo()
{
   logoDock = new DefaultMultipleCDockable( null );

   logoDock.setCloseable( false );
   logoDock.setExternalizable(false);
   logoDock.setMinimizable(false);
   logoDock.setMaximizable(false);
   logoDock.setTitleShown(false);

   JPanel logoPane = new JPanel();
   JLabel logoLabel = new JLabel(new ImageIcon(Tester.class.getResource("gif/logo.gif")));

   logoPane.add( logoLabel);
   logoDock.add( logoPane );

   work.show( logoDock );
}

Speaking of handling user events, I’m having a hard time understanding how your listeners work for CWorkingArea. I have tried all of them but they don’t fired when the user selects a different tab (dock). To me a CWorkingArea is very similar to JTabbedPane. For JTabbedPane, you add an addChangeListener() to the component and you hook onto stateChanged() method.

It appears that for CWorkingArea, I need to add an addCDockableStateListener and hook onto visibilityChanged() method but it never fires. i.e. Lets say the user opens 5 tabs (docks) in the CWorkingArea, I need to know when the user changes tabs (docks) so that I can update the meta data in another dock (i.e. DefaultSingleCDockable). I click on tabs 1,2,3,4 & 5 but nothing ever fires.

Any thoughts?

Regards,
Roger

While your first solution for the logo might work, it’s probably more complex than needed.

If you are interested in the status of something, then ask it directly - meaning: add a CFocusListener directy to the CDockables in which you are interested. Or add a global CFocusListener directly to the CControl to get all the focus events that are flying around.

I would have never ever guessed that the BackgroundExample could be used as a model for setting a background for a CWorkingArea component. I almost never code using AWT whereas you appear to have extensive knowledge. I have been coding Java/Swing for 15 years but use frameworks almost exclusively to expedite coding of projects.

You do realize that you actually did not answer my question. If the answer to a question is not setBackground() method then the answer should not be a complex AWT answer. To you the answer may be straightforward but it took me a couple hours of searching (and a headache) to realize that your paint() method should be:

{
   paintable.paintBackground( g );
   Component c = paintable.getComponent();
   ImageIcon ii = new ImageIcon(Tester.class.getResource("logo.gif"));
   Image image = ii.getImage();
   g.drawImage(image, 0, 0, c);
}```

In the future, you REALLY need to get any from 2D painting examples and just give useable answers.  That is the main problem with your tutorials, they are over complex.

Regards,
Roger

That is just odd. I tried CWorkingArea and it never fires but if I put it on a dock (DefaultMultipleCDockable for a CWorkingArea) then it fires for just that dock. How do I get back the Swing component I put into the dock?
i.e.

                           JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                           JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));

cwArea.show( editor );```

What method do I call on the CDockable for focusGained() method to retrieve the Swing component?  If I can "add()" it then I should be able to "get()" it but I do not see any methods for CDockable to retrieve it.

Also, I presume there is another listener for the user action of clicking the "X" (Close) on the tab of a dock.  What is it called?  

Regards,
Roger

It is not odd, because its the child that gets the focus, not the CWorkingArea :wink:

I would suggest to store the required information directly in the listener the moment you construct it. Otherwise try CDockable.intern().getComponent().

Closing: addCVetoClosingListener (also exists on the CControl if you want to have a global listener)

Yes, but CWorkingArea should know exactly how many docks it has and which one is the currently active (has focus) dock. The parentage, at least how I understand from the brief docs, is CControl → CWorkingArea → DefaultMultipleCDockable

When I do CDockable.intern().getComponent() it returns bibliothek.gui.dock.util.ConfiguredBackgroundPanel. I want the JComponent I put into the dock. What method do I call to get it?

You have the strangest idea of Java MVC I have ever seen. My controller is actually multi-level controller running in separate threads. The only thing I want to share is CWorkingArea. Assigning multiple listeners to each dock and having 10’s or 100’s of docks in the CWorkingArea means that there will be a huge waste of memory and breaks my multi-level controllers. The controllers are supposed to be in charge. Note: I want to stay off EDT because if I don’t then the application looks sluggish.

You appear to have coded the framework to have everything at an atomic level (bite size pieces of things). This may work for standard applications but when an application is extremely robust and complex, it does not work.

Then I went and looked up addCVetoClosingListener, as you suggested and thought ok, more bite size pieces. As I was reading CVetoClosingListener followed by CVetoClosingEvent, I started yelling at my monitor. :eek: Well, technically I was yelling at you. Why?!? Because CVetoClosingEvent has 2 methods that should also be in CWorkingArea: getDockable() and getDockableCount().

Why, why, why doesn’t CWorkingArea have getDockable(index) and getDockableCount()? Plus CWorkingArea should have a method called getActiveDockable() that returns CDockable (for the dock that has focus).

Don’t you think that makes sense? Can you update CWorkingArea to have those methods?

Now back to my question above. Here is a simple piece of code:

editor.setTitleText("Editor ");
editor.setCloseable( true );
editor.setExternalizable(false);
editor.setRemoveOnClose(true);
editor.add( new JScrollPane( new JTable() ) );
cwArea.show( editor );```
Lets assume other method has ONLY access to the CWorkingArea, now without using listeners or components, please complete the following line of code for me (i.e. what method(s) do I call to get back the JScrollPane):
```JScrollPane jsp = cwArea.get???().get???()```
or in 2 statements
```DefaultMultipleCDockable editor = cwArea.get???()
JScrollPane jsp = editor.get???()```
I don't care if it is 1 or 2 statements.  I have wasted so much time on this.  I read the java docs, played with the tutorials and even stepped through the code with the debugger and I just can't figure it out.  Help.  Please provide real working code sample as per my 1 or 2 line statement.

Regards,
Roger

First you should know that DockingFrames is in not built to support concurrency, you should always speak to it through the EDT. But DockingFrames itself does not do anything that requires a lot of resources (it plays around a bit with Lists and Maps, paints some stuff…). Of course DF is only a (small) part of your application, and does not care how the rest of the application works.

Uh, not exactly. More like „CControl → DockController → CSplitDockStation → CSplitDockStation → StackDockStation → CommonDockable → DefaultMultipleCDockable“.

You are probably missing CWorkingArea in that list. Everything in the „common“-JAR file is just a wrapper around code from the „core“-JAR file. And „core“ keeps all the different elements (stations and dockables) together.

The story goes like this: first there was „core“, it was powerful but nobody could use it because with great power came too many methods and classes. Hence „common“ was introduced, a simplified view of „core“. You could say, that „common“ is just a glorified configuration for „core“.

CWorkingArea is just a grouping element, it is referenced by some CDockables, yet it does not reference them. There simply is no need for a CWorkingArea to know about its children to satisfy its goal of being a grouping element. Since CControl knows all CDockables, and each CDockable can tell to which CWorkingArea it belongs, looping over all CDockables is a way to find all children - even those children that are invisible, minimized of floating on their own dialog.

When I do CDockable.intern().getComponent() it returns bibliothek.gui.dock.util.ConfiguredBackgroundPanel. I want the JComponent I put into the dock. What method do I call to get it?

Hmpf, you are right. As I said, I’m currently without IDE but please try:

// content-pane is an idea stolen from JFrame/JDialog
Container content = ((DefaultCDockable)d).getContentPane();
Component yourComponent = content.getComponent(0);

You have the strangest idea of Java MVC I have ever seen. My controller is actually multi-level controller running in separate threads. The only thing I want to share is CWorkingArea. Assigning multiple listeners to each dock and having 10’s or 100’s of docks in the CWorkingArea means that there will be a huge waste of memory and breaks my multi-level controllers. The controllers are supposed to be in charge. Note: I want to stay off EDT because if I don’t then the application looks sluggish.

You appear to have coded the framework to have everything at an atomic level (bite size pieces of things). This may work for standard applications but when an application is extremely robust and complex, it does not work.

For me: everything that belongs to DockingFrames is part of the view. Even CControl or listeners are view, just because they have funny names does not mean they are part of your controller. I don’t know how your application works, if it is over-engineered, what calculations you need to make, if it is a science project, etc… but they way your describe it DF clearly is not much more than a plugin, an addon, to the application? Hence → view.

Now the rest of your post is just ranting. And while I admit that DF clearly is legacy code (I would not write the same code today), and working with legacy code is a pain (In my job I need to maintain and extend a system full of old code, remaining sane is hard), it’s also an open-source project and I do not have any obligations to invest time in it.

As a result I decided to not answer to the rest of your post.

True that is why I use SwingUtilities.invokeLater() method.

[QUOTE=Beni;126588]Uh, not exactly. More like „CControl → DockController → CSplitDockStation → CSplitDockStation → StackDockStation → CommonDockable → DefaultMultipleCDockable“.

You are probably missing CWorkingArea in that list. Everything in the „common“-JAR file is just a wrapper around code from the „core“-JAR file. And „core“ keeps all the different elements (stations and dockables) together.

CWorkingArea is just a grouping element, it is referenced by some CDockables, yet it does not reference them. There simply is no need for a CWorkingArea to know about its children to satisfy its goal of being a grouping element. Since CControl knows all CDockables, and each CDockable can tell to which CWorkingArea it belongs, looping over all CDockables is a way to find all children - even those children that are invisible, minimized of floating on their own dialog.[/QUOTE]

Now that makes a lot more sense. This should be documented in both the Common Overview PDF and in the CWrokingArea JavaDocs. If you clearly documented the parentage or lack of parentage for CWorkingArea then I would not have asked the same question over and over because visually a CWorkingArea appears to look/act like a JTabbedPane.

Regards,
Roger

While I agree that an overhaul of the documentation would be a good thing, I just am not motivated enough to actually do it. Unless my employer forces me into a few weeks of vacation (unlikely) and I get really bored (unlikely too), I can guarantee that nothing will happen in that area.

Just a few sentences in each would help future users. :wink:

Regards,
Roger