English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. C/C++

 
 
Thread Tools
Old 05 July 2021, 15:57   #1
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Converting to Reaction: Shared UserPort?

I have an app designed without Reaction, and now that 3.2 is available, I'm trying to convert it to work with Reaction. One of the things that is different is event handling. I may describe this wrong, but this is how I think the differences come out:
  • Pre-reaction: we use messages. Do GetMsg, do stuff, do ReplyMsg
  • In Reaction, we don't use messages. You do GetAttr for the window to get its signal, wait on the signal (same as pre-reaction), then loop through RA_HandleInput() until it is out of messages (ok, IT uses messages?) - WMHI_LASTMSG

My app uses windows like this:
- a backdrop window, opened on launch
- user opens 1 to n additional windows as they want to. Its basically a file manager, so if you double click on a file icon for a folder, another window opens showing contents of that folder.
- you can do an About menu item and an About Window opens.
- other window types to come (config, etc.)

I added the About Window after reaction came out, so it has been my testbed. There will only ever be 1 about window open, so it's pretty straight forward.

But the main window type, there could be any number of them. I am using a shared userPort for all of them.

When a signal comes in:
- is it for the about window? let it process it. STOP
- is it for the app reply port? let the app handle that. STOP
- is it for the shared user port?
- Get the message
- Get the IDCMPWindow property from the message
- iterate through the list of open windows to find a match (I have a parent class for windows that wraps the Intuition struct Window).
- Let that window class handle the message and reply.


Is it possible to use a shared signal with Reaction windows? What's the best design for a case like this?

- Do GetAttr for each window as it is made, and store that in the parent window class. add all those signals together in the signal mask, then iterate through to find out which one it was?
- something else entirely?

Current event handler:
Code:
// wait for events for the currently active window.
// having it centralized makes it possible to have user close first window gracefully
// requires that WBSurfaces keep track of which one is active, which isn't hard.
void WaitForEvents(void)
{
	WindowRequiredAction	action_needed = action_none;
	struct					IntuiMessage* the_message;
	struct					Window* window_with_message;
	ULONG					window_event_signal = 1L << global_app->shared_user_port_->mp_SigBit;
	ULONG					launched_app_signal = 1L << global_app->launched_app_reply_port_->mp_SigBit;
	ULONG					our_idcmp_signals = window_event_signal | launched_app_signal;
	ULONG					about_window_signal;
	ULONG					signals_we_want;
	ULONG					signals_received;

	// LOGIC:
	//   we will listen at 2 ports:
	//   - one for window events. this is the user user port that we set for all WB2KSurfaces
	//   - one that we set up to be a port where apps we launch can reply back to when they are quit
	//   we combine all signals (2 in this case), then use Wait() instead of WaitPort().
	//   we also need to listen on a per-window basis for Reaction signals, as reaction does not seem to work with shared ports
	//     at current time (6/30/2021), only About window uses reaction, so we can check for that window specifically
	//     need a more dynamic solution when we move the main window type to reaction (iterating through all app's surfaces is probably slow)

	
	while (action_needed != action_quit_app)
	{
		signals_we_want = our_idcmp_signals;
		
		if (global_app->about_window_ != NULL)
		{
			GetAttr(WINDOW_SigMask, global_app->about_window_->objects_[ABOUT_OID_MAIN], &about_window_signal);
			signals_we_want |= about_window_signal;
 		}

		signals_received = Wait(signals_we_want);

		if (signals_received & about_window_signal)
		{
			//DEBUG_OUT(("WaitForEvents %d: about win reaction signal detected!", __LINE__));

			action_needed = AboutWindow_HandleEvents(global_app->about_window_);

			if (action_needed == action_close_window)
			{
				App_CloseAboutWindow(global_app);
			}
		}
		else if (signals_received & launched_app_signal)
		{
			//DEBUG_OUT(("WaitForEvents %d: launched_app_signal received", __LINE__));
			App_HandleLaunchedAppSignal();			
		}
		else if (signals_received & window_event_signal)
		{
			//DEBUG_OUT(("WaitForEvents %d: window_event_signal received", __LINE__));

			while ((the_message = (struct IntuiMessage*)GetMsg(global_app->shared_user_port_)) != NULL)
			{
				// determine which window this event was directed at
				// acquire the message, because it has a pointer to the window struct
				//the_message = (struct IntuiMessage*)GetMsg(global_app->shared_user_port_);
				window_with_message = the_message->IDCMPWindow;

				//DEBUG_OUT(("WaitForEvents %d: window_with_message=%p", __LINE__, window_with_message));

				WB2KList*	the_item = *(global_app->list_surfaces_);
				
				if ( the_item == NULL )
				{
					LOG_WARN(("WaitForEvents %d: no windows available??", __LINE__));
				}
				else
				{
					boolean signal_not_matched = true;

					while (the_item != NULL)
					{
						WB2KWindow* this_surface = (WB2KWindow*)(the_item->payload_);

						//DEBUG_OUT(("WaitForEvents %d: window_with_message=%p; this_surface=%p", __LINE__, window_with_message, this_surface->window_));

						// is this the item we are looking for?
						if (this_surface->window_ == window_with_message)
						{
							//printf("WaitForEvents: event detected for surface %p, window %p \n", this_surface, this_surface->window_);
							signal_not_matched = false;
							this_surface->intuimsg_ = the_message;
							action_needed = Window_HandleEvents(this_surface);

							if (action_needed == action_close_window)
							{
								App_CloseOneWindow(global_app, the_item);
							}

							break;
						}						

						the_item = the_item->next_item_;
					}

					if (signal_not_matched)
					{
						LOG_ERR(("WaitForEvents %d: a message couldn't be matched to a window", __LINE__));
						App_Destroy(); // crash early, crash often
					}
				}
			}
		}
	}
}
Warty is offline  
Old 05 July 2021, 22:14   #2
thomas
Registered User
 
