/**************/ /* dropfile.c */ /**************************************************************************** PROGRAM: dropfile.c PURPOSE: Drag and Drop-palvelin AUTHOR: Jeffrey M. Richter. Vesa Lappalainen 27.8.1993 muutti aliohjelmiksi USAGE: Projektiin laitettava dropfile.c dropfile.rc (Jos on oma.RC dropfile.rc voidaan laittaa omaan .RChen inludina) 1) Omaan ohjelmaan lisätään kutsu: DropFiles(hWnd,files); kun halutaan siirtää tiedostojen nimiä toiseen ohjelmaan. files on merkkijonomuuttuja, joka on muotoa: "hak1 t1 t2 t3;hak2 t4 t5 t6;hak3\t7" Eli esimerkiksi: "C:\DOS SUBST.COM FC.EXE;C:\BIN\TGREP.COM tarkoittaisi tiedostoja: C:\DOS\SUBST.COM C:\DOS\FC.EXE C:\BIN\TGREP.COM Itse asiassa merkkijono voi sisältää mitä tahansa merkkijonoja puolipisteellä erotettuna, jollei niissä ole välilyöntejä. Jos vastaanottaja ymmärtää nämä nimet niin hyvä on. 2) Alla on myös aliohjelmia tiedostojen valitsemiseksi dialogin avulla edellä mainittuun merkkijonomuotoon. Drag and drop (DaD) asiakkaanahan ohjelma voi toimia seuraavasti: ----------------------------------------------------------------- 1) Ilmoitetaan että hyvksytään DaD: DragAcceptFiles(hWnd,TRUE); 2) Vastataan viestiin: WM_DROPFILES ottamalla tiedostot kahvalla hDrop = (HDROP)wParam käyttäen kutsuja: lkm = DragQueryFile(hDrop,-1,NULL,0); ... DragQueryFile(hDrop,i,FileStr[i],msize); ... DragFinish(hDrop); /! Muistilohkon vapautus muistettava!!! ============ Aliohjelmat: (dropfile.c) ============ int DropFiles(HWND hWnd,LPCSTR files); -------------------------------------- - käynnistää Drop toiminnon files-listan tiedostoille. Mikäli hiiren vasen nappi ei ole alhaalla, on toiminto aktiivinen kunnes nappi painetaan alas ja päästetään ylös. Kursori muuttuu sen mukaan pystyykö alla oleva sovellus ottamaan pudotuksen vastaan vaiko ei. Jos nappi päästetään ylös ei-asiakkaan kohdalla, palautetaan 0, muuten pudotettujen tiedostojen lukumäärä Kun pudotus on tehty, lähetetään kutsuneelle ohjelmalle viesti: UM_UPDATECAPTION jonka saamisen jälkeen esim. pyyhitään pois pudotuksen mahdollistaminen. Tosin tätä harvoin tarvitaan, koska funktio palauttaa jo nimessään tiedon onnistuiko homma vai ei. LPCSTR MultiSelectFiles(HWND hWnd) ---------------------------------- - tiedostojen valitsemiseksi DropFiles-vaatimaan muotoon. Kutsulla saadaan osoite staattiseen merkkijonoon tai NULL. Jono on muotoa "hak t1 t2 t3" tai "hak\t1" Kutsu käyttää common-dialogin tiedostovalitsinta. int DropSelectFiles(hWnd) ------------------------- - Kutsuu edellistä, laittaa tiedostot pudotukseen ja palauttaa pudotettujen tiedostojen lukumäärän. int DropListBoxFiles(HWND hWnd,UINT id,LPCSTR path) --------------------------------------------------- - laittaa list-boxissa hWnd,id olevat nimet pudotukseen. Kunkin nimen eteen lisätään jono path. int DropFilesToListBox(HDROP hDrop,HWND hWnd,int id,int clear) -------------------------------------------------------------- - ottaa Drop listassa olevat nimet ja laittaa ne list-boxiin hWnd,id. clear == 1 => poistaa nimistä polkuosan == 2 => tyhjentää ensin list-boxin == 3 => molemmat edelliset ( == 1 | 2 ) LPSTR RemovePath(LPSTR s) ------------------------- - paluttaa osoitteminen saman merkkijono pelkkään tiedoston nimeen. LPSTR DropFilesToString(HDROP hDrop,LPSTR s,int mb) --------------------------------------------------- - ottaa Drop-listassa olevat nimet ja palauttaa ne muodossa path1\n1;path2\n2;path3\n3 WORD GetSinglePathName (LPCSTR szFileOpenStr, int wIndex, LPSTR szPathName, WORD wMaxLen) --------------------------------------------------------- - palautetaan szFileOpenStr-muuttujasta wIndex-numeroinen kokonainen tiedoston nimi. Numerointi alkaa nollasta. Seuraavalla kutsulla saadaan selville tiedostonimien lukumäärä merkkijonossa: wNumFiles = GetSinglePathName(szAllFileNames, -1, NULL, 0); HDROP DragCreateFiles (LPPOINT lpptMousePos,BOOL fInNonClientArea) ------------------------------------------------------------------ - luodaan Drop-struktuuri, Hiirren ilmoitettava paikka ja ollaanko asiakkaan otsikossa vaiko ei. HDROP DragAppendFile (HGLOBAL hDrop, LPCSTR szPathname) ------------------------------------------------------- - lisätään tiedoston nimi Drop-struktuuriin. Tuloksena kahva, joka välitetään asiakkaalle ja jonka asiakas on velvollinen hävittämään. Kolme viimeistä on lähinnä kirjaston sisäiseen käyttöön, mutta tietysti niitä voi käyttää itsekin. Tällöin tosin joudutaan tekemään itse kursorin muodon muutokset, jotka nyt hoituvat automaattisesti aliohjelman DropFiles-ansiosta. Vikoja: ------- Totetus perustuu Jeffrey M. Richter "nuuskimaan" tietorakenteeseen Drop-tiedostoille. Windows itse ei käytä (esim. FileManager) tietorakennetta näin suoraan, vaan lähettää viestejä jollekin Drop-palvelimelle. Näiden viestien käyttöyritys kuitenkin kaataa vastaanottaja ohjelman heti kun tullaan sen jonkin lapsi-ikkunan kohdalle. Viestien käyttö olisi kuitenkin varmasti parempi tapa hoitaa homma, mutta kun sitä ei ole dokumentoitu! *****************************************************************************/ #include #include #include #include #include "Dropfile.h" /****************************************************************************/ static int AdvToFileNr(LPCSTR *file,int wIndex,int *Only1file) /* ** Siirrytään wIndex numeroiseen tiedoston nimeen tässä ** sectionissa (; erotettu osa). Palautetaan ** se indeksi, johon siirryttiin tai sectionion nimien lkm. ** Jos haluttuun indeksiin ei päästä, siirrytään seuraavan ** section alkuun. ** ** 0123456789012345678901234567890 wIndex ret *c file ** ' c:\dos f0 f1 f2' 9 ==> 3 24 0 ** ' c:\dos f0 f1 ; c:\bat f2 ' 9 ==> 2 21 0 ** ' c:\dos f0 f1 ; c:\bat f2 ' 1 ==> 2 14 0 ** ' c:\dos\f0 ' 9 ==> 1 16 1 ** ' c:\dos\f0 ' 0 ==> 1 4 1 ** ' c:\dos f0 ' 0 ==> 1 11 0 ** ' ' 9 ==> 0 16 0 */ { int num = -1; char prev=' '; LPCSTR p = *file; *Only1file = 0; while ( *p == ' ' ) p++; for (prev=' '; *p && *p != ';'; prev=*p,p++) { if (*p != ' ' && prev == ' ') { // Came into first char num++; *file = p; if ( num > wIndex ) return num; } } if ( num == 0 ) { // Form "c:\dos\f1.com" *Only1file = 1; num = 1; if ( wIndex == 0 ) return num; } while ( *p ==';' || *p ==' ' ) p++; *file = p; return max(num,0); } /****************************************************************************/ static int AdvContainingSection(LPCSTR *sec,LPCSTR *file,WORD wIndex, int *Only1file) /* ** Siirrytään tiedostonnimilistassa wIndex numeroisen tiedoston alkuun. ** sec muuttujassa palautetaan vastaavan sectionin alkuosoite. ** Jos indeksipyyntö on liian iso, palautetaan alkioiden lukumäärä. ** ** wInd 0123456789012345678901234567890123 ret *sec *file Only1file ** 1 ' c:\dos f0 f1 f2' ==> 2 4 17 0 ** 10 ' c:\dos f0 f1 ; c:\bat f2 ' ==> 3 33 33 0 ** 2 ' c:\dos\f0;c:\bat\f1;c:\bin\f2' ==> 3 14 14 1 */ { LPCSTR f = *sec; int num = 0; while ( 1 ) { num += AdvToFileNr(&f,wIndex-num,Only1file); if ( *f == 0 || num > wIndex ) break; *sec = f; // Passed whole section, so now in begining of next one } *file = f; while ( **sec == ' ' ) (*sec)++; // Skip leading spaces return num; } /****************************************************************************/ WORD GetSinglePathName (LPCSTR szFileOpenStr, int wIndex, LPSTR szPathName, WORD wMaxLen) /* ** sec 1 sec 2 sec 3 wIndex ** c:\dos f0 f1 f2;c:\bat f3 f4 f5;c:\bin f6 f7 f8 5 => c:\bat\f5 ** c:\dos\f0 ;c:\bat f1 f2 f3 0 => c:\dos\f0 ** c:\dos f0 f1 f2; 3 => "" */ { WORD wNumFiles = 0, x; LPCSTR sec = szFileOpenStr,file; int Only1file; char szBuf[200]; if ( szPathName != NULL ) szPathName[0] = 0; if ( !szFileOpenStr ) return 0; // Initialize the buffer by clearing it _fmemset(szBuf, 0, sizeof(szBuf)); wNumFiles = AdvContainingSection(&sec,&file,10000,&Only1file); // If the user only wants the number of files, return that if (wIndex == -1) return wNumFiles; // User requested more files than exist if (wIndex >= wNumFiles) return 0; // *** Construct the full pathname of the requested string *** sec = szFileOpenStr; AdvContainingSection(&sec,&file,wIndex,&Only1file); x = min(_fstrcspn(sec, " ;"),sizeof(szBuf)-1); // Copy the path portion of the string into a temp buffer _fstrncpy(szBuf, sec, x); if ( !Only1file ) { // Form c:\dos f1 // Append a backslash if necessary if ( !strchr("\\:",szBuf[x-1]) ) szBuf[x++] = '\\'; // Copy the filename into the temp buffer _fstrncpy(&szBuf[x], file, _fstrcspn(file," ;")); } if (szPathName != NULL) { // If the user passed an address, copy the string into its buffer _fstrncpy(szPathName, szBuf, wMaxLen); szPathName[wMaxLen - 1] = 0; // Force zero-termination } return lstrlen(szBuf); // Returns length of string } //*************************************************************** //****************** ***************** //****************** DROP-FILE SERVER FUNCTIONS ***************** //****************** ***************** //*************************************************************** typedef struct { WORD wSize; // Size of data structure POINT ptMousePos; // Position of mouse cursor BOOL fInNonClientArea; // Was the mouse in the // window's non-client area } DROPFILESTRUCT, FAR *LPDROPFILESTRUCT; //*************************************************************** HDROP DragCreateFiles (LPPOINT lpptMousePos, BOOL fInNonClientArea) { HGLOBAL hDrop; LPDROPFILESTRUCT lpDropFileStruct; // GMEM_SHARE must be specified because the block will // be passed to another application hDrop = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(DROPFILESTRUCT) + 1); // If unsuccessful, return NULL if (hDrop == NULL) return(hDrop); // Lock block and initialize the data members lpDropFileStruct = (LPDROPFILESTRUCT) GlobalLock(hDrop); lpDropFileStruct->wSize = sizeof(DROPFILESTRUCT); lpDropFileStruct->ptMousePos = *lpptMousePos; lpDropFileStruct->fInNonClientArea = fInNonClientArea; GlobalUnlock(hDrop); return(hDrop); } //*************************************************************** int DragSetPoint(HGLOBAL hDrop,LPPOINT lpptMousePos) { LPDROPFILESTRUCT lpDropFileStruct; lpDropFileStruct = (LPDROPFILESTRUCT) GlobalLock(hDrop); lpDropFileStruct->ptMousePos = *lpptMousePos; GlobalUnlock(hDrop); return 0; } //*************************************************************** HDROP DragAppendFile (HGLOBAL hDrop, LPCSTR szPathname) { LPDROPFILESTRUCT lpDropFileStruct; LPCSTR lpCrnt; WORD wSize; lpDropFileStruct = (LPDROPFILESTRUCT) GlobalLock(hDrop); // Point to first pathname in list lpCrnt = (LPSTR) lpDropFileStruct + lpDropFileStruct->wSize; // Search for a pathname were first byte is a zero byte while (*lpCrnt) { // While the 1st char of path is non-zero while (*lpCrnt) lpCrnt++; // Skip to zero byte lpCrnt++; } // Calculate current size of block wSize = (WORD) (lpCrnt - (LPSTR) lpDropFileStruct + 1); GlobalUnlock(hDrop); // Increase block size to accommodate // the new pathname being appended hDrop = GlobalReAlloc(hDrop, wSize + lstrlen(szPathname) + 1, GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE); // Return NULL if insufficient memory if (hDrop == NULL) return(hDrop); lpDropFileStruct = (LPDROPFILESTRUCT) GlobalLock(hDrop); // Append the pathname to the block lstrcpy((LPSTR) lpDropFileStruct + wSize - 1, szPathname); GlobalUnlock(hDrop); return(hDrop); // Return the new handle to the block } /****************************************************************************/ /********************** **************************/ /********************** FILE OPEN UTILITY FUNCTION **************************/ /********************** **************************/ /****************************************************************************/ static char szAllFileNames[800]; static char szDropPathName[200]; /****************** MultiSelectFiles ****************************************/ LPCSTR MultiSelectFiles(HWND hWnd) { OPENFILENAME ofn; // Initialize structure for calling the "Open File" common dialog _fmemset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "All files\0*.*\0"; ofn.Flags = OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; // Set up the buffer to receive the selected file(s) szAllFileNames[0] = 0; ofn.lpstrFile = szAllFileNames; ofn.nMaxFile = sizeof(szAllFileNames); if ( !GetOpenFileName(&ofn) ) return NULL; if ( szAllFileNames[0] == 0 ) return NULL; return szAllFileNames; } /****************** SelectDropFiles *****************************************/ int DropSelectFiles(HWND hWnd) { return DropFiles(hWnd,MultiSelectFiles(hWnd)); } /*********************** GLOBAL VARIABLES ***********************************/ char _szAppName[] = "Drop File Server"; /****************************************************************************/ int DropFiles(HWND hWnd,LPCSTR szAllFileNames) { int state, has_been_down = 0; WORD x,wNumFiles; HCURSOR hCrsrDrpNotAllow, hCrsrDrpSingle, hCrsrDrpMultiple; BOOL fInNonClientArea, fOkToDrop; POINT ptMousePos; HWND hWndSubject; HDROP hDrop, hDropT; HINSTANCE _hInstance = GetWindowWord(hWnd,GWW_HINSTANCE); if ( !szAllFileNames ) return 0; // Make sure that there are some selected // files to be dropped wNumFiles = GetSinglePathName(szAllFileNames, -1, NULL, 0); if (wNumFiles == 0) { MessageBox(hWnd, "No files to drop.",_szAppName, MB_OK); return 0; } // Get the handles to the cursors // that will shown to the user. hCrsrDrpNotAllow = LoadCursor(_hInstance, "DRPFIL_NOTALLOWED"); hCrsrDrpSingle = LoadCursor(_hInstance, "DRPFIL_SINGLE"); hCrsrDrpMultiple = LoadCursor(_hInstance, "DRPFIL_MULTIPLE"); //#define DROP_MESSAGE #ifdef DROP_MESSAGE #include "drop2.inc" #endif // Loop for determining the drop-file client window SetCapture(hWnd); do { MSG Msg; while (PeekMessage(&Msg, NULL, NULL, NULL, PM_REMOVE)) if ( Msg.message != WM_LBUTTONDOWN ) DispatchMessage(&Msg); else has_been_down = 1; // Get cursor position & window under the cursor GetCursorPos(&ptMousePos); hWndSubject = WindowFromPoint(ptMousePos); fOkToDrop = FALSE; while ( IsWindow(hWndSubject) ) { #ifdef DROP_MESSAGE ScreenToClient(hWndSubject, &ptMousePos); DragSetPoint(hDrop,&ptMousePos); if ( SendMessage(hWndSubject,wm_querydropobject,0,MAKELONG(hDrop,0)) ) { #else if ( (GetWindowLong(hWndSubject, GWL_EXSTYLE) & WS_EX_ACCEPTFILES)) { #endif fOkToDrop = TRUE; break; } hWndSubject = GetParent(hWndSubject); } if ( fOkToDrop ) SetCursor((wNumFiles > 1) ? hCrsrDrpMultiple : hCrsrDrpSingle); else SetCursor(hCrsrDrpNotAllow); // Terminate loop when mouse button is released state = GetAsyncKeyState(VK_LBUTTON) & 0x8000; if ( state ) has_been_down = 1; } while ( state || !has_been_down); ReleaseCapture(); // Free the loaded cursors from memory DestroyCursor(hCrsrDrpNotAllow); DestroyCursor(hCrsrDrpSingle); DestroyCursor(hCrsrDrpMultiple); #ifdef DROP_MESSAGE GlobalFree(hDrop); #endif if (!fOkToDrop) return 0; // Is the cursor in the window's non-client area? fInNonClientArea = (HTCLIENT != SendMessage(hWndSubject, WM_NCHITTEST, 0, MAKELPARAM(ptMousePos.x, ptMousePos.y))); // Create drop-file memory block and initialize it ScreenToClient(hWndSubject, &ptMousePos); hDrop = DragCreateFiles(&ptMousePos, fInNonClientArea); if (hDrop == NULL) { MessageBox(hWnd, "Insufficient memory to drop file(s).", _szAppName, MB_OK); return 0; } // Append each full pathname to // the drop-file memory block for (x = 0; x < wNumFiles; x++) { GetSinglePathName(szAllFileNames, x, szDropPathName, sizeof(szDropPathName)); // Append pathname to end of drop-file memory block hDropT = DragAppendFile(hDrop, szDropPathName); if (hDropT == NULL) { MessageBox(hWnd, "Insufficient memory to drop file(s).", _szAppName, MB_OK); GlobalFree(hDrop); hDrop = NULL; return 0; } else { hDrop = hDropT; } } if (hDrop != NULL) { // All pathnames appended successfully, post // the message to the drop-file client window PostMessage(hWndSubject, WM_DROPFILES, hDrop, 0L); // Clear our own state SendMessage(hWnd, UM_UPDATECAPTION, 0, 0); // Dont't free the memory, the Dropfile // client will do it } return wNumFiles; } #include /****************************************************************************/ int DropListBoxFiles(HWND hWnd,UINT id,LPCSTR path) { int pit,i,lkm,n=sizeof(szAllFileNames); int *items; int ret = 0; int eka = 0; char *sep = ";"; int is_path = ( path && path[0] ); lkm = (int)SendDlgItemMessage(hWnd,id,LB_GETSELCOUNT,0,0); if ( lkm == LB_ERR || lkm == 0) return 0; items = malloc(sizeof(int)*lkm); if ( !items ) return 0; lkm = (int)SendDlgItemMessage(hWnd,id,LB_GETSELITEMS,lkm,(LPARAM)(int FAR*)items); if ( lkm == LB_ERR ) goto lopetus; szAllFileNames[0] = 0; if ( is_path ) { _fstrncpy(szAllFileNames, path,n-1); sep[0]=' '; } else eka = 1; n -= strlen(szAllFileNames); for (i=0; i mb-2 ) break; _fstrcat(s,st); if ( i < n-1 ) _fstrcat(s,";"); } DragFinish(hDrop); /* Muistilohkon vapautus! */ return s; } /****************************************************************************/ LPSTR RemovePath(LPSTR s) { int i = lstrlen(s)-1; for ( ;i>0; i-- ) if ( strchr("\\:",s[i]) ) return s+i+1; return s; } /****************************************************************************/ int DropFilesToListBox(HDROP hDrop,HWND hWnd,int id,int clear) { int i,n = DragQueryFile(hDrop,-1,NULL,0); /* Nimien määrä*/ LPSTR sd; if ( clear & 2 ) SendDlgItemMessage(hWnd,id,LB_RESETCONTENT,0,0); for (i=0; i