1//------------------------------------------------------------------------------
2//	Copyright (c) 2001-2002, Haiku
3//
4//	Permission is hereby granted, free of charge, to any person obtaining a
5//	copy of this software and associated documentation files (the "Software"),
6//	to deal in the Software without restriction, including without limitation
7//	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8//	and/or sell copies of the Software, and to permit persons to whom the
9//	Software is furnished to do so, subject to the following conditions:
10//
11//	The above copyright notice and this permission notice shall be included in
12//	all copies or substantial portions of the Software.
13//
14//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19//	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20//	DEALINGS IN THE SOFTWARE.
21//
22//	File Name:		CurView.cpp
23//	Author:			DarkWyrm <bpmagic@columbus.rr.com>
24//	Description:	cursor handler for the app
25//
26//------------------------------------------------------------------------------
27#include <OS.h>
28#include <Directory.h>
29#include <Alert.h>
30#include <storage/Path.h>
31#include <Entry.h>
32#include <File.h>
33#include <stdio.h>
34#include "CursorWhichItem.h"
35#include "CurView.h"
36#include <PortLink.h>
37#include "defs.h"
38#include "ServerConfig.h"
39#include <ServerProtocol.h>
40#include <PortMessage.h>
41#include <InterfaceDefs.h>
42#include <TranslationUtils.h>
43
44//#define DEBUG_CURSORSET
45
46#define SAVE_CURSORSET 'svcu'
47#define DELETE_CURSORSET 'dlcu'
48#define LOAD_CURSORSET 'ldcu'
49#define CURSOR_UPDATED 'csru'
50
51CurView::CurView(const BRect &frame, const char *name, int32 resize, int32 flags)
52	:BView(frame,name,resize,flags), settings(B_SIMPLE_DATA)
53{
54	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
55
56
57	cursorset=new CursorSet("Default");
58
59/*
60	// Code disabled -- cursor set management belongs in another app
61	BMenuBar *mb=new BMenuBar(BRect(0,0,Bounds().Width(),16),"menubar");
62
63	settings_menu=new BMenu("Settings");
64	settings_menu->AddItem(new BMenuItem("Save Cursor Set",new BMessage(SAVE_CURSORSET),'S'));
65	settings_menu->AddSeparatorItem();
66	settings_menu->AddItem(new BMenuItem("Delete Cursor Set",new BMessage(DELETE_CURSORSET)));
67	mb->AddItem(settings_menu);
68
69	cursorset_menu=LoadCursorSets();
70	if(cursorset_menu)
71		mb->AddItem(cursorset_menu);
72	else
73	{
74		// We should *never* be here, but just in case....
75		cursorset_menu=new BMenu("Cursor Sets");
76		mb->AddItem(cursorset_menu);
77	}
78	AddChild(mb);
79*/
80	BRect wellrect(0,0,20,20);
81	wellrect.OffsetTo(10,25);
82	wellrect.right=wellrect.left+50;
83
84/*
85	// Code disabled -- cursor set management belongs in another app
86	cursorset_label=new BStringView(wellrect,"cursorset_label","Cursor Set: ");
87	AddChild(cursorset_label);
88	cursorset_label->ResizeToPreferred();
89	cursorset_name="<untitled>";
90*/
91
92	// Set up list of cursor attributes
93	BRect cvrect;
94	BRect rect;
95
96	cvrect.Set(0,0,75,75);
97
98	bmpview=new BitmapView(BPoint(10,10),new BMessage(CURSOR_UPDATED),this);
99	bmpview->MoveTo( (Bounds().Width()-bmpview->Bounds().Width())/2,30);
100	AddChild(bmpview);
101
102
103	rect.left=(Bounds().Width()-200)/2;
104	rect.right=rect.left+190;
105	rect.top=bmpview->Frame().bottom+30;
106	rect.bottom=rect.top+100;
107
108	attrlist=new BListView(rect,"AttributeList");
109
110	scrollview=new BScrollView("ScrollView",attrlist, B_FOLLOW_LEFT |
111		B_FOLLOW_TOP, 0, false, true);
112	AddChild(scrollview);
113	scrollview->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
114
115	attrlist->SetSelectionMessage(new BMessage(ATTRIBUTE_CHOSEN));
116
117	attrlist->AddItem(new CursorWhichItem(B_CURSOR_DEFAULT));
118	attrlist->AddItem(new CursorWhichItem(B_CURSOR_TEXT));
119	attrlist->AddItem(new CursorWhichItem(B_CURSOR_MOVE));
120	attrlist->AddItem(new CursorWhichItem(B_CURSOR_DRAG));
121	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE));
122	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE_NWSE));
123	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE_NESW));
124	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE_NS));
125	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE_EW));
126
127
128	cvrect.Set(0,0,60,30);
129	cvrect.OffsetTo( (Bounds().Width()-200)/2,scrollview->Frame().bottom+20);
130
131	defaults=new BButton(cvrect,"DefaultsButton","Defaults",
132		new BMessage(DEFAULT_SETTINGS),B_FOLLOW_LEFT |B_FOLLOW_TOP,
133		B_WILL_DRAW | B_NAVIGABLE);
134	AddChild(defaults);
135
136	cvrect.OffsetBy(70,0);
137	revert=new BButton(cvrect,"RevertButton","Revert",
138		new BMessage(REVERT_SETTINGS),B_FOLLOW_LEFT |B_FOLLOW_TOP,
139		B_WILL_DRAW | B_NAVIGABLE);
140	AddChild(revert);
141	revert->SetEnabled(false);
142
143	cvrect.OffsetBy(70,0);
144	apply=new BButton(cvrect,"ApplyButton","Apply",
145		new BMessage(APPLY_SETTINGS),B_FOLLOW_LEFT |B_FOLLOW_TOP,
146		B_WILL_DRAW | B_NAVIGABLE);
147	AddChild(apply);
148	apply->SetEnabled(false);
149
150	BEntry entry(COLOR_SET_DIR);
151	entry_ref ref;
152	entry.GetRef(&ref);
153
154	attribute=B_PANEL_BACKGROUND_COLOR;
155	attrstring="Background";
156	LoadSettings();
157
158}
159
160CurView::~CurView(void)
161{
162	delete cursorset;
163}
164
165void CurView::AllAttached(void)
166{
167	attrlist->Select(0);
168	attrlist->SetTarget(this);
169	apply->SetTarget(this);
170	defaults->SetTarget(this);
171	revert->SetTarget(this);
172	bmpview->SetTarget(this);
173	BMessenger msgr(this);
174}
175
176void CurView::MessageReceived(BMessage *msg)
177{
178	if(msg->WasDropped())
179	{
180	}
181
182	switch(msg->what)
183	{
184		case CURSOR_UPDATED:
185		{
186			CursorWhichItem *cwi=(CursorWhichItem*)attrlist->ItemAt(attrlist->CurrentSelection());
187			if(cwi)
188				cwi->SetBitmap(bmpview->GetBitmap());
189
190			break;
191		}
192		case ATTRIBUTE_CHOSEN:
193		{
194			CursorWhichItem *cwi=(CursorWhichItem*)attrlist->ItemAt(attrlist->CurrentSelection());
195			if(cwi)
196			{
197				bmpview->SetBitmap(cwi->GetBitmap());
198				bmpview->Invalidate();
199			}
200
201			break;
202		}
203		default:
204			BView::MessageReceived(msg);
205			break;
206	}
207}
208
209/*
210// Code disabled -- cursor set management belongs in another app
211BMenu *CurView::LoadCursorSets(void)
212{
213#ifdef DEBUG_CURSORSET
214printf("Loading cursor sets from disk\n");
215#endif
216	// This function populates the member menu *cursorset_menu with the cursor
217	// set files located in the cursor set directory. To ensure that there are
218	// no entries pointing to invalid cursor sets, they are validated before
219	// a menu item is added.
220	BDirectory dir;
221	BEntry entry;
222	BPath path;
223	BString name;
224
225	BMenu *menu=new BMenu("Cursor Sets");
226
227	status_t dirstat=dir.SetTo(COLOR_SET_DIR);
228	if(dirstat!=B_OK)
229	{
230		// We couldn't set the directory, so more than likely it just
231		// doesn't exist. Create it and return an empty menu.
232		switch(dirstat)
233		{
234			case B_NAME_TOO_LONG:
235			{
236				BAlert *a=new BAlert("Haiku","Couldn't open the folder for cursor sets. "
237					"You will be able to change system cursors, but be unable to save them to a cursor set. "
238					"Please contact Haiku about Appearance Preferences::CurView::"
239					"LoadCursorSets::B_NAME_TOO_LONG for a bugfix", "OK",
240					 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
241				a->SetFlags(a->Flags() | B_CLOSE_ON_ESCAPE);
242				a->Go();
243				break;
244			}
245			case B_ENTRY_NOT_FOUND:
246			{
247				create_directory(COLOR_SET_DIR,0777);
248				break;
249			}
250			case B_BAD_VALUE:
251			{
252				printf("CurView::LoadCursorSets(): Invalid cursorset folder path.\n");
253				break;
254			}
255			case B_NO_MEMORY:
256			{
257				printf("CurView::LoadCursorSets(): No memory left. We're probably going to crash now. \n");
258				break;
259			}
260			case B_BUSY:
261			{
262				printf("CurView::LoadCursorSets(): Busy node " CURSOR_SET_DIR "\n");
263				break;
264			}
265			case B_FILE_ERROR:
266			{
267				BAlert *a=new BAlert("Haiku","Couldn't open the folder for cursor sets "
268					"because of a file error. Perhaps there is a file (instead of a folder) at " COLOR_SET_DIR
269					"? You will be able to change system cursors, but be unable to save them to a cursor set. ",
270					"OK", NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
271				a->SetFlags(a->Flags() | B_CLOSE_ON_ESCAPE);
272				a->Go();
273				break;
274			}
275			case B_NO_MORE_FDS:
276			{
277				BAlert *a=new BAlert("Haiku","Couldn't open the folder for cursor sets "
278					"because there are too many open files. Please close some files and restart "
279					" this application.", "OK",
280					 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
281				a->SetFlags(a->Flags() | B_CLOSE_ON_ESCAPE);
282				a->Go();
283				if(Window())
284					Window()->PostMessage(B_QUIT_REQUESTED);
285				break;
286			}
287		}
288
289		return menu;
290	}
291
292	int32 count=dir.CountEntries();
293
294
295	BMessage *msg;
296	CursorSet cs(NULL);
297
298	for(int32 i=0;i<count;i++)
299	{
300		dir.GetNextEntry(&entry);
301		entry.GetPath(&path);
302
303		if(cs.Load(path.Path())!=B_OK)
304			continue;
305
306		// Don't include the default set in the menu
307		name=path.Leaf();
308		if(name.Compare("Default")==0)
309			continue;
310
311		name=path.Path();
312
313		name.Remove(0,name.FindLast('/')+1);
314
315		msg=new BMessage(LOAD_CURSORSET);
316		msg->AddString("name",name);
317		menu->AddItem(new BMenuItem(name.String(),msg));
318	}
319
320	return menu;
321}
322
323void CurView::SetCursorSetName(const char *name)
324{
325	if(!name)
326		return;
327	BString namestr("Cursor Set: ");
328	cursorset_name=name;
329	namestr+=name;
330	cursorset_label->SetText(namestr.String());
331	cursorset_label->ResizeToPreferred();
332	cursorset_label->Invalidate();
333}
334*/
335
336void CurView::SaveSettings(void)
337{
338	// Save the current GUI cursor settings to the GUI cursors file in the
339	// path specified in defs.h
340
341	BString path(SERVER_SETTINGS_DIR);
342	path+=CURSOR_SETTINGS_NAME;
343#ifdef DEBUG_CURSORSET
344printf("SaveSettings: %s\n",path.String());
345#endif
346	cursorset->Save(path.String(),B_CREATE_FILE|B_ERASE_FILE);
347
348//	prev_set_name=cursorset_name;
349	revert->SetEnabled(false);
350}
351
352void CurView::LoadSettings(void)
353{
354	// Load the current GUI cursor settings from disk. This is done instead of
355	// getting them from the server at this point for testing purposes. Comment
356	// out the #define LOAD_SETTINGS_FROM_DISK line to use the server query code
357#ifdef DEBUG_CURSORSET
358printf("Loading settings from disk\n");
359#endif
360	settings.MakeEmpty();
361
362	BDirectory dir,newdir;
363	if(dir.SetTo(SERVER_SETTINGS_DIR)==B_ENTRY_NOT_FOUND)
364	{
365#ifdef DEBUG_CURSORSET
366printf("Cursor set folder not found. Creating %s\n",SERVER_SETTINGS_DIR);
367#endif
368		create_directory(SERVER_SETTINGS_DIR,0777);
369	}
370
371	BString path(SERVER_SETTINGS_DIR);
372	path+=CURSOR_SETTINGS_NAME;
373
374	status_t stat=cursorset->Load(path.String());
375
376	if(stat!=B_OK)
377	{
378#ifdef DEBUG_CURSORSET
379printf("Couldn't open file %s for read\n",path.String());
380#endif
381		SetDefaults();
382		SaveSettings();
383	}
384	return;
385}
386
387void CurView::SetDefaults(void)
388{
389	// The server will perform the necessary work to set defaults, so just ask it to do the
390	// work for us. It is a synchronous procedure, so we will notify the server and load the cursor
391	// set 'Default'.
392	BString string(CURSOR_SET_DIR);
393	string+="Default";
394
395	cursorset->Load(string.String());
396
397	port_id port=find_port(SERVER_PORT_NAME);
398	if(port==B_NAME_NOT_FOUND)
399		return;
400
401	BPrivate::PortLink link(port);
402	int32 code;
403
404	link.StartMessage(AS_SET_SYSCURSOR_DEFAULTS);
405	link.Flush();
406	link.GetNextMessage(code);
407
408}
409
410BitmapView::BitmapView(const BPoint &pt,BMessage *message, const BHandler *handler, const BLooper *looper=NULL)
411 : BBox(BRect(0,0,75,75).OffsetToCopy(pt),"bitmapview",B_FOLLOW_NONE,B_WILL_DRAW, B_PLAIN_BORDER),
412 	BInvoker(message,handler,looper)
413{
414	SetFont(be_plain_font);
415	bitmap=NULL;
416	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
417	SetDrawingMode(B_OP_ALPHA);
418	drawrect=Bounds().InsetByCopy(5,5);
419}
420
421BitmapView::~BitmapView(void)
422{
423}
424
425void BitmapView::SetBitmap(BBitmap *bmp)
426{
427	bitmap=bmp;
428}
429
430void BitmapView::Draw(BRect r)
431{
432	BBox::Draw(r);
433
434	if(bitmap)
435		DrawBitmap(bitmap, drawrect);
436}
437
438void BitmapView::MessageReceived(BMessage *msg)
439{
440	if(msg->WasDropped())
441	{
442		entry_ref ref;
443		if(msg->FindRef("refs",&ref)!=B_OK)
444			return;
445
446		BBitmap *tmp=BTranslationUtils::GetBitmap(&ref);
447		if(tmp)
448		{
449			delete bitmap;
450			bitmap=tmp;
451
452			int32 offset;
453			BRect r(Bounds());
454			r.right-=10;
455			r.bottom-=10;
456
457			drawrect=bitmap->Bounds();
458			if(r.Contains(bitmap->Bounds()))
459			{
460				// calculate a centered rect for direct display
461				offset=((r.IntegerWidth()-bitmap->Bounds().IntegerWidth()) >> 1)+5;
462				drawrect.OffsetBy(offset,offset);
463			}
464			else
465			{
466				// calculate a scaled-down rectangle for display
467				drawrect.left=drawrect.top=0;
468
469				if(bitmap->Bounds().Height() > bitmap->Bounds().Width())
470				{
471					drawrect.right=(r.Height()*bitmap->Bounds().Width())/bitmap->Bounds().Height();
472					drawrect.bottom=r.Height();
473					offset=((r.IntegerWidth()-drawrect.IntegerWidth()) >> 1)+5;
474					drawrect.OffsetBy(offset,5);
475				}
476				else
477				{
478					drawrect.bottom=(r.Width()*bitmap->Bounds().Height())/bitmap->Bounds().Width();
479					drawrect.right=r.Width();
480					offset=((r.IntegerHeight()-drawrect.IntegerHeight()) >> 1)+5;
481					drawrect.OffsetBy(5,offset);
482				}
483			}
484			Invoke();
485			Invalidate();
486			return;
487		}
488	}
489	else
490		BBox::MessageReceived(msg);
491}
492
493