thomas's Avatar
 
Join Date: Jan 2002
Location: Germany
Posts: 6,985
Quote:
Originally Posted by Warty View Post
now that 3.2 is available, I'm trying to convert it to work with Reaction.
Interesting. ReAction is available since more than 20 years. ClassAct is even longer.


Quote:
Is it possible to use a shared signal with Reaction windows?
Window.class has a WINDOW_SharedPort attribute.
thomas is offline  
Old 05 July 2021, 22:50   #3
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Quote:
Originally Posted by thomas View Post
Interesting. ReAction is available since more than 20 years. ClassAct is even longer.
Yes, people tell me that, so I know it's true, but it just hasn't been true for *me*. For me, the OS went like this:
A500 w/ 1.3 -> A3000 w/ 2.04 ->>>>>MANY DEPRESSING EVENTS >>>>> me doing non-Amiga stuff -->>>> 2018 or so: FS-UAE with WB 3.1 >>>> A2000 w/ 3.1.4 >>> A3000 w/3.2

So I've never had or even seen 3.9. Let alone any PPC 4.x type system. I did try to get ClassAct working a year or two ago while waiting for 3.2, but didn't have much luck (and there were lots of other low-level stuff to work on in the meantime).

I've got an idea to try out here to figure out which window is getting the event, event without a message->window arrangement to take advantage of.
Warty is offline  
Old 06 July 2021, 15:28   #4
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Quote:
Originally Posted by thomas View Post
Window.class has a WINDOW_SharedPort attribute.
This was helpful, thanks. I was still leaving it out, and then adjusting it after creating the window object (which, under non-reaction, apparently saves Intuition from creating an extra user port when window is created). I switched to specifying the user port right in the WindowObject creation call, and that gets it set.

I admit, however, that I don't understand WHY this works. From window_cl.doc:

Quote:
If you have set up the window to use a shared message port, you
can simply call this method on each of the windows:

if (mask & wsig)
{
WmHandleInputWin1();
WmHandleInputWin2();
WmHandleInputWin3();
}
If I have 3 windows, all with the same gadgets, and I call each of them in turn every time a signal from ONE of them comes through the shared user port... how does intuition know which window is which? I mean, it does: I open 3 windows, and I click the sort button in the first, it doesn't sort the content of the other 2. The signals look the same to me. Not a big deal, just curious.

