Zablokowanie menu podręcznego lub wyłączenie fukncji Paste.

0

Witam serdecznie!
Chciałbym poprosić o radę. Mam do napisania program na zaliczenie i w sumie już skończyłem, ale chciałbym go zabezpieczyć dokładnie przed "złośliwym użytkownikiem". Cały program zawiera tylko kilka komponentów: Label, Edit, Button. Piszę pod Debianem w Lazarus'ie. W Edit'ach znajdują się jedynie liczby. Generalizując mam połowę Edit'ów ReadOnly(Properties -> ReadOnly(True)), a połowę nie. No i te Edit'y, które nie są ReadOnly(do wpisywania cyfr z zakresu np. [-100; 100]) są zabezpieczone przed przekroczeniem zakresu przez właściwość MaxLenght oraz przez Event'y: Exit(Wyświetlający się komunikat, gdy się jest poza zakresem) oraz KeyPress(Zakres klawiszy od 0 do 9 wraz z minusem, tab oraz backspace).
Chciałbym by w tych Edit'ach, które nie są zabezpieczone ReadOnly nie działał prawy klawisz myszy lub nie byłaby aktywna opcja Paste z menu podręcznego prawego klawisza myszy. Zauważyłem, że opcja Paste w menu podręcznym jest NIE aktywna w polach ReadOnly, więc pewnie istnieje jakaś głęboka właściwość/funkcja/procedura, która powoduje dezaktywacje wklejania tekstu i właśnie na takim rozwiązaniu mi najbardziej zależy.
Powód: Niby program jest zabezpieczony przed wpisywaniem złych danych(alfabetu oraz cyfr poza zakresem), ale jak wkleję jakiś skopiowany tekst za pomocą myszy to po wykryciu przez program Event'u Exit wyskakuje wyjątek środowiska/programu(Project Zadanie9 raised exception class 'EConvertError' with message:[...]).

Dla jasności wkleje Event'y o których wspomniałem
Exit:

procedure TForm1.EditXExit(Sender: TObject);
{ Wcześniej przekonwetowałem obiekty TEdit EditX,Y,A,B,R na TMaskEdit
  i zastosowałem taką maske w polu właściwości EditMask: #999;1;
  Wyglądało to źle, więc zdecydowałem się na usunięcie maski
  i zastosowanie tylko tej procedury.}
var
      Liczba : Integer;
      BezSpacji : string;
begin
  BezSpacji := Trim (EditX.Text);
  if BezSpacji <> '' then
    begin
        Liczba := StrToInt (BezSpacji);
        if (-100 > Liczba) or (Liczba > 100) then
           begin
               EditX.SetFocus;
               ShowMessage('Proszę wprowadzić liczbę X z zakresu od -100 do 100.');
           end;
    end
end; 

KeyPress:

