1 /* editor.c
2  * Copyright (C) 2002-2004 Pascal Eberhard <pascal.ebo@netcourrier.com>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 
21 #include "common.h"
22 #include <string.h>
23 #include <gdk/gdkkeysyms.h>
24 
25 static void     editor_draw_icon(editor_t *e, GdkPixbuf *pixbuf_view,
26                                  char *iconame, gint x, gint y,
27                                  gboolean is_valid, gboolean is_cursor);
28 static void     editor_build_interface(editor_t *e);
29 
30 static gboolean editor_button_press_event(GtkWidget *widget,
31                                           GdkEventButton *event,
32                                           editor_t *e);
33 static gboolean editor_motion_notify_event(GtkWidget *widget,
34                                            GdkEventMotion *event,
35                                            editor_t *e);
36 static gboolean editor_is_dblclick(editor_t *e, int x, int y);
37 static gboolean editor_button_release_event(GtkWidget *widget,
38                                             GdkEventButton *event,
39                                             editor_t *e);
40 static void     editor_arrow_up_clicked(GtkWidget *widget, editor_t *e);
41 static void     editor_arrow_down_clicked(GtkWidget *widget, editor_t *e);
42 static void     editor_arrow_left_clicked(GtkWidget *widget, editor_t *e);
43 static void     editor_arrow_right_clicked(GtkWidget *widget, editor_t *e);
44 
45 // ----------------------------------------------------------------------------
editor_new(struct apwalapp_t * apwal)46 editor_t* editor_new(struct apwalapp_t *apwal)
47 {
48   editor_t *editor;
49 
50   g_assert(apwal != NULL);
51   g_assert(apwal->editor_frame != NULL);
52 
53   editor = (editor_t *)malloc(sizeof(editor_t));
54   g_assert(editor != NULL);
55 
56   editor->apwal = apwal;
57   editor->dblclick_timer = NULL;
58 
59   editor->drag_in_progress = FALSE;
60 
61   editor->xwidth = gdk_screen_width();
62   editor->xheight = gdk_screen_height();
63 
64   editor->x = -2;
65   editor->y = -2;
66   editor->width = 10;
67   if (editor->xheight <= 480)
68     editor->height = 3;
69   else
70     editor->height = 6;
71 
72   editor_build_interface(editor);
73   editor_refresh(editor);
74 
75   return editor;
76 }
77 // ----------------------------------------------------------------------------
editor_draw_icon(editor_t * e,GdkPixbuf * pixbuf_view,char * iconame,gint x,gint y,gboolean is_valid,gboolean is_cursor)78 void editor_draw_icon(editor_t *e, GdkPixbuf *pixbuf_view,
79                       char *iconame, gint x, gint y, gboolean is_valid,
80                       gboolean is_cursor)
81 {
82   GdkPixbuf *pixbuf_icon;
83   gint        width;
84   gint        height;
85   g_assert(e != NULL && pixbuf_view != NULL &&  iconame != NULL);
86   // first check if this icon has to be showed
87   if ((x < e->x) || (x >= (e->x + e->width)))
88     return;
89   if ((y < e->y) || (y >= (e->y + e->height)))
90     return;
91 
92   // icon pixbuf
93   pixbuf_icon = gdk_pixbuf_new_from_apwal(iconame, NULL, NULL);
94 
95   // if the app has something wrong then change is color to N&B.
96   if (!is_valid && !is_cursor)
97     gdk_pixbuf_saturate_and_pixelate(pixbuf_icon, pixbuf_icon,
98                                      0.1/*saturation*/,
99                                      TRUE/*pixelate*/);
100 
101   if ((x == e->drag_start_x) && (y == e->drag_start_y))
102   {
103     if ((e->drag_in_progress == TRUE) &&
104         ( ! ((x == e->drag_current_x) && (y == e->drag_current_y)) ) )
105     {
106       if (!is_cursor)
107         gdk_pixbuf_saturate_and_pixelate(pixbuf_icon, pixbuf_icon,
108                                          0.0/*saturation*/,
109                                          TRUE/*pixelate*/);
110       if ((e->drag_start_x != e->drag_current_x) ||
111           (e->drag_start_y != e->drag_current_y))
112       {
113         if ((!is_cursor) &&
114             (app_list_at_xy(e->apwal->apps, e->drag_current_x,
115                             e->drag_current_y) == NULL))
116           editor_draw_icon(e, pixbuf_view, iconame,  e->drag_current_x,
117                            e->drag_current_y, TRUE/*valid?*/, FALSE/*cursor?*/);
118       }
119     }
120   }
121 
122   // accept icons with size lower that 48x48
123   width = gdk_pixbuf_get_width(pixbuf_icon);
124   height = gdk_pixbuf_get_height(pixbuf_icon);
125   width = width < ICON_WIDTH  ? width : ICON_WIDTH;
126   height= height< ICON_HEIGHT ? height: ICON_HEIGHT;
127   /*
128   TRACE("x:%d y:%d w:%d h:%d, "
129           "icon x:%d y:%d w:%d h:%d coord:(%d,%d)",
130           e->x, e->y, e->width, e->height,
131           x, y, width, height,
132           (x - e->x) * ICON_WIDTH, (y - e->y) * ICON_HEIGHT);
133   */
134   // put the icon in the image which has to be displayed
135   gdk_pixbuf_composite(pixbuf_icon/*src*/, pixbuf_view/*dest*/,
136                        (x - e->x) * ICON_WIDTH/*dest_x*/,
137                        (y - e->y) * ICON_HEIGHT/*dest_y*/,
138                        gdk_pixbuf_get_width(pixbuf_icon)/*dest_width*/,
139                        gdk_pixbuf_get_height(pixbuf_icon)/*dest_height*/,
140                        (x - e->x) * ICON_WIDTH/*offset_x*/,
141                        (y - e->y) * ICON_WIDTH/*offset_y*/,
142                        1/*scale_x*/,
143                        1/*scale_y*/,
144                        GDK_INTERP_NEAREST/*interp_type*/,
145                        255/*overall_alpha*/);
146   g_object_unref(pixbuf_icon);
147 
148   return;
149 }
150 // ----------------------------------------------------------------------------
editor_refresh(editor_t * e)151 void editor_refresh(editor_t *e)
152 {
153   GdkPixbuf *pixbuf_view;
154   app_t     *app;
155   gboolean   is_valid;
156 
157   g_assert(e != NULL);
158   g_assert(e->apwal != NULL);
159   g_assert(e->apwal->apps != NULL);
160 
161   pixbuf_view = gdk_pixbuf_new(GDK_COLORSPACE_RGB/*colorspace*/,
162                                TRUE/*has_alpha*/,
163                                8/*bits_per_sample*/,
164                                e->width * ICON_WIDTH,
165                                e->height * ICON_HEIGHT);
166   gdk_pixbuf_fill(pixbuf_view, 0x00000000);
167 
168   app = app_list_first(e->apwal->apps);
169   while (app)
170   {
171     is_valid = app_is_executable(app);
172     editor_draw_icon(e, pixbuf_view, app->icon, app->x, app->y, is_valid,
173                      FALSE/*cursor?*/);
174     app = app_list_next(e->apwal->apps);
175   } // end while app
176 
177   // draw cursor
178   editor_draw_icon(e, pixbuf_view, "cursor2", 0, 0, TRUE, TRUE/*cursor?*/);
179   // draw selection around the current selected icon
180   if (e->apwal->selected_app != NULL)
181     editor_draw_icon(e, pixbuf_view, "selected", e->apwal->selected_app->x,
182                      e->apwal->selected_app->y, TRUE, TRUE/*cursor?*/);
183     //editor_draw_icon(e, pixbuf_view, "selected", e->drag_current_x,
184     //                 e->drag_current_y, TRUE, TRUE /*cursor?*/);
185 
186   gtk_image_set_from_pixbuf(GTK_IMAGE(e->image), pixbuf_view);
187   g_object_unref(pixbuf_view);
188 
189 }
190 // ----------------------------------------------------------------------------
editor_build_interface(editor_t * e)191 static void editor_build_interface(editor_t *e)
192 {
193 //GtkWidget     *notebook;
194 //GtkWidget       *editor_vbox;
195 //GtkWidget         *editor_frame;
196   GtkWidget           *table;
197   GtkWidget             *eventbox;
198 //GtkWidget               *image;
199   GtkWidget             *button; // left, right, top, button & ?
200   GtkWidget             *image;  // ? img
201   GtkWidget             *eventbox_for_image;  // ? img
202   char *tooltips_str;
203 
204   g_assert(e != NULL);
205   g_assert(e->apwal != NULL);
206   g_assert(e->apwal->editor_frame != NULL);
207 
208   // table for the editor view
209   table = gtk_table_new(3/*rows*/,3/*cols*/, FALSE/*homogeous*/);
210   gtk_container_add(GTK_CONTAINER(e->apwal->editor_frame), table);
211   gtk_widget_show(table);
212 
213   // arrows ^ v < >  and '?' for the tips
214   button = gtk_arrow_button_new(GTK_ARROW_UP);
215   gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 0, 1);
216   g_signal_connect(G_OBJECT(button), "clicked",
217                    G_CALLBACK(editor_arrow_up_clicked), e);
218   gtk_widget_show(button);
219   button = gtk_arrow_button_new(GTK_ARROW_DOWN);
220   gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 2, 3);
221   g_signal_connect(G_OBJECT(button), "clicked",
222                    G_CALLBACK(editor_arrow_down_clicked), e);
223   gtk_widget_show(button);
224   button = gtk_arrow_button_new(GTK_ARROW_LEFT);
225   gtk_table_attach_defaults(GTK_TABLE(table), button, 0, 1, 1, 2);
226   g_signal_connect(G_OBJECT(button), "clicked",
227                    G_CALLBACK(editor_arrow_left_clicked), e);
228   gtk_widget_show(button);
229   button = gtk_arrow_button_new(GTK_ARROW_RIGHT);
230   gtk_table_attach_defaults(GTK_TABLE(table), button, 2, 3, 1, 2);
231   g_signal_connect(G_OBJECT(button), "clicked",
232                    G_CALLBACK(editor_arrow_right_clicked), e);
233   gtk_widget_show(button);
234 
235   eventbox_for_image = gtk_event_box_new();
236   gtk_table_attach_defaults(GTK_TABLE(table), eventbox_for_image, 0, 1, 0, 1);
237   gtk_widget_show(eventbox_for_image);
238   tooltips_str = "Editor behaviours\n"
239     ". Double click on an icon to select a new one\n"
240     ". Drag and drop an icon to move it on the grid\n"
241     ". Press the remove button to delete an application\n"
242     ". Use the arrow buttons to see all the grid\n"
243     ". The red cross is the positon of the cursor";
244   gtk_tooltips_set_tip(e->apwal->tips, eventbox_for_image, tooltips_str, NULL);
245   image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO,
246                                    GTK_ICON_SIZE_MENU);
247 //                                 GTK_ICON_SIZE_SMALL_TOOLBAR);
248   gtk_container_add(GTK_CONTAINER(eventbox_for_image), image);
249   gtk_widget_show(image);
250 
251   // eventbox
252   eventbox = gtk_event_box_new();
253   g_signal_connect(G_OBJECT(eventbox), "button_press_event",
254                    G_CALLBACK(editor_button_press_event), e);
255   g_signal_connect(G_OBJECT(eventbox), "button_release_event",
256                    G_CALLBACK(editor_button_release_event), e);
257   g_signal_connect(G_OBJECT(eventbox), "motion_notify_event",
258                    G_CALLBACK(editor_motion_notify_event), e);
259   gtk_table_attach_defaults(GTK_TABLE(table), eventbox, 1, 2, 1, 2);
260   gtk_widget_show(eventbox);
261 
262   // image
263   e->image = gtk_image_new();
264   gtk_container_add(GTK_CONTAINER(eventbox), e->image);
265   gtk_widget_show(e->image);
266 
267   //gtk_widget_set_extension_events(eventbox, GDK_EXTENSION_EVENTS_ALL);
268   //gtk_widget_add_events(eventbox, GDK_ALL_EVENTS_MASK | GDK_KEY_RELEASE_MASK);
269   //GTK_WIDGET_SET_FLAGS (eventbox, GTK_CAN_FOCUS);
270   //g_signal_connect(G_OBJECT(eventbox), "key_release_event",
271   //                 G_CALLBACK(editor_key_release_event), e);
272 
273   //gtk_notebook_append_page(GTK_NOTEBOOK(e->apwal->notebook),
274   //                         editor_vbox, editor_label);
275 }
276 // ----------------------------------------------------------------------------
277 // ----------------------------------------------------------------------------
editor_button_press_event(GtkWidget * widget,GdkEventButton * event,editor_t * e)278 static gboolean editor_button_press_event(GtkWidget *widget,
279                                           GdkEventButton *event,
280                                           editor_t *e)
281 {
282   gint x, y;
283   app_t *app;
284   g_assert(widget != NULL && e != NULL && event != NULL);
285   g_assert(e->apwal != NULL);
286 
287   // have the keyboard focus on the eventbox to be able to catch key bindings
288   gtk_widget_grab_focus(widget);
289 
290   x = (((int)event->x)/ICON_WIDTH) + e->x;
291   y = (((int)event->y)/ICON_WIDTH) + e->y;
292   TRACE("x:%d, y:%d", x, y);
293   app = app_list_at_xy(e->apwal->apps, x, y);
294   if (app != NULL)
295     e->drag_in_progress = TRUE;
296   else
297     e->drag_in_progress = FALSE;
298 
299   e->drag_start_x = x;
300   e->drag_start_y = y;
301   e->drag_current_x = x;
302   e->drag_current_y = y;
303 
304   if (app != NULL)
305   {
306     e->apwal->selected_app = app;
307     apwalapp_selected_app_modified(e->apwal);
308   }
309   editor_refresh(e);
310   return TRUE;
311 }
312 // ----------------------------------------------------------------------------
editor_motion_notify_event(GtkWidget * widget,GdkEventMotion * event,editor_t * e)313 static gboolean editor_motion_notify_event(GtkWidget *widget,
314                                            GdkEventMotion *event,
315                                           editor_t *e)
316 {
317   gint x, y;
318   g_assert(widget != NULL && e != NULL && event != NULL);
319 
320   x = (((int)event->x)/ICON_WIDTH) + e->x;
321   y = (((int)event->y)/ICON_WIDTH) + e->y;
322   if ( ! ((x == e->drag_current_x) && (y == e->drag_current_y)) ) //||
323    //       ((x == e->drag_start_x) && (y == e->drag_start_y)) ) )
324   {
325     e->drag_current_x = (((int)event->x)/ICON_WIDTH) + e->x;
326     e->drag_current_y = (((int)event->y)/ICON_WIDTH) + e->y;
327     TRACE("x:%d, y:%d", e->drag_current_x, e->drag_current_y);
328     editor_refresh(e);
329   }
330   return TRUE;
331 }
332 // ----------------------------------------------------------------------------
333 // -----------------------------------------------------------------------------
editor_is_dblclick(editor_t * e,int x,int y)334 static gboolean editor_is_dblclick(editor_t *e, int x, int y)
335 {
336   gint elapse;
337   g_assert(e != NULL);
338   if (e->dblclick_timer == NULL)
339   {
340     e->dblclick_timer = g_timer_new();
341     e->dblclick_last_x = x;
342     e->dblclick_last_y = y;
343     return FALSE;
344   }
345   elapse = (int)(g_timer_elapsed(e->dblclick_timer, NULL) * 1000);
346   g_timer_start(e->dblclick_timer);
347   if ((e->dblclick_last_x != x) || (e->dblclick_last_y != y))
348   {
349     e->dblclick_last_x = x;
350     e->dblclick_last_y = y;
351     return FALSE;
352   }
353   if (elapse > DBLCLICK)
354     return FALSE;
355 
356   return TRUE;
357 }
358 // -----------------------------------------------------------------------------
editor_button_release_event(GtkWidget * widget,GdkEventButton * event,editor_t * e)359 static gboolean editor_button_release_event(GtkWidget *widget,
360                                             GdkEventButton *event,
361                                             editor_t *e)
362 {
363   int pos_x;
364   int pos_y;
365   app_t      *app, *newapp;
366   gboolean drag_in_progress;
367 
368   g_assert(e != NULL);
369   g_assert(e->apwal != NULL);
370   g_assert(e->apwal->apps != NULL);
371 
372   TRACE("type:%x, x:%f, y:%f, button:%d",
373           event->type, event->x, event->y, event->button);
374 
375   pos_x = (((int)event->x)/ICON_WIDTH) + e->x;
376   pos_y = (((int)event->y)/ICON_WIDTH) + e->y;
377   drag_in_progress = e->drag_in_progress;
378   e->drag_in_progress = FALSE;
379   // on double click on a app, go to icon selection tab to select the icon
380   // if no app on the position of the double click, do nothing
381   if (editor_is_dblclick(e, pos_x, pos_y) == TRUE)
382   {
383     if (e->apwal->selected_app != NULL)
384       apwalapp_goto_iconsel(e->apwal);
385     return TRUE;
386   }
387 
388   // if drag in progress and the cursor had moved then do the drop
389   // on the new location of the cursor
390   if ((drag_in_progress == TRUE) &&
391       ( ! ((pos_x == e->drag_start_x) && (pos_y == e->drag_start_y)) ) )
392   {
393     // if there are no app on the drop posistion, move the app
394     // dragged to the new position, else nothing to do
395     app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
396     if (app == NULL)
397     {
398       newapp = app_list_at_xy(e->apwal->apps, e->drag_start_x, e->drag_start_y);
399       if (newapp == NULL)
400         ERR("app_list_at_xy, e->drag_start_x:%d, e->drag_start_y:%d",
401             e->drag_start_x, e->drag_start_y);
402       newapp->x = pos_x;
403       newapp->y = pos_y;
404       e->drag_start_x = pos_x;  // so we see the app 'selected' in the editor
405       e->drag_start_y = pos_y;
406       e->drag_current_x = pos_x;  // so we see the app 'selected' in the editor
407       e->drag_current_y = pos_y;
408     }
409     editor_refresh(e);
410     return TRUE;
411   }
412   // if the cursor has moved like and drag and drop but no drag in progress,
413   // then do nothing (case of a drag from an empty position)
414   if ((drag_in_progress == FALSE) &&
415       ( ! ((pos_x == e->drag_start_x) && (pos_y == e->drag_start_y)) ) )
416     return TRUE;
417 
418 
419   // case of the simple clic
420   editor_goto(e, pos_x, pos_y);
421   return TRUE;
422 }
423 
424 // -----------------------------------------------------------------------------
425 // selected app and show it in property.
editor_goto(editor_t * e,int pos_x,int pos_y)426 void editor_goto(editor_t *e, int pos_x, int pos_y)
427 {
428   int    cc;
429   app_t *app, *newapp;
430   // if no app on the current position then move the noname app if not
431   // created. if no noname app found create one.
432   // if an app is found check if a noname app exist and remove it.
433   // then select the found app and refresh the properties.
434   app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
435   if (app == NULL)
436   {
437     newapp = app_list_find_noname(e->apwal->apps);
438     if (newapp != NULL)
439     {
440       newapp->x = pos_x;
441       newapp->y = pos_y;
442     }
443     else
444     {
445       newapp = app_new_noname(pos_x, pos_y);
446       app_list_add(e->apwal->apps, newapp);
447     }
448     app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
449     if (app != newapp)
450       ERR("app:%p != newapp:%p", app, newapp);
451   }
452   else
453   {
454     newapp = app_list_find_noname(e->apwal->apps);
455     if (newapp != NULL && newapp != app)
456     {
457       cc = app_list_remove(e->apwal->apps, newapp);
458       if (cc)
459         ERR("%s", "impossible to remove the noname app found just before.");
460     }
461   }
462   e->apwal->selected_app = app;
463   editor_refresh(e);
464   apwalapp_selected_app_modified(e->apwal);
465 }
466 // ----------------------------------------------------------------------------
editor_arrow_up_clicked(GtkWidget * widget,editor_t * e)467 static void editor_arrow_up_clicked(GtkWidget *widget, editor_t *e)
468 {
469   g_assert(e != NULL);
470   //TRACE("%s", "");
471   e->y--;
472   editor_refresh(e);
473 }
474 // ----------------------------------------------------------------------------
editor_arrow_down_clicked(GtkWidget * widget,editor_t * e)475 static void editor_arrow_down_clicked(GtkWidget *widget, editor_t *e)
476 {
477   g_assert(e != NULL);
478   //TRACE("%s", "");
479   e->y++;
480   editor_refresh(e);
481 }
482 // ----------------------------------------------------------------------------
editor_arrow_left_clicked(GtkWidget * widget,editor_t * e)483 static void editor_arrow_left_clicked(GtkWidget *widget, editor_t *e)
484 {
485   g_assert(e != NULL);
486   //TRACE("%s", "");
487   e->x--;
488   editor_refresh(e);
489 }
490 // ----------------------------------------------------------------------------
editor_arrow_right_clicked(GtkWidget * widget,editor_t * e)491 static void editor_arrow_right_clicked(GtkWidget *widget, editor_t *e)
492 {
493   g_assert(e != NULL);
494   //TRACE("%s", "");
495   e->x++;
496   editor_refresh(e);
497 }
498 // ----------------------------------------------------------------------------
499