// qps -- Qt-based visual process status monitor
// This program is free software. See the file COPYING for details.
// Author:  Mattias Engdegård, 1997-1999
//          fasthyun@magicn.com 2005-2012

#define QPS_VERSION "1.10.12.2"

// TODO & MEMO
// . Memory Hole or Qt's string allocate bug?
// . clipboard copy [CMDLINE,PID,USERNAME...]
// .  setSort,setTree system redesign *
// . [klog] table cache.
// . infobox out of window! -> 
// . save sort of tree, linear
// . watchdog showmsg substitude 
// . command edit wo
// . history system , double free 
// . Field Test !
// . remove_field () segfault ? 
// . cmd [w] analsys: [IDLE] [WHAT]
// . ExecWindow - [running] finish.
// . UNIX Domain SOCKET
// . COLOR orange FF5d00
// . P_MEM  -> P_%MEM or P_PMEM


#include "./icon/icon.xpm" 

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/utsname.h>    // uname 
#include <signal.h>
#include <errno.h>
#include <sched.h>
#include <unistd.h> 	//for sleep


#include "qps.h"
#include "dialogs.h"
#include "lookup.h"
#include "misc.h"
#include "screenshot.h"

#ifndef USING_PCH
#include <QBitmap>
#include <QTimerEvent>
#include <QVBoxLayout>
#include <QShortcut>
#include <QMenuBar>
#include <QFont>
#include <QToolTip>
#include <QTabWidget> 
#include <QApplication>
#include <QCheckBox>
#include <QToolButton>
#include <QMessageBox>
#include <QSessionManager> 
#include <QTextEdit>
//#include <QSystemTrayIcon>
#endif 


/* --------------------- Global Variable START ---------------------- */
QList<Command*>	commands;

bool 	previous_flag_show_thread_prev=false;	    // previous state
bool 	flag_show_thread=false;		    // to see informations at the thread level
int 	flag_thread_ok=true;		    // we presume a kernel 2.6.x using NPTL
bool 	flag_session_start=false;
bool 	flag_start_mini=false;		// Need for Xorg.Session
bool	flag_refresh=true;			// DEL
bool	flag_xcompmgr=false;		// DEL test.compiz.. 
bool 	flag_devel= false ;
bool	flag_schedstat=false;		
bool 	flag_smallscreen=false;
bool 	flag_firstRun=true;			// test

Qps 			*qps;
//ControlBar 		*controlbar=NULL;
SearchBox 		*search_box=NULL;
TFrame 			*infobox=NULL;		    // testing
Screenshot 		*screenshot=NULL;
QFontComboBox 	*font_cb=NULL;
WatchdogDialog 	*watchdogDialog=NULL;
		
QTabWidgetX *tbar;

#include "trayicon.h"
TrayIcon *trayicon=NULL;


// for Non-ASCII Languages (CJK:chinese,japanese,korean, arabic ...) 
#include <QTextCodec> 
QTextCodec * codec = NULL;
#define UniString(str)  str //codec->toUnicode(str)
/* ------------------------ END global variables -------------------------- */

// default values of settings, overridden by $HOME/.qpsrc if present
bool 	Qps::flag_show= true ;	 //window state of last run : mini(iconic) or normal window 
bool 	Qps::flag_exit= true ;		
bool	Qps::flag_qps_hide=true;			// TEST	
bool 	Qps::flag_useTabView=false;
bool 	Qps::show_file_path = FALSE;
bool 	Qps::show_cmd_path = TRUE;
bool 	Qps::show_infobar = TRUE;
bool 	Qps::show_ctrlbar = TRUE;
bool 	Qps::show_cpu_bar = TRUE; 
bool 	Qps::show_load_graph = TRUE;
bool 	Qps::show_statusbar = true; 
bool 	Qps::load_in_icon = TRUE;
bool 	Qps::auto_save_options = TRUE;
#ifdef LINUX
bool	Qps::hostname_lookup = TRUE;
bool	Qps::service_lookup = TRUE;
#endif
bool	Qps::pids_to_selection = TRUE;
bool	Qps::vertical_cpu_bar = FALSE; // not used
#ifdef SOLARIS
bool    Qps::normalize_nice = TRUE;
bool    Qps::use_pmap = TRUE;
#endif
bool 	Qps::tree_gadgets = TRUE;
bool 	Qps::tree_lines = TRUE;
int 	Qps::swaplimit = 5;	// default: warn when less than 10% swap left
bool 	Qps::swaplim_percent = TRUE;


QThread *thread_main=0;

Qps::Qps()
{
	setObjectName("qps_main_window");
	timer_id = 0;
	field_win = 0;
	prefs_win = 0;
	command_win = 0;
	default_icon = 0;
	default_icon_set = FALSE;

	setIconSize(22,22);
//	setMouseTracking(true); // no need?	
	
	if(flag_devel)
	{	
		thread_main=thread(); //Test
		printf("qps thread =%X , qApp thread =%X\n",thread(),QApplication::instance()->thread());
	}

	//watchdogDialog = new WatchdogDialog; //Memory Leak

//	font_cb=new QFontComboBox(this); // preload
//	font_cb->setWritingSystem ( QFontDatabase::Latin );	font_cb->hide();

	make_signal_popup_menu();

	// Move to Pstable ? yes 
	m_headpopup = new QMenu("header_popup",this);
	m_headpopup->addAction("Remove Field", this, SLOT(menu_remove_field()));
	m_fields = new QMenu("Add Field",this);
	m_headpopup->addMenu(m_fields);
	//connect(m_fields, SIGNAL(activated(int)), SLOT(add_fields_menu(int)));
	//m_headpopup->addAction("Select Field", this, SLOT(menu_custom()) );

	m_command = new QMenu("Command",this);	// filled in later
	
	QAction *act;

	m_view = new QMenu("View",this); 
	act=m_view->addAction("Process"); //act->setData(Procview::CUSTOM);
	act=m_view->addAction("Log"); //act->setData(Procview::CUSTOM);


	m_field = new QMenu("Field",this); 
	act=m_field->addAction("Custom Fields"); act->setData(Procview::CUSTOM);
	act=m_field->addAction("Basic Fields ");	act->setData(Procview::USER);
	act=m_field->addAction("Jobs Fields ");	act->setData(Procview::JOBS);
	act=m_field->addAction("Memory Fields "); act->setData(Procview::MEM);
#ifdef LINUX
	act=m_field->addAction("Scheduling Fields "); act->setData(Procview::SCHED);
#endif

	m_field->addSeparator();
//	act=m_field->addAction(QtIconLoader::icon("edit-find-replace"),"Save Current as Custom Fields",this, SLOT(menu_custom()));
	act=m_field->addAction(QtIconLoader::icon("edit-find-replace"),"Select Custom Fields...",this, SLOT(menu_custom()));
	act->setData(MENU_CUSTOM);
	
	connect(m_field, SIGNAL(triggered(QAction *)),this, SLOT(view_menu(QAction *)));
	connect(m_field, SIGNAL(aboutToShow ()), SLOT(update_menu_status()));
	
	connect(m_view, SIGNAL(triggered(QAction *)),this, SLOT(view_menu(QAction *)));
	connect(m_view, SIGNAL(aboutToShow ()), SLOT(update_menu_status()));

	m_options = new QMenu("Option",this);
	m_options->addAction("Update Period...", this, SLOT(menu_update()));
	m_options->addSeparator();
	act=m_options->addAction("", /* MENU_PATH */ this, SLOT(menu_toggle_path()));
 	act->setData(QVariant(MENU_PATH));
	act=m_options->addAction("", this, SLOT(menu_toggle_infobar()));
 	act->setData(QVariant(MENU_INFOBAR));
	act=m_options->addAction("", this, SLOT(menu_toggle_ctrlbar()));
 	act->setData(QVariant(MENU_CTRLBAR));
	act=m_options->addAction("Show Status bar", this, SLOT(menu_toggle_statusbar()));
 	act->setData(QVariant(MENU_STATUS));
	act=m_options->addAction("", this, SLOT(menu_toggle_cumul()));
 	act->setData(QVariant(MENU_CUMUL));

	m_options->addSeparator(); 
	//m_options->addAction(QtIconLoader::icon("gtk-preferences" /* gnome-settings */),"Preferences...", this, SLOT(menu_prefs())); //MENU_PREFS
	m_options->addAction(QIcon(":icon/preferences-system.png" ),"Preferences...", this, SLOT(menu_prefs())); //MENU_PREFS

	connect(m_options, SIGNAL(aboutToShow ()), SLOT(update_menu_status()));


	QMenu *m_help = new QMenu("Help",this);
	//m_help->addAction("FAQ", this, SLOT(license()));
	//m_help->addAction("About", this, SLOT(about())); //gtk-help,help
	m_help->addAction(QtIconLoader::icon("help-about"),"Feeedback", this, SLOT(about()));
	
	//menu = new QMenuBar(this);
	menubar = new QMenuBar;
	menubar->addMenu(m_command);
	am_view=menubar->addMenu(m_view);
	menubar->addMenu(m_field);
	menubar->addMenu(m_options);
	menubar->addSeparator();
	menubar->addMenu(m_help);
		
	ctrlbar = new ControlBar(this);
	//controlbar=ctrlbar;

	connect(ctrlbar, SIGNAL(need_refresh()), SLOT(refresh()));
	connect(ctrlbar, SIGNAL(viewChange(QAction*) ), SLOT(view_menu(QAction*)));	//?????

	context_col = -1;
	
	procview = new Procview(); // refresh() only not rebuild() 
#ifdef HTABLE2
	pstable = new Pstable2(this,procview); //
#else
	pstable = new Pstable(this,procview); // no refresh()
#endif
	PDisplay *display=new PDisplay(this);
	infobar=display->addSystem(procview);
//.	infobar = new Infobar(this,procview); // graph_bar
//	infobar = new Infobar(procview); // graph_bar
	statusBar= new StatusBar(this); 	
	
	if(!read_settings()) 
	{
		//printf("default value\n");
		set_update_period(1300);	// default
		resize(640, 370);			// default initial size
	}

	set_table_mode(procview->treeview); //  Pstable::refresh() occur
	make_command_menu(); // after qpsrc reading

	// misc. accelerators
	QShortcut *c1=new QShortcut ( Qt::CTRL + Qt::Key_Q, this, SLOT(save_quit()) );
	QShortcut *c3=new QShortcut ( Qt::CTRL + Qt::Key_Space, ctrlbar->pauseButton, SLOT(click()) );
	QShortcut *c2=new QShortcut ( Qt::CTRL + Qt::Key_L, pstable, SLOT(repaintAll()) );
	
	// move to pstable better?
	connect(pstable, SIGNAL(doubleClicked(int)), SLOT(open_details(int)));
	connect(pstable, SIGNAL(rightClicked(QPoint)), this, SLOT(show_popup_menu(QPoint)));
	connect(pstable->header(), SIGNAL(rightClicked(QPoint, int)),this, SLOT(context_heading_menu(QPoint, int)));	
//	connect(netable, SIGNAL(rightClicked(QPoint)), this, SLOT(context_row_menu(QPoint)));

	
	selection_items_enabled = TRUE; // ????
	update_load_time = 0;
	//// update_menu_status();
	
	
	QVBoxLayout *vlayout = new QVBoxLayout;
	vlayout->setMargin(0);
	if(flag_useTabView)
		vlayout->setSpacing(0);
	else vlayout->setSpacing(1);
	
	
	//vlayout->addWidget(menu);
	vlayout->setMenuBar(menubar);
	vlayout->addWidget( display);
	//vlayout->addWidget( infobar);
	vlayout->addWidget( ctrlbar);
	
	/// vlayout->addSpacing(5);
	
	//if(flag_devel){
	if(0){
		if(0){
		QSplitter *splitter = new QSplitter(Qt::Vertical);
		QDockWidget *dock = new QDockWidget(tr("Detail"), this);
		dock->setFeatures ( QDockWidget::DockWidgetClosable );
		//dock->setWidget();
		//dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
		//customerList = new QListWidget(dock);
		//vlayout->addStretch();
		splitter->addWidget(pstable);
		splitter->addWidget(dock);
		vlayout->addWidget( splitter);
		vlayout->addWidget( statusBar);
		setLayout(vlayout);
		}
	}

	logbox=new QTextEdit(this);	
	logbox->setReadOnly(true);
	
	if(1) //if(flag_smallscreen==false)
	{
		//
		tbar = new QTabWidgetX(this);
		tbar->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Expanding);
		//tbar->setMargin(2);
		tbar->addTab(pstable, "Process");
		tbar->addTab(logbox, "Log");
		//tbar->setDocumentMode (false); // QT 4.5
		//tbar->addTab(0, "&Files ");
		//tbar->addTab(0, "&Modules ");
		//tbar->addTab(0, "&Users ");
		//netable = new Netable(this,procview);
		//tbar->addTab(netable, " &Network ");
		vlayout->addWidget(tbar);
		connect(tbar,SIGNAL(currentChanged (int)),SLOT(tabChanged(int)));
	}
	else 
	{	
		//test
		QStackedWidget *stack; 
		stack=new QStackedWidget(this);
		stack->addWidget(pstable);
		stack->addWidget(logbox);
		//vlayout->addWidget(pstable);
		vlayout->addWidget(stack);
		//netable=NULL;
	}

	vlayout->addWidget(statusBar);
	setLayout(vlayout);
	
	infobox	=new TFrame(this);