procedure TForm1.EditXKeyPress(Sender: TObject; var Key: char);
begin
  if (key = #13) then
  begin
      Form1.Oblicz.Click;
  end
  else if not (key in ['-', '0'..'9',#8{backspace},#9{tab}]) then
  begin
       Key := #0;
  end;
end;

Mam nadzieję, że uzyskam jakieś pomocne odpowiedzi od Was. (Tak... Szukałem w Google)

3

Na wstępie powiem Tobie, że wcale nie szukałeś. Należy szukać i kombinowac do bółu. Nieważne początkujący czy nie. Pierwszy wynik w Google po wpisaniu delphi disable paste naprowadził by Ciebie na rozwiązanie.

Co do reszty, to najlepiej sobie w OnChange sprawdzać poprawność wpisów dla Editów. Są przecież funkcje TryStrToInt i tym podobne. Można dorobić własne z nich korzystające i sterować własnością Enabled buttonu do obliczeń w zależności od poprawności danych.

Pod Windows i Delphi najprościej osiągnąc to co chcesz można tak:

//...
var
  POldEditsProc : Pointer;

function EditsProc(AHwnd : HWND; Msg : UINT; WParam : WPARAM; LParam : LPARAM) : integer; stdcall;
begin
  case Msg of
    WM_CONTEXTMENU, WM_PASTE :
      begin
        Result := 0;
        Exit;
      end;
  end;
  Result := CallWindowProc(POldEditsProc, AHwnd, Msg, wParam, lParam);
end;

procedure TForm1.FormCreate(Sender : TObject);
begin
  POldEditsProc := Pointer(SetWindowLong(Edit1.Handle, GWL_WNDPROC, Integer(@EditsProc)));
end;
//...

Jednak pod Lazarusem na pewno jest WndProc do ustawienia. Ale cholera wie jak kontrolki zachowują się pod Debianem, czy to też działa na zasadzie komunikatów. Tutaj ktoś lepiej ogarnięty z Linuxami/Unixami musiał by się wypowiedzieć.

Jeżeli się nie da. To trzeba po prostu zablokować w OnKeyDown kombinacje do wklejania. Dodatkowo stworzyć puste PopupMenu i przypisać je do Edita. Osiągniesz to samo, ale trochę na około. Bo operować na większości systemowych menu - o ile wiem - się nie da.

1

Jeśli wszystkie zdarzenia są takie same to możesz podpiąć jedno pod wiele komponentów nie musisz kopiować kodu. Wystarczy rzutować (Sender as Edit) i dalej traktujesz to jako Edit na którym zostało wywołane zdarzenie.
Ja problemu o którym mówisz nie potrafię odtworzyć u siebie może to przez system a może nie, ale dostajesz ten wyjątek prawdopodobnie dlatego, że dochodzisz do linijki Liczba := StrToInt (BezSpacji);. Postaw breakpoint na początku tej metody i sprawdź co tam będzie siedziało.
Poza tym po co w ogóle masz podpięte to zdarzenie pod Edita tylko do odczytu (martwy kod)? W najgorszym przypadku na początku można sprawdzić czy aktualny Edit jest ReadOnly i jeśli tak opuścić metodę bez sprawdzania czegokolwiek.

//Dopisano
Standardowo jest też dostępny komponent SpinEdit nadaje się idealnie do wprowadzania danych liczbowych. Min/MaxValue itd.

2

Chciałbym by w tych Edit'ach, które nie są zabezpieczone ReadOnly nie działał prawy klawisz myszy lub nie byłaby aktywna opcja Paste z menu podręcznego prawego klawisza myszy.

Jeżeli chcesz, aby menu kontekstowe w ogóle nie było dostępne, to połóż komponent klasy TPopupMenu na formularz, nie uzupełniaj go i przypisz do tych editów, które mają nieobsługiwać menu (właściwość PopupMenu);

Jeśli natomiast chcesz zablokować możliwość wklejania tekstu ze schowka, to albo korzystając z sugestii @olesio wepnij się w kolejkę komunikatów i udaremnij próbę przetworzenia tych wspomnianych, albo stwórz swoje własne menu kontekstowe, bez opcji Wklej lub z zablokowaną taką opcją;

Poza tym wyjątek dostajesz jedynie w trybie debug; I nie "cyfry z zakresu" tylko "liczby z zakresu" - to duża różnica.

0

to może z innej beczki - jeżeli nie stanowi to dla Ciebie problemu, to po prostu obsłuż komunikaty o błędach, zamiast blokować kontrolki?!

0
olesio napisał(a):

Co do reszty, to najlepiej sobie w OnChange sprawdzać poprawność wpisów dla Editów. Są przecież funkcje TryStrToInt i tym podobne.

Że też ja o OnChange wcześniej nie pomyślałem! Myślę, że to będzie to. Dziękuje serdecznie!

szopenfx napisał(a):

Jeśli wszystkie zdarzenia są takie same to możesz podpiąć jedno pod wiele komponentów nie musisz kopiować kodu. Wystarczy rzutować (Sender as Edit) i dalej traktujesz to jako Edit na którym zostało wywołane zdarzenie.
Ja problemu o którym mówisz nie potrafię odtworzyć u siebie może to przez system a może nie, ale dostajesz ten wyjątek prawdopodobnie dlatego, że dochodzisz do linijki Liczba := StrToInt (BezSpacji);. Postaw breakpoint na początku tej metody i sprawdź co tam będzie siedziało.

Dzięki za radę! No na szczęście tych komponentów było kilka, więc nie było większego problemu z kopiowaniem. Jednak już nie będę tego zmieniał, bo, jak wspomniałem, już skończone. :)
Też mi się wydaje, że to może być w tym miejscu problem. Chyba całkowite zabezpieczenie Edit'ów przed znakami pozwoli wyeliminować ten problem. Spróbuje debugowania.

szopenfx napisał(a):

Poza tym po co w ogóle masz podpięte to zdarzenie pod Edita tylko do odczytu (martwy kod)? W najgorszym przypadku na początku można sprawdzić czy aktualny Edit jest ReadOnly i jeśli tak opuścić metodę bez sprawdzania czegokolwiek.

To nie jest martwy kod. Jak wspomniałem w pierwszym poście połowę Edit'ów mam ReadOnly, a połowę nie. Tam gdzie NIE ma ReadOnly są zabezpieczenia przed wpisywaniem. ;)

szopenfx napisał(a):

Standardowo jest też dostępny komponent SpinEdit nadaje się idealnie do wprowadzania danych liczbowych. Min/MaxValue itd.

Dziękuję za SpinEdit! Będę następnym razem pamiętać, żeby się rozejrzeć za komponentami zamiennymi. Gdy OnChange zawiedzie to spróbuje Twoje rozwiązanie. Szkoda kodu usuwać. :)

furious programming napisał(a):

Jeżeli chcesz, aby menu kontekstowe w ogóle nie było dostępne, to połóż komponent klasy TPopupMenu na formularz, nie uzupełniaj go i przypisz do tych editów, które mają nieobsługiwać menu (właściwość PopupMenu);

Na początku nie zrozumiałem o co chodzi, ale już chyba widzę. Położyć na Edit czysty(prosto z menu komponentów) PopupMenu? Tak menu też się rozwija. Jednak nie wiem co znaczy "przypisz do tych editów".
Wybacz mi szczerość, ale od strony programisty/środowiska to brzydko wygląda. Przepraszam za mój pedantyzm. :D

furious programming napisał(a):

Jeśli natomiast chcesz zablokować możliwość wklejania tekstu ze schowka, to albo korzystając z sugestii @olesio wepnij się w kolejkę komunikatów i udaremnij próbę przetworzenia tych wspomnianych, albo stwórz swoje własne menu kontekstowe, bez opcji Wklej lub z zablokowaną taką opcją;

Wydaje mi się, że za własne menu dostałbym dwa zaliczenia... Gdyby się dało. ;P

MiM napisał(a):

to może z innej beczki - jeżeli nie stanowi to dla Ciebie problemu, to po prostu obsłuż komunikaty o błędach, zamiast blokować kontrolki?!

Na razie stanowi i chciałbym to zrobić w sposób łatwiejszy. :)

