File: fixed_editor.ml

package info (click to toggle)
lablgtk3 3.1.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,796 kB
  • sloc: ml: 40,890; ansic: 22,312; makefile: 133; sh: 17
file content (290 lines) | stat: -rw-r--r-- 9,401 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
(**************************************************************************)
(*    Lablgtk - Examples                                                  *)
(*                                                                        *)
(*    This code is in the public domain.                                  *)
(*    You may freely copy parts of it in your application.                *)
(*                                                                        *)
(**************************************************************************)

open Gdk  
open Gtk
open GObj

let dnd_source_window () =
  let window = GWindow.window ~position:`MOUSE () in
  let vbx = GPack.vbox ~border_width:10 ~packing:window#add ()
  in   
  let evb = GBin.event_box ~border_width:0 ~packing:vbx#add () in
  let frm = GBin.frame ~shadow_type:`OUT ~packing:evb#add () in
  let lbl = GMisc.label ~text:"hello" ~packing:frm#add () in
  let lbl2 = GMisc.label ~text:"drag from here!" ~packing:vbx#add () in
  let targets = [ { target = "STRING"; flags = []; info = 0} ] in
  begin
    window#show ();
    evb#drag#source_set targets ~modi:[`BUTTON1] ~actions:[`COPY];
    evb#drag#connect#data_get ~callback: begin
      fun _ sel ~info ~time ->
      	sel#return "hello! "
    end
  end

let corner_width  = 7  
let corner_height = 7

type drag_action_type =
    GB_DRAG_NONE
  | GB_MIDDLE
  | GB_TOP
  | GB_BOTTOM
  | GB_LEFT
  | GB_RIGHT
  | GB_TOP_LEFT
  | GB_TOP_RIGHT
  | GB_BOTTOM_LEFT
  | GB_BOTTOM_RIGHT

let get_position_in_widget w ~x ~y ~width ~height =
  if (x <= corner_width) then
    if (y <= corner_height) then
      GB_TOP_LEFT
    else if (y >= height-corner_width) then
      GB_BOTTOM_LEFT
    else GB_LEFT
  else if (x >= width-corner_width) then
    if (y <= corner_height) then
      GB_TOP_RIGHT
    else if (y >= height-corner_width) then
      GB_BOTTOM_RIGHT
    else GB_RIGHT
  else if (y <= corner_height) then
      GB_TOP
    else if (y >= height-corner_width) then
      GB_BOTTOM
    else GB_MIDDLE
    
class drag_info = object
  val mutable drag_action = GB_DRAG_NONE
  val mutable drag_offset = (0, 0)
  val mutable toimen      = (0, 0)
  val mutable drag_widget = None
  method drag_action = drag_action
  method drag_offset = drag_offset
  method toimen = toimen (* coord. of opposite corner *)
  method set_drag_widget (w : GObj.widget) = begin
    match drag_widget with
      None -> begin
    	GMain.Grab.add w;
    	drag_widget <- Some w;
	()
      end
    | Some w -> ()
  end
  method unset_drag_widget () = begin
    match drag_widget with
      Some w -> begin
    	GMain.Grab.remove w;
	drag_widget <- None;
	()
      end
    | None -> ()
  end
  method set_drag_offset ~x ~y = drag_offset <- (x, y)
  method set_drag_action (w : Gdk.window) ~x ~y =
    begin
      let (x0, y0) = Window.get_position w in
      let (width, height) = Drawable.get_size w in
      drag_action <- get_position_in_widget w ~x ~y ~width ~height;
      let (x1, y1) = (x0+width, y0+height) in
      toimen <-
	match drag_action with
	  GB_TOP_LEFT     -> (x1, y1)
      	| GB_BOTTOM_LEFT  -> (x1, y0)
      	| GB_TOP_RIGHT    -> (x0, y1)
      	| GB_BOTTOM_RIGHT -> (x0, y0)
	| GB_TOP          -> (x0, y1)
	| GB_BOTTOM       -> (x0, y0)
	| GB_LEFT	  -> (x1, y0)
	| GB_RIGHT	  -> (x0, y0)
	|  _              -> (-1, -1) 
    end
  method unset_drag_action () = drag_action <- GB_DRAG_NONE