//	setAttribute(Qt::WA_ShowWithoutActivating);
	if(update_period != eternity)
		Qps::update_timer();	//startTimer(update_period);
///	setFocusPolicy (Qt::WheelFocus); 
	
	bar_visibility(); // need 

	// testing
	popupx = new QMenu("test",this);
	popupx->addAction("Copied to Clipboard");
}

// explicit destructor needed for gcc
Qps::~Qps()
{}
void Qps::tabChanged(int idx)
{
	/*
	for(int i; i<1024;i++)
	{
		QFont font;
		if(font_cb)
			font=font_cb->currentFont();
		//font=font_cb->currentFont();
		QApplication::setFont(font);
	}

	printf("tab\n");
	*/

	if(idx==0) {
		procview->viewproc=Procview::ALL;
		pstable->refresh();
	//	pstable->update();
	}
	else 
    if(idx==1)
	{
		//procview->viewproc=Procview::NETWORK;
		//netable->refresh();
		//netable->update();
	}
}
// return true if all selected processes are stopped
bool Qps::all_selected_stopped()
{
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected && p->state != 'T')
			return FALSE;
	}
	return TRUE;
}

// Adjust menu to contain Stop or Continue
void Qps::adjust_popup_menu(QMenu *m, bool cont)
{
	//int idx = m->indexOf(MENU_DETAILS);
	if(procview->treeview==true)
	{
	//	m->setItemVisible (MENU_PARENT,false);
	//	m->setItemVisible (MENU_CHILD,false);
	//	m->setItemVisible (MENU_DYNASTY,false);
	}
	else	
	{
	//	m->setItemVisible (MENU_PARENT,true);
	//	m->setItemVisible (MENU_CHILD,true);
	//	m->setItemVisible (MENU_DYNASTY,true);
	}

	if(procview->viewproc == Procview::HIDDEN) 
	{
	//	m->setItemVisible (MENU_ADD_HIDDEN,false);
	//	m->setItemVisible (MENU_REMOVE_HIDDEN,true);
	}	
	else 
	{
	//	m->setItemVisible (MENU_ADD_HIDDEN,true);
	//	m->setItemVisible (MENU_REMOVE_HIDDEN,false);
	}
}

//
void Qps::adjust_popup_menu()
{
	bool allstop = all_selected_stopped();
	QList<QAction*> list=m_popup->actions();
	// printf("adjust_popup_menu () size=%d \n",list.size());

	if(allstop)
		for(int i =0 ;i< list.size() - 1 ;i++)
		{
			QAction *act=list[i];
			int id=act->data().toInt();
			if(id==MENU_SIGCONT)	act->setVisible(true);
			if(id==MENU_SIGSTOP)	act->setVisible(false);
		}
	else 
		for(int i =0 ;i< list.size() - 1 ;i++)
		{
			QAction *act=list[i];
			int id=act->data().toInt();
			if(id==MENU_SIGCONT)	act->setVisible(false);
			if(id==MENU_SIGSTOP)	act->setVisible(true);
		}
}