I took the plunge yesterday and started the conversion. Long way to go, but it's coming along.
Warty is offline  
Old 06 July 2021, 16:34   #5
thomas
Registered User
 
thomas's Avatar
 
Join Date: Jan 2002
Location: Germany
Posts: 6,985
Each window.class object knows which Intuition window it opened and it handles messages only from that window.

When working with objects, you should never poke into the returned memory. Always work with SetAttrs or DoMethod.

Especially the struct Window pointer you get from WM_OPEN belongs to the class. You must not do anything with it except to forward it unchanged to RefreshGList and other such functions which require a window pointer.
thomas is offline  
Old 08 July 2021, 15:26   #6
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
I will do a little audit of what I'm doing to this window. It's been a year or so since I really worked on it, I don't remember everything I'm doing.

I did make a lot of progress on the reaction conversion. Most things are working again.

A couple of things I'm stuck with though:
1. how do I get the scrollbars back into the window borders? I have taken out all the old code I had that positioned the scrollbars in the borders using GA_RelBottom, etc. My assumption is that when I make them part of the reaction layout, I shouldn't be doing that sort of thing.

2. I can't pick up events for the scrollbars. I have an IDCMP hook set up:

the scroller objects:
Code:
	// create the scrollbar objects and the string object used for renaming files
	the_surface->gadgets_[MAIN_GID_H_SCROLL] = ScrollerObject,
			GA_ID,					MAIN_GID_H_SCROLL,
			SCROLLER_Orientation,	SORIENT_HORIZ,
			GA_Immediate,			TRUE,
			GA_RelVerify,			TRUE,
			ICA_TARGET, 			ICTARGET_IDCMP,
		ScrollerEnd;
	
	the_surface->gadgets_[MAIN_GID_V_SCROLL] = ScrollerObject,
			GA_ID,					MAIN_GID_V_SCROLL,
			SCROLLER_Orientation,	SORIENT_VERT,
			GA_Immediate,			TRUE,
			GA_RelVerify,			TRUE,
			ICA_TARGET, 			ICTARGET_IDCMP,
		ScrollerEnd;
relevant part of the object creation for the window:
Code:
		WA_IDCMP,			IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE | IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_GADGETUP | IDCMP_IDCMPUPDATE | IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW | IDCMP_CHANGEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_MENUPICK | IDCMP_EXTENDEDMOUSE,
		WINDOW_IDCMPHook,		&the_surface->scroller_hook_,
		WINDOW_IDCMPHookBits,	IDCMP_IDCMPUPDATE | IDCMP_EXTENDEDMOUSE,
		WINDOW_AppPort,		global_app->launched_app_reply_port_,
		WINDOW_SharedPort,	the_user_port,
Code:
		// set up an IDCMP hook that will let us examine IDCMP messages to see if scrollers have been moved
		the_surface->scroller_hook_.h_Entry = (void *)Window_ScrollerHook;
		the_surface->scroller_hook_.h_SubEntry = NULL;
		the_surface->scroller_hook_.h_Data = (APTR)the_surface;