end

    
let to_grid g x = x - (x mod g)
  
let to_grid2 g (x, y) = (to_grid g x, to_grid g y)

class fix_editor ~width ~height ~packing =
  let info = new drag_info in
  let fix = GPack.fixed ~has_window:true ~width ~height ~packing () in
  let _ = fix#misc#realize () in
  let fix_window = fix#misc#window in
  let fix_drawing = new GDraw.drawable fix_window in

  object (self)
    inherit GObj.widget fix#as_widget
    val mutable grid = 1
    method set_grid g =
      if (grid != g) then begin
      	let pix =
          GDraw.pixmap ~window:fix ~width:g ~height:g ~mask:true () in
	let c = fix#misc#style#bg `NORMAL in
	pix#set_foreground (`COLOR c);
	pix#rectangle ~filled:true ~x:0 ~y:0 ~width:g ~height:g ();
	pix#set_foreground `BLACK;
      	pix#point ~x:0 ~y:0;
      	Gdk.Window.set_back_pixmap fix_window (`PIXMAP pix#pixmap)
      end;
      grid <- g

    method new_child ~name ~x ~y ~width ~height ~callback =
      let evb = GBin.event_box ~border_width:0 ~packing:fix#add () in
      let lbl = GMisc.label ~text:name ~width ~height ~packing:evb#add () in
      evb#misc#realize ();
      fix#move evb#coerce ~x ~y;
      self#connect_signals ~ebox:evb ~widget:lbl#coerce ~callback;
      ()

    method private connect_signals
      ~ebox:(ebox : GBin.event_box) ~widget:(widget : widget) ~callback:cbfun =
      let drawing = new GDraw.drawable (ebox#misc#window) in
      let draw_id = ref None in
      let exps_id = ref None in
      let on_paint _ =
      	let (width, height) = Drawable.get_size (ebox#misc#window) in begin
      	  drawing#set_foreground `BLACK;
      	  drawing#rectangle ~filled:true ~x:0 ~y:0
	    ~width:corner_width ~height:corner_height ();
      	  drawing#rectangle ~filled:true ~x:(width-corner_width) ~y:0
	    ~width:corner_width ~height:corner_height ();
      	  drawing#rectangle ~filled:true
	    ~x:(width-corner_width)
	    ~y:(height-corner_height)
	    ~width:corner_width ~height:corner_height ();
      	  drawing#rectangle ~filled:true
	    ~x:0
	    ~y:(height-corner_height)
	    ~width:corner_width ~height:corner_height ();
      	  drawing#rectangle ~filled:false
	    ~x:0 ~y:0 ~width:(width-1) ~height:(height-1) ();
	end
      in
      ebox#event#connect#button_press ~callback:
      	begin fun ev -> 
	  let bx = int_of_float (GdkEvent.Button.x ev) in
	  let by = int_of_float (GdkEvent.Button.y ev) in
	  info#set_drag_action (ebox#misc#window) ~x:bx ~y:by;
	  info#set_drag_offset ~x:bx ~y:by;
	  true
      	end;
      ebox#event#connect#motion_notify ~callback:
      	begin fun ev ->
	  info#set_drag_widget ebox#coerce;
	  let action = info#drag_action in
	  let (mx, my) = fix#misc#pointer in
	  let (ox, oy) = info#drag_offset in
	  begin match action with
	    GB_MIDDLE ->
	      let (nx, ny) = to_grid2 grid (mx-ox, my-oy) in
	      fix#move ebox#coerce ~x:nx ~y:ny;
	      if cbfun ~x:nx ~y:ny ~width:(-2) ~height:(-2) then
	      	()
	      else (* should we undo ? *) ()
	  | GB_DRAG_NONE -> () (* do nothing *)
	  | GB_TOP_LEFT | GB_BOTTOM_LEFT
	  | GB_TOP_RIGHT | GB_BOTTOM_RIGHT ->
	      let (toi_x, toi_y) =  info#toimen in
	      let (mx, my) = to_grid2 grid (mx, my) in
	      let (lx, rx) =
	      	if mx<toi_x then (mx, toi_x) else (toi_x, mx) in
	      let (ty, by) =
	      	if my<toi_y then (my, toi_y) else (toi_y, my) in
	      let (w, h) = (rx-lx, by-ty) in
	      ebox#misc#set_size_request ~width:w ~height:h ();
              fix#move ebox#coerce ~x:lx ~y:ty;
	      if cbfun ~x:lx ~y:ty ~width:w ~height:h then
	      	()
	      else (* should we undo ? *) ()
	  | GB_TOP | GB_BOTTOM ->
	      let (lx, toi_y) = info#toimen in
	      let my = to_grid grid my in
	      let (ty, by) = if my<toi_y then (my, toi_y) else (toi_y, my) in
	      let h = by-ty in
	      fix#move ebox#coerce ~x:lx ~y:ty;
              ebox#misc#set_size_request ~height:h ();
	      if cbfun ~x:lx ~y:ty ~width:(-2) ~height:h then
	      	()
	      else (* should we undo ? *) ()
	  | GB_LEFT | GB_RIGHT ->
	      let (toi_x, ty) = info#toimen in
	      let mx = to_grid grid mx in
	      let (lx, rx) = if mx<toi_x then (mx, toi_x) else (toi_x, mx) in
	      let w = rx-lx in 
              fix#move ebox#coerce ~x:lx ~y:ty;
	      ebox#misc#set_size_request ~width:w ();
	      if cbfun ~x:lx ~y:ty ~width:w ~height:(-2) then
	      	()
	      else (* should we undo ? *) ()
	  end;
	  true
      	end;
      ebox#event#connect#button_release ~callback:
      	begin fun ev -> 
	  info#unset_drag_action ();
	  info#unset_drag_widget ();
	  true
      	end;
      exps_id := Some (ebox#event#connect#after#expose
                         ~callback:(fun _ -> on_paint(); false));
      (* draw_id := Some (ebox#misc#connect#draw ~callback:on_paint); *)
      ()
    initializer
      fix#drag#dest_set ~actions:[`COPY]
      	[ { target = "STRING"; flags = []; info = 0} ];
      fix#drag#connect#data_received ~callback: begin
	fun context ~x ~y data ~info ~time ->
	  let name = data#data in
	  let _ = self#new_child ~name ~x ~y ~width:32 ~height:32
	      ~callback:(fun ~x ~y ~width ~height -> true) in
(*		  Printf.printf "%s %d %d\n" (data#data) x y;
		  flush stdout; *)
	  context#finish ~success:true ~del:false ~time;
      end;
      ()
  end
    
(* the following is for test only *)
let window1 () =    
  let window = GWindow.window () in
  let _ = window#connect#destroy ~callback: GMain.quit in
  let fix = new fix_editor ~width:640 ~height:480 ~packing:window#add in
  fix#set_grid 5;
  let setter = fix#new_child ~name:"hello" ~x:100 ~y:200 ~width:32 ~height:32
      ~callback:begin fun ~x ~y ~width ~height ->
	(* Printf.printf "name=%s, x=%d, y=%d, width=%d, height=%d\n"
	              "hello" x y width height;
	flush stdout; *)
	true
      end in
   window#show ();
  ()


    
let main () =
  let _ = GMain.init () in
  window1 ();
  dnd_source_window ();
  GMain.main ()
  
let _ = main ()

(* Todo
   
   change mouse cursor
   resize fixed itself
   remove_child
   (drag and) drop
   
*)