// build signal menu (used in two places)
QMenu *Qps::make_signal_popup_menu()
{
	
	//move to pstable?
	QAction *act; // show_popup_menu() callback
	m_popup = new QMenu("context popup",this);
	m_popup->addAction("Renice...", 	this, SLOT(menu_renice()));
	m_popup->addAction("Scheduling...", 	this, SLOT(menu_sched()));
	m_popup->addSeparator();
	m_popup->addAction("Terminate", this, SLOT(sig_term()),Qt::Key_Delete ); // better
	m_popup->addAction("Hangup", this, SLOT(sig_hup()),Qt::ALT + Qt::Key_H);
	m_popup->addAction("Kill", this, SLOT(sig_kill()),Qt::ALT + Qt::Key_K);
	act=m_popup->addAction("Stop", this, SLOT(sig_stop())); act->setData(MENU_SIGSTOP);
	act=m_popup->addAction("Continue", this, SLOT(sig_cont())); act->setData(MENU_SIGCONT);
	
//	connect(m_popup, SIGNAL(aboutToShow ()),this, SLOT(adjust_popup_menu()));
///	m_popup->addAction("Find Parent", 	this, SLOT(menu_parent()),0,MENU_PARENT);
///	m_popup->addAction("Find Children", 	this, SLOT(menu_children()),0,MENU_CHILD);
///	m_popup->addAction("Find Descendants", this, SLOT(menu_dynasty()),0,MENU_DYNASTY);



	QMenu *m = new QMenu("Other Signals");
	act=m->addAction("SIGINT (interrupt)"); act->setData(SIGINT);
	act=m->addAction("SIGCONT (continue)"); act->setData(SIGCONT);
	act=m->addAction("SIGSTOP (stop)"); act->setData(SIGSTOP);
	act=m->addAction("SIGQUIT (quit)"); act->setData(SIGQUIT);
	act=m->addAction("SIGILL (illegal instruction)"); act->setData(SIGILL);
	act=m->addAction("SIGABRT (abort)"); act->setData(SIGABRT);
	act=m->addAction("SIGFPE (floating point exception)"); act->setData(SIGFPE);
	act=m->addAction("SIGSEGV (segmentation violation)"); act->setData(SIGSEGV);
	act=m->addAction("SIGPIPE (broken pipe)"); act->setData(SIGPIPE);
	act=m->addAction("SIGALRM (timer signal)"); act->setData(SIGALRM);
	act=m->addAction("SIGUSR1 (user-defined 1)"); act->setData(SIGUSR1);
	act=m->addAction("SIGUSR2 (user-defined 2)"); act->setData(SIGUSR2);
	act=m->addAction("SIGCHLD (child death)"); act->setData(SIGCHLD);
	act=m->addAction("SIGTSTP (stop from tty)"); act->setData(SIGTSTP);
	act=m->addAction("SIGTTIN (tty input)"); act->setData(SIGTTIN);
	act=m->addAction("SIGTTOU (tty output)"); act->setData(SIGTTOU);

	connect(m, SIGNAL(triggered(QAction *)), SLOT(signal_menu(QAction *)));


	m_popup->addMenu(m);
	m_popup->addSeparator();
	m_popup->addAction("View Details", 	this, SLOT(Action_Detail()));


	return m;
}

void Qps::signal_menu(QAction *act )
{
	int signal=act->data().toInt();
	send_to_selected(signal);	
}


#ifdef MOSIX

// build migrate menu
static int intcmp(const int *a, const int *b)
{
	return *a - *b;
}

QMenu *Qps::make_migrate_menu()
{
	QString buf;
	QMenu *m = new QMenu;
	Svec<int> lst = Procinfo::mosix_nodes();
	lst.sort(intcmp); ///sort
	m->insertItem("Home", 1);
	m->insertItem("Find Best", 0);
	for(int i = 0; i < lst.size(); i++) {
		buf.sprintf("to node %d", lst[i]);
		m->insertItem(buf, lst[i] + 1);
	}
	return m;
}
#endif // MOSIX

// update the visibility of the {info, control, status} bar
void Qps::bar_visibility()
{
	
	if(show_infobar)
		infobar->show();
 	else 
		infobar->hide();

	if(show_ctrlbar) 
		ctrlbar->show();
	else 
		ctrlbar->hide();

	if(show_statusbar) 
		statusBar->show();
	else 
		statusBar->hide();

	if(flag_useTabView) 
	{
		tbar->showTab(true);
		am_view->setVisible(false);
	}
	else
	{
		tbar->showTab(false);
		am_view->setVisible(true);
	}
}

void Qps::timerEvent(QTimerEvent *e)
{
	Qps::refresh();
}
//
//dialogs.cpp:    qps->update_timer();
void Qps::update_timer()
{
	if(timer_id)
		killTimer(timer_id); 
	timer_id=startTimer(update_period); // *** important !!!
}

// change the update period, recomputing the averaging factor
void Qps::set_update_period(int milliseconds)
{
	if(milliseconds==0) 
	{	
		qDebug("DEBUG: set_update_period(): update= %dms \n",milliseconds);
		milliseconds=1000;
	}
	update_period = milliseconds;
	Procview::avg_factor =	exp(-(float)update_period / Procview::cpu_avg_time);
}

void Qps::refresh()
{
	if(flag_refresh==false) return;

	procview->refresh(); // important 
	infobar->update_load(); // add_point 

	if(isVisible()) {
		//if(search_box and !search_box->hasFocus())	search_box->setFocus(Qt::OtherFocusReason);
		pstable->refresh();
		// if(netable and netable->isVisible())netable->refresh(); 	
		if(show_infobar) infobar->refresh(); 
		//DEL update_menu_selection_status(); // update pop menu
	}

	if(trayicon) update_icon(); //make icon for systray 
	refresh_details();
}

// make next timer_refresh happen a little earlier to remove processes that
// might have died after a signal
void Qps::earlier_refresh()
{
	const int delay = 500;	// wait no more than this period (ms)
	if(update_period > delay && update_period != eternity) {
	}
}

void Qps::resizeEvent ( QResizeEvent * e )
{
	//DEBUG("Qps::resize() w=%d\n",e->size().width());
	//QWidget::resizeEvent(event);
	
	int w;
	return;

	//search_box->isVisible(true); // imidiate
	w=ctrlbar->sizeHint().width();
	if(!search_box->isVisible())
		w+=search_box->sizeHint().width();
	
	
	if(e->size().width()<w)
	{
		//ctrlbar->
		search_box->hide();
		
	}
//	else 	search_box->show();

	
	return ;
	if(e->size().width()<639)
	{
	//	tbar->setDrawBase (false);
		flag_smallscreen=true;
		statusBar->hide();
	//	void QTabWidget::setTabEnabled ( int index, bool enable )
	}
	else 
	{
		flag_smallscreen=false;
		statusBar->show();
	}
}

/*
 Description :
 	1. called when visible() or hide() state.  QT4
 	2. called when clicked  WINDOW_EXIT_X_BUTTON   
 	3. NOT called when user logout (session manager)  
*/
void Qps::closeEvent(QCloseEvent *e) 
{
	//DEBUG("DEBUG: closeEvent()....\n");
	//sleep(2);
	if( (Qps::flag_exit==false) and trayicon->hasSysTray())
	{
		e->ignore(); // dont close window!
		hide();
		return;
	}
	e->accept(); // ok. I will be exit Now !! 
	save_quit();
}

// call by void Qps::make_command_menu()
//			void signal_handler(int sig)
void Qps::save_quit() // will be removed !!!(by fasthyun@show.co.kr) 
{
	//printf("DEBUG: save_quit()....\n");
	close();	// if another window exists, then no exit. // occurs QCoseEvent!
	save_settings();
	qApp->quit();  // MACRO
}

// NEW Version !
// : write geometry, visible fields and other settings to $HOME/.qpsrc
void Qps::save_settings() 
{
	if(Qps::auto_save_options) 	write_settings();
}

void Qps::leaveEvent ( QEvent * event )  
{
	// printf("out!xx\n");	//works well
}

void Qps::enterEvent ( QEvent * event )   
{
	// printf("in!\n");	 // works well
}

void Qps::showEvent ( QShowEvent * event )
{
//	printf("showEvent()\n");	
//	event->accept();
} 
void Qps::mouseMoveEvent ( QMouseEvent * event ) 
{
//	printf("Qps::mouseMoveEvent\n"); //work in child surface  without setMouseTracking(true);	
}

// setFocusPolicy() should on
void Qps::focusInEvent ( QFocusEvent * event )
{
//	printf("focusIn\n"); // not work in compiz,Metacity
//	infobar->showup();
//	event->accept();
}

void Qps::focusOutEvent ( QFocusEvent * event )
{
//	printf("focusOut\n"); // not work in compiz,Metacity
//	event->accept();
}


void Qps::keyPressEvent ( QKeyEvent * event )
{
//	printf("Qps::key\n"); // not work in compiz
	if(search_box)
	{
		if(!search_box->hasFocus())		search_box->setFocus(Qt::OtherFocusReason);
		search_box->keyPressEvent(event);
	}
	//search_box->setFocusProxy(pstable);
	
}
void Qps::moveEvent ( QMoveEvent * event )
{
	infobar->refresh();
	infobar->showup();
	//printf("move\n");
}
void Qps::paintEvent(QPaintEvent *event)
{
//	printf("paintEvent : Qps\n");
//	update_icon();
}
void Qps::update_icon()
{
	if(load_in_icon) set_load_icon(); // always true
	else
		set_default_icon();
}
// called by update_icon()
void Qps::set_default_icon()
{
	if(!default_icon_set) {
		if(!default_icon)
			default_icon = new QPixmap((const char**)icon_xpm);
		setWindowIcon(*default_icon);
		default_icon_set = TRUE;
	}
}


void Qps::set_load_icon()
{
	QPixmap *pm = infobar->load_icon(icon_width, icon_height);
	if(!pm->mask())
	{
		// avoid a fvwm/Qt 1.30 problem and create a filled mask for the icon
		// (without mask, Qt would attempt to use a heuristically created mask)
		QBitmap bm(pm->size());
		bm.fill(Qt::color1);
		pm->setMask(bm);
	}
	if(trayicon->hasSysTray())
	{
			trayicon->setIcon(*pm);
			//QString str;
			//str.sprintf("Qps %2.2f%",Procinfo::loadQps);  
			//trayicon->setToolTip(str);
			//trayicon->showMessage("str",str);
	}
	else setWindowIcon(*pm); 
	default_icon_set = FALSE;
}