Wszystkim dziękuję za poświęcony czas!

Tak poza tym to czemu Wy nie śpicie? :D

1

Na początku nie zrozumiałem o co chodzi, ale już chyba widzę. Położyć na Edit czysty(prosto z menu komponentów) PopupMenu?

Niekoniecznie na Edit - po prostu na formularz; Zwróć uwagę, że PopupMenu to nie jest komponent wizualny - podczas projektowania formularza widać go jako malutki przycisk, ale tylko wtedy; Jeśli program skompilujesz i uruchomisz, to już go widać nie będzie;

Tak menu też się rozwija. Jednak nie wiem co znaczy "przypisz do tych editów".

No właśnie - rozwija się, bo to menu systemowe; Jeżeli przypiszesz położony na formularzu komponent do pola edycyjnego, to wyświetlane nie będzie już to systemowe menu, a Twoje; Ale że jest puste, to okno menu w ogóle się nie pokaże; Wytłumaczyłem Ci wcześniej jak się "przypisuje" menu do komponentu:

  • kładziesz na formularz pusty komponent TPopupMenu,
  • zaznaczasz komponent, któremu chcesz ustawić nowe menu kontekstowe (w Twoim przypadku pole lub pola edycyjne),
  • przechodzisz do okienka Inspektora Obiektów i szukasz właściwości PopupMenu,
  • klikasz na nie i wybierasz z listy (ComboBox) nowe menu spośród wszystkich dostępnych na liście;
    Poniżej obrazek, jakbyś nadal nie pokapował:

