Шаг 76.
Библиотека OWL.
Реализация интерфейса Drag&Drop

    На этом шаге мы рассмотрим реализацию интерфейса Drag&Drop.

    Студентка 5 курса (2009-10 уч.год) Нечаева Ольга реализовала интерфейс Drag&Drop средствами бибилиотеки OWL.

    Данное приложение представляет собой программу, которая перемещает элементы одного списка в другой список с использованием правой клавиши мыши, причем если элемент взяли из первого списка, то мы можем добавить его только во второй, и если элемент взяли из второго списка, то мы можем добавить его только в первый.

    Вот текст приложения:

#include <owl\applicat.h>
#include <owl\window.h>
#include <owl\framewin.h>
#include <owl\edit.h>
#include <owl\listbox.h>
#include <string.h>
#include <cstring.h>
#include <windows.h>
#include "pr76_1.rc"
const int UPDATE_WINDOW1=101;
const int UPDATE_WINDOW3=103;


// Класс приложения.

class TApp: public TApplication
{
  public:
	 TApp():TApplication() {}
	 void InitMainWindow();
};

class TNewList : public TListBox
{
	public:
          TNewList(TWindow *parent,int Id,int x,int y,int w,int h, 
              TModule* module = 0):TListBox(parent,Id,x,y,w,h,0){};
	protected:
          void EvRButtonDown (UINT modKeys,TPoint &point);
          void EvRButtonUp (UINT modKeys,TPoint &point);
          DECLARE_RESPONSE_TABLE(TNewList);
};

DEFINE_RESPONSE_TABLE1(TNewList, TListBox)
	 EV_WM_RBUTTONDOWN,
	 EV_WM_RBUTTONUP,
END_RESPONSE_TABLE;
// Класс главного окна.
class TWndw : public  TFrameWindow
{
  public:
      TWndw ( const char  *title);
  protected:
      BOOL fl;
      char s[30];
      int n,p;
      TNewList *listBox1;
      TNewList *listBox2;
      void SetupWindow();
      void EvRButtonUp(UINT,TPoint &point);
      void EvMouseMove(UINT,TPoint &point);
      void Vibor1();
      void Vibor2();
      void Otp1();
      void Otp2();
      DECLARE_RESPONSE_TABLE(TWndw);
};

 DEFINE_RESPONSE_TABLE1(TWndw, TFrameWindow)
	EV_WM_RBUTTONUP,
	EV_WM_MOUSEMOVE,
	EV_CHILD_NOTIFY(ID_LISTBOX1,UPDATE_WINDOW1,Vibor1),
	EV_CHILD_NOTIFY(ID_LISTBOX2,UPDATE_WINDOW1,Vibor2),
	EV_CHILD_NOTIFY(ID_LISTBOX1,UPDATE_WINDOW3,Otp1),
	EV_CHILD_NOTIFY(ID_LISTBOX2,UPDATE_WINDOW3,Otp2),
END_RESPONSE_TABLE;


// Это конструктор главного окна.
TWndw::TWndw( const char  *title):
			 TFrameWindow (0, title),TWindow(0,title)
{ // Допускает переход по клавише табуляции между
  // управляющими элементами.
  EnableKBHandler();
  AssignMenu(MENU_1);
  // Определить размеры и расположение окна.
  Attr.X = 100;
  Attr.Y = 60;
  Attr.W = 600;
  Attr.H = 550;
  // Создать управляющие элементы в окне.
  new TStatic (this, -1, "List Box1:",30, 14, 80, 36, 10 );
  listBox1 = new TNewList (this, ID_LISTBOX1,  30, 45,150, 150 );
  new TStatic (this, -1, "List Box2:",250, 14, 80, 36, 10 );
  listBox2 = new TNewList (this, ID_LISTBOX2,  250, 45,150, 150 );
}


void TWndw::SetupWindow()
{
  TFrameWindow::SetupWindow();
  // Определить начальное состояние управляющих элементов.
  listBox1->AddString("Клод Моне");
  listBox1->AddString("Пьер Огюст Ренуар");
  listBox1->AddString("Эдгар Дега");
  listBox1->AddString("Винсент Ван Гог");
  listBox2->AddString("Жемчуг");
  listBox2->AddString("Изумруд");
  listBox2->AddString("Топаз");
  listBox2->AddString("Сапфир");
  listBox2->AddString("Бриллиант");
  listBox1->SetSelIndex(-1);
  listBox2->SetSelIndex(-1);
  fl=false;  //пока ничего не "несём"
  n=0;
  p=-1;
}