QPixmap *Qps::get_load_icon()
{
	return infobar->load_icon(icon_width, icon_height);
}

// 
void Qps::refresh_details()
{	
////	details.first();
	////Details d;
#ifdef LINUX
	// Proc::invalidate_sockets();
#endif

	for(int i=0;i< details.size(); ++i)
	{
		/////Details *d=details[i]
		////if(details[i].isVisible())
			/////details[i].refresh();
	}
	/*
	while((d = details.current()) != 0) {
		if(d->isVisible())
			d->refresh();
		details.next();
	} */
}


// called by 
//	1.void Qps::field_removed(int index)
//  ====> QMenu::aboutToShow () 
void Qps::update_menu_status()
{
//	printf("update_menu_status\n");
	update_menu_selection_status();
	//ctrlbar->view->setCurrentItem (procview->viewproc); //???

	// Field Menu 
	QList<QAction*> list=m_field->actions();
	for(int i =0 ;i< list.size() - 1 ;i++)
	{
		QAction *act=list[i];
		act->setCheckable(true);
		int id=act->data().toInt();
		if(id==procview->viewfields)
				act->setChecked(true);
		else 
				act->setChecked(false);
	}

	// View Menu temporary
	QString str=tbar->tabText(tbar->currentIndex());
	list=m_view->actions();
	for(int i =0 ;i< list.size() ;i++)
	{
		QAction *act=list[i];
		act->setCheckable(true);
	
		if(act->text()==str)
		{	
			act->setChecked(true);
		}
		else
			act->setChecked(false);
		
	}

	// Option Menu 	
	list=m_options->actions();
	for(int i =0 ;i< list.size() - 1 ;i++)
	{
		QAction *act=list[i];
		int id=act->data().toInt();
		
		if(id==MENU_PATH)
		{	
			act->setCheckable(true);
			act->setChecked(Procview::flag_show_file_path);
			act->setText("Show File Path");
		//	act->setText(Procview::flag_show_file_path? "Hide File Path" : "Show File Path");
		}
		else if(id==MENU_INFOBAR)
		{
			act->setCheckable(true);
			act->setChecked(show_infobar);
			act->setText("Show Graph");
			//act->setText(show_infobar 	? "Hide Graph" : "Show Graph");
		}
		else if(id==MENU_CTRLBAR)
		{
			act->setCheckable(true);
			act->setChecked(show_ctrlbar);
			act->setText("Show Control Bar");
			//act->setText(show_ctrlbar	? "Hide Control bar" : "Show Control Bar");
		}
		else if(id==MENU_STATUS)
		{
			act->setCheckable(true);
			act->setChecked(show_statusbar);
		}
		else if(id==MENU_CUMUL)
		{
			act->setCheckable(true);
			act->setChecked(Procview::flag_cumulative);
			act->setText("Include Child Times");
			//act->setText(Procview::flag_cumulative 	? "Exclude Child Times" : "Include Child Times");
		}
	}

}

// need to change name , redesign 
void Qps::view_menu(QAction *act)
{
	int id = act->data().toInt();
	if(act->text()=="Process") 
	{
		tbar->setCurrentIndex(0);
		return;
	}
	if(act->text()=="Log") 
	{	
		tbar->setCurrentIndex(1);
		return;
	}

	int state = id;
	if(id >= Procview::ALL && id <= Procview::HIDDEN) {
		if(procview->viewproc != state) {
			procview->viewproc = state;
		}
	}
	
	if(id >= Procview::USER && id <= Procview::CUSTOM) {
		if(procview->viewfields != state) {
			procview->viewfields = state;
			procview->set_fields();
		}
	}
	pstable->refresh(); // layout 
///	update_menu_status();
}


// called when selection changed & update time
// void Qps::update_menu_status()
// void Qps::make_command_menu()
//  DEL.
//  -> show_popup_menu()
void Qps::update_menu_selection_status()
{
	
//	bool enabled = (pstable->numSelected() > 0);
//	if(enabled) 
	if(0)
	{
		bool cont = all_selected_stopped();
		adjust_popup_menu(m_popup, cont);
	}
	
	for(int i = 0; i < commands.size(); i++)
	{
	/*	if (commands[i]->IsNeedProc()==false)
			m_command->setItemEnabled(MENU_FIRST_COMMAND + i, true);
		else	m_command->setItemEnabled(MENU_FIRST_COMMAND + i, enabled);
	*/} 
}

// call by SearchBox::keyPressEvent

//slot
void Qps::sig_term()
{
	send_to_selected(SIGTERM);
}

void Qps::sig_hup()
{
	send_to_selected(SIGHUP);
}

// need
void Qps::sig_stop()
{
	send_to_selected(SIGSTOP);
	
	//test
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected)
		{	p->test_stop=1;   // who ??
			//sendsig(p, sig);
		}
	}
}

void Qps::sig_cont()
{
	send_to_selected(SIGCONT);

	// test
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected)
			p->test_stop=0;
	}

}

void Qps::sig_kill()
{
	send_to_selected(SIGKILL);
}

void Qps::menu_custom()
{
////	view_menu(Procview::CUSTOM); // should !!
	if(field_win) {
		field_win->show();
		field_win->raise();
	} else {
		field_win = new FieldSelect(procview);
		setWindowGroup(field_win);
		field_win->show();
		connect(field_win, SIGNAL(added_field(int)), this, SLOT(field_added(int)));
		connect(field_win, SIGNAL(removed_field(int)),this, SLOT(field_removed(int)));
	}
}


// MOVE TO PSTABlE ? hmmm wait...
// Interface for  reading_setting()  
// SLOT:  
//
// called by 
// 	1.click Tree_checkbox  
//	2.void Qps::view_menu(int id)
void Qps::set_table_mode(bool treemode)
{
//	qDebug("set_table_mode()\n");
	ctrlbar->setMode(treemode); 	// toggle checkbox
	pstable->setTreeMode(treemode); //first  pstable->refresh(); 	    
}


// Slot:
// SEGFAULT CODE:  MOVETO Pstable
// called by 
//      1. void Qps::add_fields_menu(int id)
//      2. field_win
void Qps::field_added(int field_id)  
{
	int where=-1; 
	where=pstable->clickedColumn();
	procview->addField(field_id,where);
	pstable->refresh(); //pstable->update(); // repaint 
    //update_menu_status();
}

// move to Proview
// what this hell? SEGFAULT!
// call by 
//	1. FieldSelect 
//	2. void Qps::menu_remove_field()
void Qps::field_removed(int index)
{
	///printf("field_removed() index=%d\n",index);
	procview->removeField(index);
	if(procview->treeview and index == F_CMD) set_table_mode(false); 
	//should be changed to linear mode !!

///	update_menu_status(); // ???

//	pstable->update();	// no
	pstable->refresh();
	context_col=-1;	// *** important **** : right clicked column removed
	return;
}

// moveto command?
void Qps::menu_edit_cmd()
{
	if(command_win) {
		command_win->show();
		command_win->raise();
	} else {
		command_win = new CommandDialog();
		setWindowGroup(command_win);
		command_win->show();
		connect(command_win, SIGNAL(command_change()),	SLOT(make_command_menu()));
	}
}

// init command menu
// callback by CommandDialog::add_new()
void Qps::make_command_menu()
{
	//should clear SIGNAL!!! 
	QAction *act;
	m_command->clear();
	m_command->disconnect();

	if(flag_devel){
		m_command->addAction("WatchDog",watchdogDialog,SLOT(show()));//, m_event);
	//	m_command->addAction("ScreenShot", screenshot, SLOT(show()) );
		act=m_command->addAction("Edit Commands...",this, SLOT(menu_edit_cmd()));
	//	act->setEnabled(false);
	}


//	if(commands.size())	m_command->addSeparator();
	add_default_command();

	for(int i = 0; i < commands.size(); i++)
	{	
		//commands[i]->menu = m_command->insertItem(commands[i]->name, MENU_FIRST_COMMAND + i);
		//commands[i]->menu =
		act=m_command->addAction(commands[i]->name);
		//connect(act, SIGNAL(triggered(bool )), SLOT(signal_menu(bool)));
    }
	connect(m_command, SIGNAL(triggered(QAction *)), SLOT(run_command(QAction *)));
	
	update_menu_selection_status(); //DEL
//#ifdef SOLARIS
	/* Solaris CDE don't have a tray, so we need a method to terminate */
	m_command->addSeparator();
	//m_command->addAction(QtIconLoader::icon("application-exit"), "&Quit", this, SLOT(save_quit()), Qt::ALT + Qt::Key_Q);
	m_command->addAction(QtIconLoader::icon("gtk-quit"), "&Quit", this, SLOT(save_quit()), Qt::ALT + Qt::Key_Q);
//#endif
}