Code:
// hook function to receive IDCMP events, and check them for scrollbar movement
// based on code found here: https://github.com/pcwalton/NetSurf/blob/master/netsurf/amiga/tree.c\ami_tree_scroller_hook
void Window_ScrollerHook(struct Hook* hook, Object* object, struct IntuiMessage* msg) 
{
	ULONG					activated_gadget_id;
	ULONG					x;
	ULONG					y;
	WB2KWindow*				the_surface = (WB2KWindow*)hook->h_Data;
	struct IntuiWheelData*	wheel;

	DEBUG_OUT(("Window_ScrollerHook %d: msg->Class = %lu, code=%u, qualifier=%u, addr=%p", __LINE__, msg->Class, msg->Code, msg->Qualifier, msg->IAddress));
	
	switch(msg->Class)
	{
		case IDCMP_IDCMPUPDATE:
			activated_gadget_id = GetTagData( GA_ID, 0, msg->IAddress ); 
			DEBUG_OUT(("Window_ScrollerHook %d: activated_gadget_id = %lu", __LINE__, activated_gadget_id));

			if (activated_gadget_id == MAIN_GID_H_SCROLL || activated_gadget_id == MAIN_GID_V_SCROLL)
			{
				signed long		new_pos;
				
				new_pos = Gadget_GetTop(the_surface->gadgets_[activated_gadget_id]);
				DEBUG_OUT(("Window_ScrollerHook %d: scrollbar gadget %lu = %li", __LINE__, activated_gadget_id, new_pos));

				if (activated_gadget_id == MAIN_GID_H_SCROLL)
				{
					the_surface->content_left_ = new_pos;
				}
				
				// draw content without recalculating icon positions
				Panel_RegenerateDisplay(the_surface->active_panel_);
			}
			

			break;

		case IDCMP_EXTENDEDMOUSE:
			// TODO: mousewheel support! 
// 			if(msg->Code == IMSGCODE_INTUIWHEELDATA)
// 			{
// 				wheel = (struct IntuiWheelData *)msg->IAddress;
// 
// 				ami_tree_scroll(twin, (wheel->WheelX * 20), (wheel->WheelY * 20));
// 			}
			break;
	}
}
The hook function is getting called, but the msg code is basically always 0 on mouse down and scrollbar movement. I think on mouse up it is sending the other result you see in the screenshot. I say "I think" because once I click on a scrollbar, the amiga gets very slow and unresponsive for a few seconds. Definitely something not right.
Attached Thumbnails
Click image for larger version

Name:	Screen Shot 2021-07-08 wb2k reaction IDCMP hook.png
Views:	96
Size:	69.1 KB
ID:	72492  
Warty is offline  
Old 08 July 2021, 18:06   #7
thomas
Registered User
 
thomas's Avatar
 
Join Date: Jan 2002
Location: Germany
Posts: 6,985
The h_Entry entry point of a hook receives arguments in registers (A0, A2, A1). Your hook function however seems to expect arguments on the stack.

To continue doing so you should initialize h_Entry with HookEntry and set h_SubEntry to your hook function. HookEntry is part of amiga.lib and declared in clib/alib_protos.h.
thomas is offline  
Old 09 July 2021, 00:52   #8
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Quote:
Originally Posted by thomas View Post
you should initialize h_Entry with HookEntry and set h_SubEntry to your hook function.
Thanks! That was it; it works great now. I wouldn't have figured that out.

I wish there was a compilation of programs that came with source code. I find source code to be the easiest thing to learn from, but it can be hard to find relevant examples for the Amiga. More info and examples in Autodocs would help a lot of course.
Warty is offline  
Old 09 July 2021, 15:29   #9
bubbob42
Registered User
 
Join Date: Oct 2012
Location: Germany
Posts: 585
Quote:
Originally Posted by Warty View Post
Thanks! That was it; it works great now. I wouldn't have figured that out.

I wish there was a compilation of programs that came with source code. I find source code to be the easiest thing to learn from, but it can be hard to find relevant examples for the Amiga. More info and examples in Autodocs would help a lot of course.
Well, now that you’ve got working code, no one will stop you from abstracting it into a usable, documented example focusing on the issue. Send it in for review and we could add it to a future NDK revision.
bubbob42 is offline  
Old 09 July 2021, 16:52   #10
Minuous
Coder/webmaster/gamer
 
Minuous's Avatar
 
Join Date: Oct 2001
Location: Canberra/Australia
Posts: 2,629
There are a lot of ReAction examples in the 3.2 NDK; also you could look at the source code of programs like MCE or Report+.
Minuous is offline  
Old 10 July 2021, 07:23   #11
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Quote:
Originally Posted by Minuous View Post
There are a lot of ReAction examples in the 3.2 NDK; also you could look at the source code of programs like MCE or Report+.
Those reaction examples in the NDK are very helpful, they (and the Autodocs) are the first place I look. Solid resource.