void TNewList::EvRButtonDown(UINT modKeys,TPoint &point)
{
  Parent->SendNotification(Attr.Id,UPDATE_WINDOW1,HWindow);
 }


void TNewList::EvRButtonUp(UINT modKeys,TPoint &point)
{
 Parent->SendNotification(Attr.Id,UPDATE_WINDOW3,HWindow);
 }

void TWndw::Vibor1()
{
	p=listBox1->GetSelIndex();
	if (p!=(-1)) // если есть выбранный элемент
		{listBox1->GetSelString(s,sizeof(s));
		 fl=true;
		 n=1; //"взяли из первого списка"
		  }
	else   
           MessageBox("Строка не выбрана или список пуст!",
                "Ошибка",MB_OK|MB_ICONEXCLAMATION);
}

void TWndw::Vibor2()
{
	p=listBox2->GetSelIndex();
	if (p!=(-1)) // если есть выбранный элемент
		{ listBox2->GetSelString(s,sizeof(s));
		  fl=true;
		  n=2; //"взяли из второго списка"
		 }
       else  
           MessageBox("Строка не выбрана или список пуст!",
                "Ошибка",MB_OK|MB_ICONEXCLAMATION);

}

void TWndw::EvMouseMove(UINT ,TPoint &point)
{    const char*cursor;
	  HINSTANCE instance;
	  HCURSOR hCursor;
	if (fl==true)     //если что-то "несём", то поменяем курсор
	 { instance=NULL;
	  cursor=IDC_WAIT;
	  hCursor=LoadCursor(instance,cursor);
	  SetClassWord(GCW_HCURSOR,(WORD) hCursor);   }
}



void TWndw::Otp1()
{ // если "взяли" из первого списка
  if ((n==2)&&(fl==true))
	 { listBox1->AddString(s);
		listBox2->DeleteString(p); }
  fl=false;
  n=0;
  //меняем курсор на обычный
  const char*cursor;
  HINSTANCE instance;
  HCURSOR hCursor;
  instance=NULL;
  cursor=IDC_ARROW;
  hCursor=LoadCursor(instance,cursor);
  SetClassWord(GCW_HCURSOR,(WORD) hCursor);
}
void TWndw::Otp2()
{ // если "взяли" из первого списка
  if ((n==1)&&(fl==true))
	  {listBox2->AddString(s);
		listBox1->DeleteString(p); }
  fl=false;
  n=0;
  //меняем курсор на обычный
  const char*cursor;
  HINSTANCE instance;
  HCURSOR hCursor;
  instance=NULL;
  cursor=IDC_ARROW;
  hCursor=LoadCursor(instance,cursor);
  SetClassWord(GCW_HCURSOR,(WORD) hCursor);
}

void TWndw::EvRButtonUp(UINT,TPoint &point)
{  //если "не донесли" до списка
  	if (fl==true)
 	{fl=false;
	       n=0;
	       p=0;
	       const char*cursor;
   	 HINSTANCE instance;
	       HCURSOR hCursor;
	       instance=NULL;
	       cursor=IDC_ARROW;
	       hCursor=LoadCursor(instance,cursor);
	       SetClassWord(GCW_HCURSOR,(WORD) hCursor);    }
 }


 void TApp::InitMainWindow()
{
  SetMainWindow(new TWndw("Drag&Drop"));
}

int OwlMain(int,char *[])
{
  return TApp().Run();
}

    Файл ресурсов:

#ifndef WORKSHOP_INVOKED
#include "windows.h"
#endif

#include <owl\window.rh>

#define MENU_1       100
#define ID_LISTBOX1  109
#define ID_LISTBOX2  110

#ifdef RC_INVOKED

#endif
Текст этого приложения можно взять здесь.

    Списки имеют идентификаторы listBox1 и listBox2, присвоенные им после инициализации окна:

TWndw::TWndw( const char  *title):
			 TFrameWindow (0, title),TWindow(0,title)
{ // Допускает переход по клавише табуляции между
  // управляющими элементами.
  EnableKBHandler();
  AssignMenu(MENU_1);
  // Определить размеры и расположение окна.
  Attr.X = 100;
  Attr.Y = 60;
  Attr.W = 600;
  Attr.H = 550;
  // Создать управляющие элементы в окне.
  new TStatic (this, -1, "List Box1:",30, 14, 80, 36, 10 );
  listBox1 = new TNewList (this, ID_LISTBOX1,  30, 45,150, 150 );
  new TStatic (this, -1, "List Box2:",250, 14, 80, 36, 10 );
  listBox2 = new TNewList (this, ID_LISTBOX2,  250, 45,150, 150 );
}

    После нажатия правой кнопкой мыши в области списка, происходит событие EV_WM_RBUTTONDOWN, которому сопоставлено выполнение функции EvRButtonDown(UINT modKeys,TPoint &point):