// run by MENU_ID ? qt slot?
//void Qps::run_command(int command_id)
void Qps::run_command(QAction *act)
{
	int command_id;
	int i,j,idx=-1;
	//FUNC_START
	
	//printf("%s\n",qPrintable(act->text()));
	AddLog(act->text());

	//find_command
	for(i = 0; i < commands.size(); i++) 
	{
	//	if(commands[i]->menu==command_id)
		if(commands[i]->name==act->text())
		{
			if (commands[idx]->IsNeedProc()==false)
			{
				commands[idx]->call(NULL);
				return;
			}

			for(int i = 0; i < procview->linear_procs.size(); i++) {
				Procinfo *p = procview->linear_procs[i];
				if(p->selected)
					commands[idx]->call(p);
			}
			break;
		}
	}

	return;
}

// detail
void Qps::Action_Detail()
{
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected)	open_details(i);
	}
}

void Qps::open_details(int row)
{
	Procinfo *p = procview->linear_procs[row];
	if(p->detail)
	{
//		p->detail->raise();
		p->detail->show();
		printf("show detail \n");
	}
	else {
		Details *d =new Details(p,  procview);
	//	details.append(*d);
	//	setWindowGroup(d);
		d->show();
	///	connect(d, SIGNAL(closed(Details *)), this, SLOT(details_closed(Details *)));
	}
}

void Qps::details_closed(Details *d)
{
	//printf("details_closed()\n");
	//disconnect 
	
	// This is potentially dangerous, since this is called in response to a
	// signal sent by the widget that is about to be deleted here. Better hope
	// that nobody references the object down the call chain! 
	// what ??????
	
	//////details.removeAt(*d);	// deletes window
	//details.removeAt(*d);	// deletes window
}

// find parents of selected processes
void Qps::menu_parent()
{
	locate_relatives(&Procinfo::ppid, &Procinfo::pid);
}

void Qps::menu_children()
{
	locate_relatives(&Procinfo::pid, &Procinfo::ppid);
}

// Find processes whose attribute b is equal to the attribute a of
// selected processes. Center around topmost found.
// This is quadratic in worst case (shouldn't be a problem)
// called by 
// 		1.menu_children()
// 		2.menu_parent()
void Qps::locate_relatives(int Procinfo::*a, int Procinfo::*b)
{
	QList<int> relatives;
	const int infinity = 2000000000;
	int topmost = infinity;
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected) {
			pstable->setSelected(i, FALSE);
			for(int j = 0; j < procview->linear_procs.size(); j++) {
				Procinfo *q = procview->linear_procs[j];
				if(p->*a == q->*b) {
					relatives.append(j);
					if(j < topmost)
						topmost = j;
				}
			}
		}
	}
	for(int i = 0; i < relatives.size(); i++)
		pstable->setSelected(relatives[i], TRUE);
	///if(topmost < infinity) pstable->centerVertically(topmost);
////	pstable->selectionNotify();
}

// select all (direct and indirect) offsprings of currently selected
// processes, without deselecting them
void Qps::menu_dynasty()
{
	QList<int> family;
	for(int i = 0; i < procview->linear_procs.size(); i++)
		if(pstable->isSelected(i))
			family.append(i);
	for(int i = 0, j = family.size(); i < j;) {
		for(int k = 0; k < procview->linear_procs.size(); k++) {
			Procinfo *p = procview->linear_procs[k];
			for(int m = i; m < j; m++) {
				Procinfo *q = procview->linear_procs[family[m]];
				if(q->pid == p->ppid)
					family.append(k);
			}
		}
		i = j;
		j = family.size();
	}
	const int infinity = 2000000000;
	int topmost = infinity;
	for(int i = 0; i < family.size(); i++) {
		pstable->setSelected(family[i], TRUE);
		if(family[i] < topmost)
			topmost = family[i];
	}
	////if(topmost < infinity) pstable->centerVertically(topmost);
	////pstable->selectionNotify();
}

// CALLBACK: called when right button is clicked in table
void Qps::show_popup_menu(QPoint p)
{
#ifdef MOSIX
	bool may_migrate = FALSE;
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected && p->cantmove.isEmpty()) {
			may_migrate = TRUE;
			break;
		}
	}
	m_popup->setItemEnabled(POPUP_MIGRATE, may_migrate);
#endif
	adjust_popup_menu();	
	m_popup->popup(p);
}


// called when right button is clicked in heading
//
void Qps::context_heading_menu(QPoint p, int col)
{
	context_col = col; // ****
	////printf("context_col=%d\n",col);
	static int init=0;
	// rebuild the submenu: only include non-displayed fields
	int ncats=procview->categories.size();
	
	if(init==0)
	{
	/*	#ifdef SHash	
		SHash<int,Category *>::const_iterator it=procview->categories.begin();
		QList<int> keys;
		for ( ; it != procview->categories.end(); it++ )
    		keys.append((*it).first);
		#else
	*/	QList<int> keys=procview->categories.keys();
	//	#endif
		for(int i = 0; i < ncats; i++)
		{
			Category *cat=procview->categories[keys.takeFirst()];
			QAction *act=m_fields->addAction(cat->name); 
			act->setData(cat->id); // index==key 
		}
		connect(m_fields, SIGNAL(triggered(QAction *)),this, SLOT(add_fields_menu(QAction *)));
		init=1;
	}

	QBitArray displayed(64);//MAX_FIELDS
	displayed.fill(false);
	//printf("categories.size=%d cats.size=%d\n",ncats, procview->cats.size());

	for(int i = 0; i < procview->cats.size(); i++)
	{ 
	//	printf("cat id=%d \n",procview->cats[i]->id);
		displayed.setBit(procview->cats[i]->id);
	}

	QList<QAction *> la=m_fields->actions();
	for(int j = 0; j < la.size(); j++)
	{
		QAction *act=la[j];
		int id=act->data().toInt();
		if(!displayed.testBit(id))
			act->setVisible(true);
		else 
			act->setVisible(false);
	}

	m_headpopup->popup(p);
}

void Qps::add_fields_menu(QAction *act)
{
	//act->text();
	int id = act->data().toInt();
	field_added(id);
	//add_fields_menu(id);
}

// called when field is added from heading context menu
// called by 1. context_heading_menu
void Qps::add_fields_menu(int cat_id)
{
	field_added(cat_id);
	context_col = -1;
	//if(field_win) field_win->update_boxes(); // change to showEvent. !!!!
}


// 
void Qps::menu_remove_field()
{
	if(context_col<0) 	return;
	///printf("cats.size=%d , context_col=%d \n",procview->cats.size(),context_col);
	field_removed(procview->cats[context_col]->index);
}

void Qps::menu_update()
{

	QString txt;
	if(update_period == eternity)
		txt = "1 s";
	else if(update_period % 1000 == 0)
		txt.sprintf("%d s", update_period / 1000);
	else
		txt.sprintf("%d ms", update_period);
	IntervalDialog id(txt.toAscii().data(), update_period != eternity);
	id.exec();
	/// save_settings();
	return;

}

void Qps::menu_toggle_path()
{	
	Procview::flag_show_file_path = !Procview::flag_show_file_path;
	/// table refresh() !
}

void Qps::menu_toggle_infobar()
{	
	show_infobar = !show_infobar;
	bar_visibility();
}

void Qps::menu_toggle_ctrlbar()
{
	show_ctrlbar = !show_ctrlbar;
	bar_visibility();
}

void Qps::menu_toggle_statusbar()
{
	show_statusbar = !show_statusbar;
	bar_visibility();
}


void Qps::menu_toggle_cumul()
{
	Procview::flag_cumulative = !Procview::flag_cumulative;
	// refresh() !!
}

void Qps::menu_prefs()
{
	if(prefs_win) {
		prefs_win->show();
		prefs_win->raise();
	} else {
		prefs_win = new Preferences();
		setWindowGroup(prefs_win);
		prefs_win->show();
		prefs_win->raise();

		connect(prefs_win, SIGNAL(prefs_change()), this, SLOT(config_change()));
		///	connect(infobar, SIGNAL(config_change()), prefs_win, SLOT(update_boxes())); // doesnt need !! --> aboutToShow()
	}
}


// if "Preferences" changed
void Qps::config_change()
{

	write_settings();
	bar_visibility();
	//DEL infobar->configure();
	///resizeEvent(0);		// in case it caused geometry change
	
	for(int i=0;i< details.size(); ++i)
	{
	/////	details[i].config_change();
	}
/* 	while((d = details.current()) != 0) {
		d->config_change();
		details.next();
	} */
}