popup-selection.png

Tak poza tym to czemu Wy nie śpicie? :D

Śpimy, tylko o najprzeróżniejszych godzinach i tylko w dni parzyste :]

0

To działa! Serdecznie dziękuję! Jeszcze dla zadośćuczynienia pokombinuje trochę z OnChange. Dzięki! :)

furious programming napisał(a):

Tak poza tym to czemu Wy nie śpicie? :D

Śpimy, tylko o najprzeróżniejszych godzinach i tylko w dni parzyste :]

Hah! :D

szopenfx napisał(a):

Standardowo jest też dostępny komponent SpinEdit nadaje się idealnie do wprowadzania danych liczbowych. Min/MaxValue itd.

Twoje rozwiązanie też sprawdziłem i niestety jest jeden problem. Gdy się zastosuje zakres od -100 do 100 to nie można wpisywać minusa z klawiatury, tylko przez guzik dekrementacji. :(

Zlokalizowałem kolejny problem
Nawet jak się zablokuje wklejanie tekstu to istnieje przecież możliwość przeciągania(DragAndDrop) tekstu z innego okna... -.-'
Zobaczę czy OnChange z tym sobie poradzi.

0

Twoje rozwiązanie też sprawdziłem i niestety jest jeden problem. Gdy się zastosuje zakres od -100 do 100 to nie można wpisywać minusa z klawiatury, tylko przez guzik dekrementacji.

Można normalnie wpisywać znak -, i to nie tylko na początku wartości i nie tylko jeden znak;

Nawet jak się zablokuje wklejanie tekstu to istnieje przecież możliwość przeciągania(DragAndDrop) tekstu z innego okna... -.-'
Zobaczę czy OnChange z tym sobie poradzi.

W zdarzeniu OnChange możesz rozpoznać nawet najmniejszą zmianę w wartości, więc to zdarzenie się przyda; Ale nie jest ono przeznaczone do zabezpieczania programu przed wprowadzeniem złej wartości, tylko do wykrycia i zareagowania na zmiany wartości; Jeżeli sensownie oprogramujesz to zdarzenie to we wszystkich przypadkach będzie się zachowywało poprawnie, bez rzucania wyjątków;

Tylko że nadal nie napisałeś co chcesz w tym zdarzeniu robić - a można wiele.

0
olesio napisał(a):

Co do reszty, to najlepiej sobie w OnChange sprawdzać poprawność wpisów dla Edit'ów. Są przecież funkcje TryStrToInt i tym podobne.

Dzięki @olesio za wskazanie oczywistego! Najciemniej pod latarnią! :D
Problem rozwiązany. Oto owoce mojej pracy:

var //GLOBALNE
  Form1: TForm1;
  x, y, a, b : shortInt;
  r : byte;
  strPamiecX, strPamiecA, strPamiecY, strPamiecB, strPamiecR : string; 
procedure TForm1.EditXChange(Sender: TObject);
var
      Liczba : Integer;
begin
if EditX.Text <> '' then
 if TryStrToInt(EditX.Text, Liczba) then
  begin
      Liczba := StrToInt (EditX.Text);
      if (-100 > Liczba) or (Liczba > 100) then
        begin
          Form1.EditX.Text := strPamiecX;
          EditX.SetFocus;
          ShowMessage('Proszę wprowadzić liczbę X z zakresu od -100 do 100.');
        end
      else
        begin
          strPamiecX := Form1.EditX.Text;
        end;
  end
  else if (EditX.Text[1] = '-') AND (Length(EditX.Text) = 1) then
    begin
      strPamiecX := Form1.EditX.Text;
    end
  else
  begin
    EditX.Text := strPamiecX;
    EditX.SetFocus;
    ShowMessage('Dozwolone są jedynie cyfry oraz jeden znak - !');
  end;
end;
procedure TForm1.EditXKeyPress(Sender: TObject; var Key: char);
begin
  if (key = #13) then
  begin
      Form1.Oblicz.Click;
  end
  else if not (key in ['-', '0'..'9',#8{backspace},#9{tab}]) then
  begin
       Key := #0;
  end;
end;

Przez 2 godziny się zastanawiałem dlaczego część kodu z OnChange nie powoduje wpisania ostatniej dobrej zawartości Edit'a / zawartości przed błędem: Form1.EditX.Text := strPamiecX;. Dopiero po przypomnieniu sobie, że oprogramowuje metodę, która się wykonuje bardzo często odkryłem dlaczego. Miałem zmienną strPamiecX jako lokalną i za każdym razem, gdy zmieniam zawartość jest właśnie ta metoda wywoływana oraz resetowana zawartość tej zmiennej. Po wpisaniu zmiennej jako globalnej jest wszystko jak należy. :)
Musiałem również dodać else if (EditX.Text[1] = '-') AND (Length(EditX.Text) = 1) then, bo funkcja TryStrToInt nie potrafi przyjąć samego minusa... Właśnie chwyciła mnie myśl... Muszę wziąć pod uwagę jeszcze co się stanie z programem, gdy kliknę na Button Oblicz, a w Edit'ach będzie gdzieś sam minus. Wydaje mi się, że to będzie do ogarnięcia za pomocą OnExit.

Okej. Zrobione.

procedure TForm1.EditXExit(Sender: TObject);
begin
  if (EditX.Text[1] = '-') then
   begin
     EditX.SetFocus;
     ShowMessage('Nie da się obliczać minusów!');
   end
  else
end; 

Kolejny problem. Po wprowadzeniu powyższego kodu i wpisaniu w Edit minus i kliknięcie Tab program reaguje tak jak został oprogramowany, czyli wyświetla się komunikat i cofa Focus. Jednak gdy Edit jest pusty i wcisne Tab to wyskakuje error(już nie wyjątek, bo nie ma "Break" i "Continue", tylko "OK" oraz nagłówek okna "Error") o takiej informacji i cały program się wiesza:

Project Zadanie9 raised exception class 'External: SIGSEGV'.

 In file 'unitzadanie9.pas' at line 484:
if (EditX.Text[1] = '-') then

Czym to jest spowodowane?

Problem już rozwiązałem, ale chciałbym wiedzieć czym został spowodowany: Usunąłem indeks tablicy w warunku.

procedure TForm1.EditXExit(Sender: TObject);
begin
  if (EditX.Text = '-') then
   begin
     EditX.SetFocus;
     ShowMessage('Nie da się obliczać minusów!');
   end
  else
end; 

Jakieś sugestie? :)

1

Project Zadanie9 raised exception class 'External: SIGSEGV'.

In file 'unitzadanie9.pas' at line 484:

if (EditX.Text[1] = '-') then

Wyjątek oznajmia, że EditX nie posiada pustą właściwość Text, więc odwoływanie się do pierwszego (nieistniejącego) znaku tekstu nie jest możliwe; To przekroczenie zakresu łańcucha i naruszenie pamięci; Odwzorowałem dokładnie taki sam komunikat u siebie w Lazarusie, więc najpierw sprawdzaj czy ten komponent w ogóle posiada jakiś tekst.

0
furious programming napisał(a):

Project Zadanie9 raised exception class 'External: SIGSEGV'.

In file 'unitzadanie9.pas' at line 484:

if (EditX.Text[1] = '-') then

Wyjątek oznajmia, że EditX nie posiada pustą właściwość Text, więc odwoływanie się do pierwszego (nieistniejącego) znaku tekstu nie jest możliwe; To przekroczenie zakresu łańcucha i naruszenie pamięci; Odwzorowałem dokładnie taki sam komunikat u siebie w Lazarusie, więc najpierw sprawdzaj czy ten komponent w ogóle posiada jakiś tekst.

Ah no racja! Im dłużej się siedzi nad programem za jednym razem tym bardziej banalne się błędy robi.
Dziękuję wszystkim za pomoc! Program skończony. :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1