void TNewList::EvRButtonDown(UINT modKeys,TPoint &point)
{
  Parent->SendNotification(Attr.Id,UPDATE_WINDOW1,HWindow);
 }

    Функция TWindow::SendNotification имеет следующее описание:

  inline void SendNotificatin(int id, int notifyCode, 
           HWND hCtl, UINT msg = WM_COMAND);
где id - идентификатор дочернего окна для взаимодействия между средством управления и его родителем, notifyCode - код для данного сообщения, hCtl - дескриптор дочернего окна. Эта функция посылает сообщение от дочернего окна окну-родителю.

    В нашем случае посылается сообщение:

где
void TWndw::Vibor1()
{
	p=listBox1->GetSelIndex();
	if (p!=(-1)) // если есть выбранный элемент
		{listBox1->GetSelString(s,sizeof(s));
		 fl=true;
		 n=1; //"взяли из первого списка"
		  }
	else   
               MessageBox("Строка не выбрана или список пуст!",
                   "Ошибка",MB_OK|MB_ICONEXCLAMATION);
}

    В той функции в переменную p запоминаем номер выбранного элемента списка, в s - сам элемент, в n - номер списка, из которого "взяли элемент", устанавливаем значение true переменной fl. С помощью этой переменной определяется, "несём" мы элемент списка (true) или нет (false).

    Аналогично выглядит функция Vibor2.

    Когда мы двигаем мышкой по главному окну, возникает событие EV_WM_MOUSEMOVE. Реакцией на это событие является следующая функция:

void TWndw::EvMouseMove(UINT ,TPoint &point)
{    const char*cursor;
	  HINSTANCE instance;
	  HCURSOR hCursor;
	if (fl==true)     //если что-то "несём", то поменяем курсор
	 { instance=NULL;
	  cursor=IDC_WAIT;
	  hCursor=LoadCursor(instance,cursor);
	  SetClassWord(GCW_HCURSOR,(WORD) hCursor);   }
}

    В этой функции мы проверяем значение переменной fl. Если её значение true, то меняем курсор; если нет, то ничего не происходит.

    Если мы отпустим правую кнопку мыши на окне (т е не "донесём до списка"), то возникает событие EV_WM_RBUTTONUP и выполняется функция:

void TWndw::EvRButtonUp(UINT,TPoint &point)
{  //если "не донесли" до списка
  	if (fl==true)
 	{fl=false;
	 n=0;
	 p=0;
	 const char*cursor;
        HINSTANCE instance;
	 HCURSOR hCursor;
	 instance=NULL;
	 cursor=IDC_ARROW;
	 hCursor=LoadCursor(instance,cursor);
	 SetClassWord(GCW_HCURSOR,(WORD) hCursor);    }
 }

    Устанавливаем значение fl равным false, обнуляем n и p, меняем вид курсора на обычный.

    Если мы отпустим правую кнопку мыши в области списка, то возникает событие EV_WM_RBUTTONUP и выполняется функция

void TNewList::EvRButtonUp(UINT modKeys,TPoint &point)
{
 Parent->SendNotification(Attr.Id,UPDATE_WINDOW3,HWindow);
 }

    Посылается сообщение:

где
void TWndw::Otp1()
{ // если "взяли" из первого списка
  if ((n==2)&&(fl==true))
	 { listBox1->AddString(s);
	   listBox2->DeleteString(p); }
  fl=false;
  n=0;
  //меняем курсор на обычный
  const char*cursor;
  HINSTANCE instance;
  HCURSOR hCursor;
  instance=NULL;
  cursor=IDC_ARROW;
  hCursor=LoadCursor(instance,cursor);
  SetClassWord(GCW_HCURSOR,(WORD) hCursor);
}

    Эта функция добавляет элемент в список, если его "взяли" в другом списке, и меняет курсор на обычный.

    Результат работы приложения изображен на рисунке 1:


Рис.1. Результат работы приложения


   Примечание. Перед компиляцией программы нужно установить в окне TargetExpert значение параметра Platform в Windows 3.x (16).

    На следующем шаге мы рассмотрим программу задания цвета.




Предыдущий шаг Содержание Следующий шаг