void Qps::menu_renice()
{
//	if(pstable->hasSelection() == 0)		return;

	int defnice = -1000;

	// use nice of first selected process as default, and check permission
	bool possible = TRUE;
	int euid = geteuid();
	Procinfo *p = 0;

	for(int i = 0; i < procview->linear_procs.size(); i++) {
		p = procview->linear_procs[i];
		if(p->selected) {
			if(defnice == -1000)
				defnice = p->nice;
			if(euid != 0 && euid != p->uid && euid != p->euid)
				possible = FALSE;
		}
	}

	if(!possible) {
		QString s;
		s.sprintf("You do not have permission to renice the\n"
				"selected process%s.\n"
				"Only the process owner and the super-user\n"
				"are allowed to do that.",
				(pstable->hasSelection() == 1) ? "" : "es");
		QMessageBox::warning(this, "Permission denied", s);
		return;
	}
	
	int new_nice;
	for(;;) {
		SliderDialog sd(defnice, -20, 19); // Linux kernel : -20 ~ 19  
		if(!sd.exec())
			return;
		bool ok;
		new_nice = sd.ed_result.toInt(&ok);
		if(ok && new_nice >= -20 && new_nice <= 19)
			break;
	}
	int nicecol = procview->findCol(F_NICE);
	int statcol = procview->findCol(F_STAT);
#ifdef LINUX
	int tmscol = procview->findCol(F_TMS);
#endif

	// do the actual renicing
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected) {
			if(setpriority(PRIO_PROCESS, p->pid, new_nice) < 0) {
				QString s;
				switch(errno) {
					case EPERM:
						// this shouldn't happen, but (e)uid could be changed...
						s.sprintf("You do not have permission to renice"
								" process %d (", p->pid);
						s.append(p->command);
						s.append(").\n"
								"Only the process owner and the super-user are"
								" allowed to do that.");
						QMessageBox::warning(this, "Permission denied", s);
						break;
					case EACCES:
						QMessageBox::warning(this, "Permission denied",
								"Only the super-user may lower"
								" the nice value of a process.");
						return;
				}
			} else {
				p->nice = new_nice; // don't wait for update
#ifdef LINUX
				p->tms = p->get_tms(); //ditto
#endif
			}
		}
	}
}



void Qps::menu_sched()
{
	//if(pstable->hasSelection()==false)	return;
	if(geteuid() != 0) {
		QMessageBox::warning(this, "Permission denied",
				"Only the super-user may change the\n"
				"scheduling policy and static priority.");
		return;
	}

	// provide reasonable defaults (first selected process)
	Procinfo *p = 0;
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		p = procview->linear_procs[i];
		if(p->selected)
			break;
	}
	int pol = p->get_policy();
	int pri = p->get_rtprio();
	SchedDialog sd(pol, pri);
	if(!sd.exec())
		return;

	if(sd.out_policy == SCHED_OTHER)
		sd.out_prio = 0;	// only allowed value
	int plcycol = procview->findCol(F_PLCY);
	int rpricol = procview->findCol(F_RPRI);
#ifdef LINUX
	int tmscol = procview->findCol(F_TMS);
#endif
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected) {
			struct sched_param sp;
			sp.sched_priority = sd.out_prio;
			if(sched_setscheduler(p->pid, sd.out_policy, &sp) < 0) {
				QString s;
				if(errno == EPERM) {
					s.sprintf("You do not have permission to change the\n"
							"scheduling and/or priority of"
							" process %d (", p->pid);
					s.append(p->command);
					s.append(").\n"
							"Only the super-user may do that.");
					QMessageBox::warning(this, "Permission denied", s);
					break;
				}
			} else {
				p->policy = sd.out_policy; // don't wait for update
				p->rtprio = sd.out_prio;
#ifdef LINUX
				p->tms = p->get_tms();
#endif

			}
		}
	}
}


#ifdef MOSIX

void Qps::mig_menu(int id)
{
	migrate_selected(id - 1);
}

void Qps::migrate_selected(int migto)
{
	// User wants to migrate a process somewhere
	// Write destination into /proc/XX/goto
	int warnremote = 0;
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected) {
			if(p->isremote)
				++warnremote;
			char buf[80];
			sprintf(buf, "/proc/%d/goto", p->pid);
			FILE *f = fopen(buf, "w");
			if(f) {
				fprintf(f, "%d", migto);
				fclose(f);
			}
		}
	}
	if (warnremote)
		QMessageBox::warning(this, "Remote migration attempt",
				"You can only migrate an immigrated process "
				"using qps on the home node.");
	earlier_refresh();		
}
#else

// Since this is a slot, at least a stub must be defined even when it isn't
// used (moc ignores preprocessor directives)
void Qps::mig_menu(int) {}

#endif // MOSIX

void Qps::send_to_selected(int sig)
{
	for(int i = 0; i < procview->linear_procs.size(); i++) {
		Procinfo *p = procview->linear_procs[i];
		if(p->selected)
			sendsig(p, sig);
	}
	earlier_refresh();		// in case we killed one
}

void Qps::sendsig(Procinfo *p, int sig)
{
	if(kill(p->pid, sig) < 0) {
		// if the process is gone, do nothing - no need to alert the user
		if(errno == EPERM) {
			QString s;
			s.sprintf("You do not have permission to send a signal to"
					" process %d (", p->pid);
			s.append(p->command);
			s.append(").\n"
					"Only the super-user and the owner of the process"
					" may send signals to it.");
			QMessageBox::warning(this, "Permission denied", s);
			//PermissionDialog *pd = new PermissionDialog(s,supasswd);
			//pd->exec();
		}
	}
}

// write geometry, visible fields and other settings to $HOME/.qps-settings
#define SETTINGS_FILE ".qpsrc"
// If the file format is changed in any way (including adding new
// viewable fields), QPS_FILE_VERSION must be incremented to prevent
// version mismatches and core dumps

#ifdef LINUX
#define QPS_FILE_VERSION 40	// version of .qps-linux file format
#endif

#ifdef SOLARIS
#define QPS_FILE_VERSION 24	// version of .qps-solaris file format
#endif

struct Sflagvar { const char *name; bool *var; };
static Sflagvar  flagvars[] = {
	{ "ExitOnClose", &Qps::flag_exit },
	{ "TabView", &Qps::flag_useTabView },
	{ "SingleCPU", &Procview::flag_pcpu_single },
	{ "devel", &flag_devel },
	{ "cmdpath", &Procview::flag_show_file_path },
	{ "infobar", &Qps::show_infobar },
	{ "ctrlbar", &Qps::show_ctrlbar },
	{ "statbar", &Qps::show_statusbar },
	{ "autosave", &Qps::auto_save_options },
	{ "cpubar", &Qps::show_cpu_bar },
	{ "loadgraph", &Qps::show_load_graph },
	{ "loadicon", &Qps::load_in_icon },
	{ "selectpids", &Qps::pids_to_selection },
	{ "tree", &Procview::treeview },  
	{ "cumulative", &Procview::flag_cumulative },
#ifdef LINUX
	{ "hostname", &Qps::hostname_lookup },
	{ "service", &Qps::service_lookup }    
#endif
#ifdef SOLARIS
	{ "normalize", &Qps::normalize_nice },
	{ "pmap", &Qps::use_pmap }
#endif
	,{0,0}
};


char* settings_path(char *name)
{
	char *home = getenv("HOME");
	if(!home) return 0;
	strcpy(name, home);
	strcat(name, "/" SETTINGS_FILE);
	return name;
}

#include <QSettings>
extern QList<watchCond *> watchlist;
// 1.Procview should be contstructed !
// 2.
bool Qps::read_settings()
{
	char path[512];
	if(settings_path(path)==NULL)
	{
		return false;
	}
	QSettings set(path,QSettings::NativeFormat);

	int v=set.value("version",0).toInt();
	if(v==0)
		return false;
	
	int x, y, w, h;
	x=set.value("geometry/x").toInt();
	y=set.value("geometry/y").toInt();
	w=set.value("geometry/width").toInt();
	h=set.value("geometry/height").toInt();
	//set.value("geometry/visiable",isVisible()); 
	Qps::flag_show=true;
	setGeometry(x, y, w, h);

	if(set.value("font/name")!=QVariant() and set.value("font/size")!=QVariant() )
	{
		QString fname=set.value("font/name").toString();
		int	 fsize=set.value("font/size").toInt();	// if not exist then 0
		QFont font;
		font.setFamily (fname);
		font.setPointSize(fsize);
		QApplication::setFont(font);
	}

	
	// Field 
	procview->cats.clear();
	QStringList sl=set.value("field").toStringList();
	for(int i=0;i<sl.size();i++)
	{
		QString str=sl[i];
		procview->addField(str.toAscii().data());
	}
	procview->saveCOMMANDFIELD(); // TMP

	//procview->idxF_CMD=set.value("F_CMD").toInt();

	if(sl.size()==0) // *** for safe , if there is no field. this happen when no qpsrc 
	{
		procview->viewfields = Procview::USER;	
		procview->set_fields();///
	}
	else 
	{
		procview->viewfields = Procview::CUSTOM;
	}

	QString str=set.value("sort/field").toString(); //procview->sortcat->name
	int fid=procview->field_id_by_name(str.toAscii().data());
	int col=procview->findCol(fid);
	if(col>=0) 
		//pstable->setSortColumn(col);  // Pstable::refresh() 
		procview->setSortColumn(col);  // Pstable::refresh() 
	// pstable -> procview
	procview->reversed=set.value("sort/reversed").toBool(); //tmp 
	

	sl=set.value("flags").toStringList();
	//for(int i=0;i<sl.size();i++)
	for(Sflagvar *fs = flagvars; fs->name; fs++) 
	{
		if(sl.contains(fs->name))
			*(fs->var)=true;
		else 
			*(fs->var)=false;
	}
	//procview->treeview=set.value("treeview").toBool();

	int i=set.value("interval").toInt();
	set_update_period(i);
	i=set.value("viewproc").toInt(); 
	procview->viewproc=i; 
	ctrlbar->view->setCurrentIndex (i); //procview->viewproc=set.value("viewproc").toInt(); 

	int size=set.beginReadArray ("watchdog");
 	for(int i = 0; i < size; i++) {
		set.setArrayIndex(i);
		watchCond *w=new watchCond;
		w->putstring(set.value("cat").toString());
		watchlist.append(w);
	}
	set.endArray();	
	size=set.beginReadArray ("commands");
 	for(int i = 0; i < size; i++) {
		set.setArrayIndex(i);
		Command *c=new Command;
		c->putString(set.value("cmd").toString());
		commands.append(c);
	}
	set.endArray();	


	return true;
/*
	} else if(strcmp(buf, "swaplim") == 0) {
			swaplimit = atoi(p);
			swaplim_percent = FALSE;
			if(swaplimit < 0) {
				swaplimit = -swaplimit;
				swaplim_percent = TRUE;
			}
	fclose(f); */
	return TRUE;
}