Thanks for the suggestions of MCE and Report+. I have downloaded both, and added to my source code references folder. MCE is a bit of a beast! That's a real labor of love. I downloaded a couple of games just so I could see what the UI looked like.

I also search a bit through aminet from time to time, but you can't really search for "reaction" and "download includes source code". Were there ever any FTP sites for Amiga that had dedicated source folders? I'm thinking of, for old Mac, there was UMich FTP which had a folder of source files. That's been very helpful. I should probably spend more quality time with FredFish disks.
Warty is offline  
Old 10 July 2021, 07:29   #12
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Quote:
Originally Posted by bubbob42 View Post
Well, now that you’ve got working code, no one will stop you from abstracting it into a usable, documented example focusing on the issue. Send it in for review and we could add it to a future NDK revision.
I've noticed that trying to document something takes approximately the same amount of time as coding and researching it. And documentation is hard brain work (to do well). But yeah, I hear you! I need to get further along here, but definitely thinking about how to share back some info.
Warty is offline  
Old 10 July 2021, 07:38   #13
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
About the scrollbars. I reached the conclusion that it wasn't possible to put them into the window borders using pure Reaction. Am I right about that?

These are my personal dev notes on it:
------
Reaction Windows and Scrollbars
* This was very confusing
* I started out by doing standard reaction layout: added scrollbars to a layout. They auto refresh, etc. Figured out how to set top for them: slightly different than normal gadgets, you don’t use PGA_Top, you use SCROLLER_Top, etc.
* The problem with this method is that A) the scrollbars never get into the window frame. You can use PG_BorderBottom, but that just colors the scroller blue (frame color), it doesn’t move it. So you end up using up valuable screen real estate inside your window.
* I did figure out how to remove and add them back in when needed/not needed, under Reaction. It was a bit klutzy looking.
* In the end, I figured out that the old method not only worked best, it was doable even under reaction. I ended up deciding I don’t ever need the V scroller, so I just don’t draw that. For the H scroller, it goes into the bottom border, and only renders when needed.
* The only downside I noticed is that the scrollbars are not in fact reaction, so the reaction UI preferences don’t modify them. Don’t think that’s a real problem.

In this UI, there's a pretty basic Reaction layout with a speedbar at the top, then a spacer where I blit the icons and labels onto. The icons don't exist as gadgets at all. The scrollbar at the bottom is a gadget, but not a reaction gadget. I literally went back into GitHub and got out my old (pre-reaction) scrollbar code and dusted it off, and it worked in the border.