// USING : 
// write geometry, visible fields and other settings to $HOME/.qps-settings
void Qps::write_settings() //save setting
{
	char path[512];
	if(settings_path(path)==NULL)
	{
		return;
	}

	QSettings set(path,QSettings::NativeFormat);
	
	set.setValue("version",QPS_FILE_VERSION);
	//set.beginGroup("");
	//set.endGroup();
	set.setValue("geometry/x",geometry().x()); // if hide then, wrong value saved!!
	set.setValue("geometry/y",geometry().y());
	set.setValue("geometry/width",geometry().width());
	set.setValue("geometry/height",geometry().height());
	set.setValue("geometry/visiable",isVisible()); //isVisible() ? "show":"hide");
	set.setValue("viewproc",procview->viewproc); //	all, your, running process...
	///set.setValue("treeview",procview->treeview); 

	QStringList sl;
	//printf("procview cats.size=%d\n",procview->cats.size());
    procview->update_customfield();
	set.setValue("field",procview->customfields);
//	set.setValue("F_CMD",procview->idxF_CMD);

	if(procview->sortcat)  // sometimes,  sortcat==NULL.
	{
		set.setValue("sort/field",procview->sortcat->name);
		set.setValue("sort/reversed",procview->reversed);
	}
	//	return;
	sl.clear();
	for(Sflagvar *fs = flagvars; fs->name; fs++) 
	{
		if(*(fs->var))
			sl.append(fs->name);//fprintf(f, " %s", fs->name);
	}
	set.setValue("flags",sl);

//	fprintf(f,	"swaplim: %d\n"	"interval: %d\n",
//				swaplim_percent ? -swaplimit : swaplimit,
//				update_period);
	
	sl.clear();
	set.setValue("font/name",QApplication::font().family());
	set.setValue("font/size",QApplication::font().pointSize()); 

	set.setValue("interval",update_period);

	set.beginWriteArray ("watchdog");
 	for(int i = 0; i < watchlist.size(); i++) {
		set.setArrayIndex(i);
		set.setValue("cat", watchlist[i]->getstring());
	}
	set.endArray();

	set.beginWriteArray ("commands");
 	for(int i = 0; i < commands.size(); i++) {
		set.setArrayIndex(i);
		set.setValue("cmd", commands[i]->getString());
	}
	set.endArray();

/*	
	fprintf(f, "hidden_process:");
	for(int i = 0; i < hidden_process.size(); i++) {
		if(i!=0) fprintf(f,",");
		fprintf(f,"%s",(const char *)hidden_process[i]);
	}
*/

///	printf("Qps: setting saved !\n");
}

// set the window_group hint to that of the main (qps) window
// DELETE ? -> need function , why?
void Qps::setWindowGroup(QWidget *w)
{
		/*
	XWMHints wmh;

	wmh.flags = WindowGroupHint;
	wmh.window_group = handle();
	XSetWMHints(w->x11Display(), w->handle(), &wmh);
*/
}

//DEL
void Qps::setCommand(int argc, char **argv)
{
	// bug: argv[0] should really be frobbed into an absolute path name here
	///XSetCommand(x11Display(), handle(), argv, argc);
}


// return host name with domain stripped
QString short_hostname()
{
	//// int gethostname(char *name, size_t len); // hyun?
	struct utsname u;
	uname(&u);
	char *p = strchr(u.nodename, '.');
	if(p) *p = '\0';
	QString s(u.nodename);
	return s;
}

bool opt_eq(const char *arg, const char *opt)
{
	if(arg[0] == '-') 
		arg++;
	if(arg[0] == '-')
		arg++;

	return strncmp(arg, opt,strlen(opt)) == 0;
}

// print some help to stdout and exit
void print_help(char *cmdname)
{
	fprintf(stderr, "Usage: qps [options]\n"
			"Options:\n"
			"  -version\t\tversion\n"
			"  -mini     \t\tstart Minimized\n");
}


#include	<QX11Info> 
QByteArray geo;
void Qps::clicked_trayicon()
{
	//QT 4.5 bug : hide(), then show() window's pos will be changed a little.
	if( isMinimized() or (isVisible()==false))  // if hidden 
	{
		//printf("showNormal\n");
		//show(); // works..
		showNormal(); // works..
		return;
	}

	// METACITY- focus stealing prevention : XRaiseWindow() not work. 
	// Compiz works well !!
	if(isActiveWindow()==false) // if lower than other windows
	{
		raise(); 
		activateWindow(); 	//for  WindowMaker	
		return;
	}
	hide();
}


void Qps::clicked_trayicon(QSystemTrayIcon::ActivationReason r)
{
//	printf("clicked_trayicon() = %d\n",r);
	if(r==QSystemTrayIcon::Trigger)
	{
		///printf("Trigger = %d\n",r);
		if(!isHidden())
		{
			hide();
		}
		else{
			showNormal(); 
		}
	}

}


// 
void Qps::hideEvent ( QHideEvent * event )
{
//	printf("hideEvent()\n");
	if(trayicon->hasSysTray())
	{
		//	event->accept();
	}
//	geo=saveGeometry();
}



// For systray update. 
// this trick very suck, but I can't find a better solution. (by fasthyun@show.co.kr)
class QpsApp :public QApplication
{ 
	public:
		QpsApp( int &argc, char ** argv ):QApplication(argc,argv){};
		void commitData ( QSessionManager & sm );
		void saveState( QSessionManager & manager );
		virtual bool x11EventFilter ( XEvent *xev ){ 
			// catch X11 event for systray_update !!
			if(trayicon!=NULL) 
				return trayicon->checkNewTrayEvent(xev); 
			return false; // events to qt.
		}; 
};


#include <QSessionManager>
void QpsApp::saveState( QSessionManager & manager )
{
//	printf("saveState()\n");
	//manager.setRestartHint(QSessionManager::RestartIfRunning);
	//manager.release();
}

// this is called  when X Logout 
// closeEvent() never called !!!
void QpsApp::commitData ( QSessionManager & manager )
{ 
	/*
	printf("commitData()\n");
	manager.setRestartHint(QSessionManager::RestartIfRunning);
	qps->flag_exit=true;  // ready to Logout
	qps->save_settings() ;
	manager.release();
	sleep(2);
	return; 
	 if (manager.allowsInteraction()) {
         int ret = QMessageBox::warning(
                     qps,
                     tr("My Application"),
                     tr("Save changes to document?"),
                     QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);

         switch (ret) {
         case QMessageBox::Save:
             manager.release();
   //          if (!saveDocument())    manager.cancel();
             break;
         case QMessageBox::Discard:
             break;
         case QMessageBox::Cancel:
         default:
             manager.cancel();
         }
     } else {

			manager.release();
					
         // we did not get permission to interact, then
         // do something reasonable instead
     }
 */
	/*
//DEL sm.release();
	qDebug("Qps: Session saved\n");
//	sm.cancel();
	//sm.setRestartHint (QSessionManager::RestartIfRunning); 
	QApplication::commitData(sm);
*/
} 

#include <signal.h>
void signal_handler(int sig)
{
//	if(sig==SIGINT)	printf("DEBUG: catched SIGINT \n");
//	if(sig==SIGTERM)printf("DEBUG: catched SIGTERM \n");
	qps->save_quit();
	//printf("Qps: suiciding.... wait \n");
	printf("Qps: terminating...\n");
}


int main(int argc, char **argv, char **envp)
{
	signal(SIGTERM, signal_handler);
	signal(SIGINT, signal_handler);
	signal(SIGKILL, signal_handler);
	
	Lookup::initproctitle(argv, envp);	// will be REMOVEd

	for(int i = 1; i < argc; i++) {
		if(opt_eq(argv[i], "version")) {
			fprintf(stderr, "qps version " QPS_VERSION ", using Qt library %s\n", qVersion());
			exit(1);
		} else if(opt_eq(argv[i], "help") || opt_eq(argv[i], "h")) {
			print_help(argv[0]);
			exit(1);
		} 
		else if (opt_eq(argv[i], "session") or opt_eq(argv[i], "sm")) 
		{
			flag_session_start=true;
			sleep(2); // **** important !! maybe systray runs later
		} else if (opt_eq(argv[i], "min"))  //mini
		{
			flag_start_mini=true;
		} 
		else if (strstr(argv[i], "screen"))  
		{
			//int screenshot_main(int argc, char **argv);
			//screenshot_main(argc,argv);
			//return 1;
		}
	}
//	codec = QTextCodec::codecForLocale(); // for Local locale 
	check_system_requirement(); // check kernel version.. etc in proc.cpp
	check_qps_running();	// check already qps running.  in misc.cpp

	QpsApp app(argc, argv);

	init_misc(0); 	// init misc, some test code runs ...
	qps=new Qps();
//	sleep(20);

	QString caption = "";  
	caption.append(getenv("USER"));
	caption.append("@");
	caption.append(short_hostname());   //geteuid()
	
	qps->setWindowTitle(UniString(caption));
	qps->setWindowIcon(QPixmap( (const char**)icon_xpm));
    
	// systray 
	QMenu *menu=new QMenu(qps);
	///menu->addAction( UniString("About"), qps, SLOT(about()) );
	menu->addAction( "Show", qps, SLOT(showNormal()) );
	menu->addAction( "Hide", qps, SLOT(hide()) );
	menu->addSeparator();
	if(flag_devel)
	//	menu->addAction( "ScreenShot", screenshot, SLOT(show()) );
		menu->addAction( "ScreenShot", qps, SLOT(start_screenshot()) );
	// screenshot=new Screenshot();
	menu->addAction( "&Quit", qps, SLOT(save_quit()), Qt::ALT + Qt::Key_Q); 


	trayicon=new TrayIcon(QPixmap( (const char**)icon_xpm), "qps", menu);
	QObject::connect(trayicon,SIGNAL(clicked(const QPoint&)),qps,SLOT(clicked_trayicon()));
	QObject::connect(trayicon,SIGNAL(doubleClicked(const QPoint&)),qps,SLOT(clicked_trayicon())); 
	trayicon->sysInstall();
		
	if(flag_session_start or flag_start_mini ) 
	{
		if(trayicon->hasSysTray())
		{
			qps->hide(); //qps->setHidden(true);
		}
		else 
		{
			qps->showMinimized(); 
		}
	}
	else {
		qps->show();
	} 

	//Testing
//	app.setStyleSheet(" QToolTip { opacity: 100;" "border-width: 1px; border-style: solid;  border-color: rgb(50,110,80); border-radius: 4px ;" "background-color : rgba(0,0,0); padding: 3px; color: rgb(0,255,150); }");
	QString str;
	str.append("Qps ");
	str.append(QPS_VERSION);
	str.append(" launched.");
	AddLog(str);

	return app.exec();
}


#include <QDialog>
#include <QTextBrowser> 
#include <QDesktopServices> 
#include <QDialogButtonBox> 
#include <QClipboard> 

void Qps::test_popup(const QUrl &link)
{
	
	QClipboard * clipboard=QApplication::clipboard () ;
	clipboard->setText(link.toString(),QClipboard::Clipboard);
	popupx->popup(QCursor::pos());
}

void Qps::about()
{
	QDialog *diag=new QDialog(this); 
	diag->setWindowTitle("About" );
	QVBoxLayout *lay=new QVBoxLayout(diag);

	QLabel *label = new QLabel( diag );
//	label->setMinimumSize( 400,200 );
	label->setOpenExternalLinks ( true );
// 	QLabel::setOpenExternalLinks ( bool open );
	QTextBrowser *browser = new QTextBrowser( diag );
	browser->setOpenExternalLinks ( false );
	browser->setOpenLinks ( false);
	
	connect(browser, SIGNAL( anchorClicked ( const QUrl & )), this, SLOT(test_popup(const QUrl &)));

	lay->addWidget(label);
	lay->addWidget(browser);
	
//	QDesktopServices::openUrl(QUrl("file:///", QUrl::TolerantMode));
//

	QString str("<h2> qps " QPS_VERSION "  -   A Visual Process Manager </h2> "
#ifdef SOLARIS
#ifdef _LP64
			"64-bit "
#else
			"32-bit "
#endif
			"Solaris version "
#endif // SOLARIS
			"using Qt library "
			);

	str.append(qVersion());
	//s.append(url);
	str.append( ""
		"<br>"
		"<A href=\"http://qps.kldp.net\">http://qps.kldp.net</A>"
		);
	label->setText(str);
	
	str="";
	str.append(
		"<center><b> Bug, Complains, Opinion, Patch to </b></center>"
		"<center><a href=\"mail://fasthyun@magicn.com\" > fasthyun@magicn.com </a></center>"
		"<br>"
		"<center> Contributors </center>"
		"<center><i>Olivier.Daudel@u-paris10.fr </i></center>"
		"<center><i>jsanchez@todounix.homeip.net </i></center>"
		"<br>"
		"<center>Original Qps by</center>"
		"<center><i>" 
		"	Mattias Engdegård\n" //"(f91-men@nada.kth.se)\n"
		"</i></center>"
		);

//     QPushButton *contribut = new QPushButton(tr("&More"));
     
	// moreButton->setCheckable(true);
    // moreButton->setAutoDefault(false);
		
	browser->setText(str);


	QDialogButtonBox *bbox=new QDialogButtonBox (QDialogButtonBox::Ok,Qt::Horizontal, diag);
	

	lay->addWidget(bbox);
    
	connect(bbox, SIGNAL(accepted()), diag, SLOT(accept()));

	diag->exec();

//	mb.setIconPixmap(QPixmap((const char **)icon_xpm));
}


void Qps::license() // -> help()
{

		
	QDialog *diag=new QDialog(this); 
	diag->setWindowTitle("Quick Help" );
	//diag->setSizeGripEnabled(true) ;
	QHBoxLayout *lay=new QHBoxLayout(diag);


	QTextBrowser *browser = new QTextBrowser( diag );
	browser->setMinimumSize( 400,200 );

	lay->addWidget(browser);
	
//	QDesktopServices::openUrl(QUrl("file:///", QUrl::TolerantMode));
// 	QLabel::setOpenExternalLinks ( bool open );
	browser->setOpenExternalLinks ( true );
	browser->setOpenLinks ( true);
	browser->setText(
			"<H1>QPS Help</H1>"
			"Updated: May 24 2005<BR>"
			"<A HREF=\"http://qps.kldp.net\">http://qps.kldp.net</A><HR>"
			//			"temporary<BR>"
			
			"<table style=\"text-align: center; width: 100%;\" border=\"1\""
			" cellpadding=\"1\" cellspacing=\"0\">"
			"  <tbody>"
			"    <tr>"
			"      <td"
			" style=\"vertical-align: top; background-color: rgb(204, 204, 204);\">Quit"
			"      </td>"
			"      <td >&nbsp; CTRL + q , CTRL + x"
			"      </td>"
			"    </tr>"
			"    <tr>"
			"      <td"
			" style=\"vertical-align: top; background-color: rgb(204, 204, 204);\">Update"
			"      </td>"
			"      <td>&nbsp;Space , Enter "
			"      </td>"
			"    </tr>"
			"    <tr><td> process Terminate </td>  <td> ALT + T , DELETE </td> </tr>"
			"    <tr><td> process Kill </td>  <td> ALT + K  </td> </tr>"
			"  </tbody>"
			"</table>"
			);
	diag->exec();
}

// slot
void Qps::start_screenshot()
{


}

// MOVETO qps::keyPressEvent()
void SearchBox::keyPressEvent ( QKeyEvent * e )
{
	if(e->key()==Qt::Key_Delete)
	{
		qps->sig_term();
		return;
	}

	if(e->key()==Qt::Key_Escape )
	{
		clear();
		//xb->setDown(true);
		//event_xbutton_clicked();		
		//xb->setDown(false);
	}
	else 
		QLineEdit::keyPressEvent ( e );
	qps->procview->filterstr=text();
	qps->pstable->refresh(); 	//qps->refresh(); // better ?
}


void STATUSBAR_COUNT_UPDATE()
{

}

void STATUSBAR_SETCOUNT(int n)
{	
	if(qps)	qps->statusBar->update(n);
}

void PSTABLE_SETTREEMODE(bool mode)
{
	if(qps) qps->pstable->setTreeMode(mode);
}

void QPS_SHOW()
{
	if(qps) qps->showNormal();
}

//TESTING
int QPS_PROCVIEW_CPU_NUM()
{
	if(qps) 
		return qps->procview->num_cpus;
	else
		return 0;
}

int AddLog(QString str)
{
	if(qps and qps->logbox)
	{
		qps->logbox->append(str);
	}

}