Code:
// Recalculate the scrollbar sliders and reset scrollbar position to top/left
// call this when required size of the window is different than what it had been
void Window_ResetScrollbars(WB2KWindow* the_surface)
{	
	//DEBUG_OUT(("Window_ResetScrollbars %d: content left=%i, avail=%li, needed=%li", __LINE__, the_surface->content_left_, the_surface->inner_width_, the_surface->required_inner_width_));
	
	
	// hide or show the scrollbars, depending on need
	if (the_surface->inner_width_ < the_surface->required_inner_width_)
	{
		// if scrollbar not already visible, add it to the layout. if visible, adjust it's current/max values
		if (the_surface->h_scroller_visible_ == true)
		{
			Gadget_SetTop(the_surface->gadgets_[MAIN_GID_H_SCROLL], the_surface, the_surface->required_inner_width_, the_surface->inner_width_, the_surface->content_left_);
		}
		else
		{
			// LOGIC:
			//   Create a new scrollbar object, and then use AddGList to attach it to the end of the list of gadgets
			//   it is necessary to call RefreshGadgets() here, but not necessary to explicitly refresh the window frame. 
			//   we need to dynamically get the width/height of the resize gadget in case it has changed
			
			struct Screen*	scr = global_app->screen_;
			// NOTE: these MUST be "ULONG" as of 3.2, based on how VBCC defines unsigned long
			// See: https://eab.abime.net/showthread.php?p=1490903#post1490903
			ULONG			win_resize_gadget_width = 18;	
			ULONG			win_resize_gadget_height = 11;	// Default size (pre 3.2?)
			struct Image*	win_resize_image;

			// figure out how big the Window sizing gadget image is, so we can have scrollbars not collide with it
			if ( (win_resize_image = NewObject (NULL, (STRPTR)SYSICLASS, SYSIA_Which, SIZEIMAGE, SYSIA_DrawInfo, the_surface->draw_info_, TAG_DONE)) != NULL)
			{
				// Get size gadget geometry
				GetAttr (IA_Width, (APTR)win_resize_image, &win_resize_gadget_width);
				GetAttr (IA_Height, (APTR)win_resize_image, &win_resize_gadget_height);

				// don't need the image object any more
				DisposeObject (win_resize_image);
			}
			
			if ( (the_surface->gadgets_[MAIN_GID_H_SCROLL] = NewObject (NULL, (STRPTR)PROPGCLASS,
				GA_ID,				MAIN_GID_H_SCROLL,
				GA_RelBottom,		- win_resize_gadget_height + ((win_resize_gadget_height > 15) ? 4 : 3),
				GA_Left,			scr->WBorLeft,
				GA_Height,			win_resize_gadget_height - ((win_resize_gadget_height > 15)  ? 6 : 4),
				GA_RelWidth,		- (win_resize_gadget_width + scr->WBorLeft + 3),
				GA_BottomBorder,	TRUE,
				GA_DrawInfo,		the_surface->draw_info_,
				PGA_Freedom,		FREEHORIZ,
				PGA_Borderless,		((the_surface->draw_info_->dri_Flags & DRIF_NEWLOOK) && (the_surface->draw_info_->dri_Depth != 1)),
				PGA_NewLook,		TRUE,
				PGA_Total,			the_surface->required_inner_width_,
				PGA_Visible,		the_surface->inner_width_,
				PGA_Top,			the_surface->content_left_,
				ICA_TARGET,			ICTARGET_IDCMP,
				TAG_DONE)) == NULL)
			{
				LOG_ERR(("Window_ResetScrollbars %d: couldn't create H scroller object", __LINE__));
				App_Destroy(); // crash early, crash often
			}

			AddGList(the_surface->window_, (struct Gadget *)the_surface->gadgets_[MAIN_GID_H_SCROLL], (UWORD)~0, 1, NULL);
			RefreshGadgets((APTR)the_surface->gadgets_[MAIN_GID_H_SCROLL], the_surface->window_, NULL);
			the_surface->h_scroller_visible_ = true;
			DEBUG_OUT(("Window_ResetScrollbars %d: added H scroller", __LINE__));
 		}
	}
	else
	{
		// if scrollbar is visible, remove it from the layout
		if (the_surface->h_scroller_visible_ == true)
		{
			// LOGIC:
			//   Use RemoveGList to remove the scroll bar, then refresh the window frame explicitly
			//   If window frame isn't refreshed on removal, the scroll bar visuals will stay until next time window is resized or made inactive/active.
			//     When adding the scrollbar, refreshing the window frame is not necessary. Calling RefreshGadgets() here does not help.
			
			RemoveGList( the_surface->window_, (struct Gadget *)the_surface->gadgets_[MAIN_GID_H_SCROLL], 1L );
			RefreshWindowFrame(the_surface->window_);
			the_surface->gadgets_[MAIN_GID_H_SCROLL] = NULL;
			the_surface->h_scroller_visible_ = false;
		}
	}
}
Attached Thumbnails
Click image for larger version

Name:	Screen Shot 2021-07-10 wb2k reaction scrollbars.png
Views:	105
Size:	32.2 KB
ID:	72500  
Warty is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
ClassAct/Reaction & Eclipse/GCC Cross Chain Warty Coders. C/C++ 3 15 August 2020 18:41
installing Classact and Reaction on 3.1.4 dschallock support.Other 8 06 February 2020 19:19
Shared configuration Anubis support.FS-UAE 2 25 April 2016 23:51
Update ReAction classes mritter0 Coders. General 2 30 July 2014 01:55
Proper ReAction coding mritter0 Coders. C/C++ 3 24 April 2014 07:34

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 10:05.

Top

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Page generated in 0.99493 seconds with 14 queries